Skip to content

Commit 93bd428

Browse files
[InlineAsm] refactor InlineAsm class NFC (#65649)
I would like to steal one of these bits to denote whether a kind may be spilled by the register allocator or not, but I'm afraid to touch of any this code using bitwise operands. Make flags a first class type using bitfields, rather than launder data around via `unsigned`.
1 parent e33f3f0 commit 93bd428

23 files changed

+347
-347
lines changed

llvm/include/llvm/IR/InlineAsm.h

Lines changed: 139 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#ifndef LLVM_IR_INLINEASM_H
1616
#define LLVM_IR_INLINEASM_H
1717

18+
#include "llvm/ADT/Bitfields.h"
1819
#include "llvm/ADT/SmallVector.h"
1920
#include "llvm/ADT/StringRef.h"
2021
#include "llvm/IR/Value.h"
@@ -196,23 +197,6 @@ class InlineAsm final : public Value {
196197
return V->getValueID() == Value::InlineAsmVal;
197198
}
198199

199-
// These are helper methods for dealing with flags in the INLINEASM SDNode
200-
// in the backend.
201-
//
202-
// The encoding of the flag word is currently:
203-
// Bits 2-0 - A Kind::* value indicating the kind of the operand.
204-
// Bits 15-3 - The number of SDNode operands associated with this inline
205-
// assembly operand.
206-
// If bit 31 is set:
207-
// Bit 30-16 - The operand number that this operand must match.
208-
// When bits 2-0 are Kind::Mem, the Constraint_* value must be
209-
// obtained from the flags for this operand number.
210-
// Else if bits 2-0 are Kind::Mem:
211-
// Bit 30-16 - A Constraint_* value indicating the original constraint
212-
// code.
213-
// Else:
214-
// Bit 30-16 - The register class ID to use for the operand.
215-
216200
enum : uint32_t {
217201
// Fixed operands on an INLINEASM SDNode.
218202
Op_InputChain = 0,
@@ -241,6 +225,7 @@ class InlineAsm final : public Value {
241225
// Addresses are included here as they need to be treated the same by the
242226
// backend, the only difference is that they are not used to actaully
243227
// access memory by the instruction.
228+
// TODO: convert to enum?
244229
Constraint_Unknown = 0,
245230
Constraint_es,
246231
Constraint_i,
@@ -274,15 +259,12 @@ class InlineAsm final : public Value {
274259
Constraint_ZT,
275260

276261
Constraints_Max = Constraint_ZT,
277-
Constraints_ShiftAmount = 16,
278-
279-
Flag_MatchingOperand = 0x80000000
280262
};
281263

282264
// Inline asm operands map to multiple SDNode / MachineInstr operands.
283265
// The first operand is an immediate describing the asm operand, the low
284266
// bits is the kind:
285-
enum class Kind {
267+
enum class Kind : uint8_t {
286268
RegUse = 1, // Input register, "r".
287269
RegDef = 2, // Output register, "=r".
288270
RegDefEarlyClobber = 3, // Early-clobber output register, "=&r".
@@ -292,101 +274,149 @@ class InlineAsm final : public Value {
292274
Func = 7, // Address operand of function call
293275
};
294276

295-
static unsigned getFlagWord(Kind Kind, unsigned NumOps) {
296-
assert(((NumOps << 3) & ~0xffff) == 0 && "Too many inline asm operands!");
297-
return static_cast<unsigned>(Kind) | (NumOps << 3);
298-
}
299-
300-
static bool isRegDefKind(unsigned Flag) {
301-
return getKind(Flag) == Kind::RegDef;
302-
}
303-
static bool isImmKind(unsigned Flag) { return getKind(Flag) == Kind::Imm; }
304-
static bool isMemKind(unsigned Flag) { return getKind(Flag) == Kind::Mem; }
305-
static bool isFuncKind(unsigned Flag) { return getKind(Flag) == Kind::Func; }
306-
static bool isRegDefEarlyClobberKind(unsigned Flag) {
307-
return getKind(Flag) == Kind::RegDefEarlyClobber;
308-
}
309-
static bool isClobberKind(unsigned Flag) {
310-
return getKind(Flag) == Kind::Clobber;
311-
}
312-
313-
/// getFlagWordForMatchingOp - Augment an existing flag word returned by
314-
/// getFlagWord with information indicating that this input operand is tied
315-
/// to a previous output operand.
316-
static unsigned getFlagWordForMatchingOp(unsigned InputFlag,
317-
unsigned MatchedOperandNo) {
318-
assert(MatchedOperandNo <= 0x7fff && "Too big matched operand");
319-
assert((InputFlag & ~0xffff) == 0 && "High bits already contain data");
320-
return InputFlag | Flag_MatchingOperand | (MatchedOperandNo << 16);
321-
}
322-
323-
/// getFlagWordForRegClass - Augment an existing flag word returned by
324-
/// getFlagWord with the required register class for the following register
325-
/// operands.
326-
/// A tied use operand cannot have a register class, use the register class
327-
/// from the def operand instead.
328-
static unsigned getFlagWordForRegClass(unsigned InputFlag, unsigned RC) {
329-
// Store RC + 1, reserve the value 0 to mean 'no register class'.
330-
++RC;
331-
assert(!isImmKind(InputFlag) && "Immediates cannot have a register class");
332-
assert(!isMemKind(InputFlag) && "Memory operand cannot have a register class");
333-
assert(RC <= 0x7fff && "Too large register class ID");
334-
assert((InputFlag & ~0xffff) == 0 && "High bits already contain data");
335-
return InputFlag | (RC << 16);
336-
}
277+
// These are helper methods for dealing with flags in the INLINEASM SDNode
278+
// in the backend.
279+
//
280+
// The encoding of Flag is currently:
281+
// Bits 2-0 - A Kind::* value indicating the kind of the operand.
282+
// Bits 15-3 - The number of SDNode operands associated with this inline
283+
// assembly operand.
284+
// If bit 31 is set:
285+
// Bit 30-16 - The operand number that this operand must match.
286+
// When bits 2-0 are Kind::Mem, the Constraint_* value must be
287+
// obtained from the flags for this operand number.
288+
// Else if bits 2-0 are Kind::Mem:
289+
// Bit 30-16 - A Constraint_* value indicating the original constraint
290+
// code.
291+
// Else:
292+
// Bit 30-16 - The register class ID to use for the operand.
293+
//
294+
// Bits 30-16 are called "Data" for lack of a better name. The getter is
295+
// intentionally private; the public methods that rely on that private method
296+
// should be used to check invariants first before accessing Data.
297+
class Flag {
298+
uint32_t Storage;
299+
using KindField = Bitfield::Element<Kind, 0, 3, Kind::Func>;
300+
using NumOperands = Bitfield::Element<unsigned, 3, 13>;
301+
using Data = Bitfield::Element<unsigned, 16, 15>;
302+
using IsMatched = Bitfield::Element<bool, 31, 1>;
303+
304+
unsigned getData() const { return Bitfield::get<Data>(Storage); }
305+
bool isMatched() const { return Bitfield::get<IsMatched>(Storage); }
306+
void setKind(Kind K) { Bitfield::set<KindField>(Storage, K); }
307+
void setNumOperands(unsigned N) { Bitfield::set<NumOperands>(Storage, N); }
308+
void setData(unsigned D) { Bitfield::set<Data>(Storage, D); }
309+
void setIsMatched(bool B) { Bitfield::set<IsMatched>(Storage, B); }
310+
311+
public:
312+
Flag() : Storage(0) {}
313+
explicit Flag(uint32_t F) : Storage(F) {}
314+
Flag(enum Kind K, unsigned NumOps) {
315+
setKind(K);
316+
setNumOperands(NumOps);
317+
setData(0);
318+
setIsMatched(false);
319+
}
320+
operator uint32_t() { return Storage; }
321+
Kind getKind() const { return Bitfield::get<KindField>(Storage); }
322+
bool isRegUseKind() const { return getKind() == Kind::RegUse; }
323+
bool isRegDefKind() const { return getKind() == Kind::RegDef; }
324+
bool isRegDefEarlyClobberKind() const {
325+
return getKind() == Kind::RegDefEarlyClobber;
326+
}
327+
bool isClobberKind() const { return getKind() == Kind::Clobber; }
328+
bool isImmKind() const { return getKind() == Kind::Imm; }
329+
bool isMemKind() const { return getKind() == Kind::Mem; }
330+
bool isFuncKind() const { return getKind() == Kind::Func; }
331+
StringRef getKindName() const {
332+
switch (getKind()) {
333+
case Kind::RegUse:
334+
return "reguse";
335+
case Kind::RegDef:
336+
return "regdef";
337+
case Kind::RegDefEarlyClobber:
338+
return "regdef-ec";
339+
case Kind::Clobber:
340+
return "clobber";
341+
case Kind::Imm:
342+
return "imm";
343+
case Kind::Mem:
344+
case Kind::Func:
345+
return "mem";
346+
}
347+
}
337348

338-
/// Augment an existing flag word returned by getFlagWord with the constraint
339-
/// code for a memory constraint.
340-
static unsigned getFlagWordForMem(unsigned InputFlag, unsigned Constraint) {
341-
assert((isMemKind(InputFlag) || isFuncKind(InputFlag)) &&
342-
"InputFlag is not a memory (include function) constraint!");
343-
assert(Constraint <= 0x7fff && "Too large a memory constraint ID");
344-
assert(Constraint <= Constraints_Max && "Unknown constraint ID");
345-
assert((InputFlag & ~0xffff) == 0 && "High bits already contain data");
346-
return InputFlag | (Constraint << Constraints_ShiftAmount);
347-
}
349+
/// getNumOperandRegisters - Extract the number of registers field from the
350+
/// inline asm operand flag.
351+
unsigned getNumOperandRegisters() const {
352+
return Bitfield::get<NumOperands>(Storage);
353+
}
348354

349-
static unsigned convertMemFlagWordToMatchingFlagWord(unsigned InputFlag) {
350-
assert(isMemKind(InputFlag));
351-
return InputFlag & ~(0x7fff << Constraints_ShiftAmount);
352-
}
355+
/// isUseOperandTiedToDef - Return true if the flag of the inline asm
356+
/// operand indicates it is an use operand that's matched to a def operand.
357+
bool isUseOperandTiedToDef(unsigned &Idx) const {
358+
if (!isMatched())
359+
return false;
360+
Idx = getData();
361+
return true;
362+
}
353363

354-
static Kind getKind(unsigned Flags) { return static_cast<Kind>(Flags & 7); }
364+
/// hasRegClassConstraint - Returns true if the flag contains a register
365+
/// class constraint. Sets RC to the register class ID.
366+
bool hasRegClassConstraint(unsigned &RC) const {
367+
if (isMatched())
368+
return false;
369+
// setRegClass() uses 0 to mean no register class, and otherwise stores
370+
// RC + 1.
371+
if (!getData())
372+
return false;
373+
RC = getData() - 1;
374+
return true;
375+
}
355376

356-
static unsigned getMemoryConstraintID(unsigned Flag) {
357-
assert((isMemKind(Flag) || isFuncKind(Flag)) &&
358-
"Not expected mem or function flang!");
359-
return (Flag >> Constraints_ShiftAmount) & 0x7fff;
360-
}
377+
// TODO: convert to enum?
378+
unsigned getMemoryConstraintID() const {
379+
assert((isMemKind() || isFuncKind()) &&
380+
"Not expected mem or function flag!");
381+
return getData();
382+
}
361383

362-
/// getNumOperandRegisters - Extract the number of registers field from the
363-
/// inline asm operand flag.
364-
static unsigned getNumOperandRegisters(unsigned Flag) {
365-
return (Flag & 0xffff) >> 3;
366-
}
384+
/// setMatchingOp - Augment an existing flag with information indicating
385+
/// that this input operand is tied to a previous output operand.
386+
void setMatchingOp(unsigned MatchedOperandNo) {
387+
assert(getData() == 0 && "Matching operand already set");
388+
setData(MatchedOperandNo);
389+
setIsMatched(true);
390+
}
367391

368-
/// isUseOperandTiedToDef - Return true if the flag of the inline asm
369-
/// operand indicates it is an use operand that's matched to a def operand.
370-
static bool isUseOperandTiedToDef(unsigned Flag, unsigned &Idx) {
371-
if ((Flag & Flag_MatchingOperand) == 0)
372-
return false;
373-
Idx = (Flag & ~Flag_MatchingOperand) >> 16;
374-
return true;
375-
}
392+
/// setRegClass - Augment an existing flag with the required register class
393+
/// for the following register operands. A tied use operand cannot have a
394+
/// register class, use the register class from the def operand instead.
395+
void setRegClass(unsigned RC) {
396+
assert(!isImmKind() && "Immediates cannot have a register class");
397+
assert(!isMemKind() && "Memory operand cannot have a register class");
398+
assert(getData() == 0 && "Register class already set");
399+
// Store RC + 1, reserve the value 0 to mean 'no register class'.
400+
setData(RC + 1);
401+
}
376402

377-
/// hasRegClassConstraint - Returns true if the flag contains a register
378-
/// class constraint. Sets RC to the register class ID.
379-
static bool hasRegClassConstraint(unsigned Flag, unsigned &RC) {
380-
if (Flag & Flag_MatchingOperand)
381-
return false;
382-
unsigned High = Flag >> 16;
383-
// getFlagWordForRegClass() uses 0 to mean no register class, and otherwise
384-
// stores RC + 1.
385-
if (!High)
386-
return false;
387-
RC = High - 1;
388-
return true;
389-
}
403+
/// setMemConstraint - Augment an existing flag with the constraint code for
404+
/// a memory constraint.
405+
void setMemConstraint(unsigned Constraint) {
406+
assert((isMemKind() || isFuncKind()) &&
407+
"Flag is not a memory or function constraint!");
408+
assert(Constraint <= Constraints_Max && "Unknown constraint ID");
409+
assert(getData() == 0 && "Mem constraint already set");
410+
setData(Constraint);
411+
}
412+
/// clearMemConstraint - Similar to setMemConstraint(0), but without the
413+
/// assertion checking that the constraint has not been set previously.
414+
void clearMemConstraint() {
415+
assert((isMemKind() || isFuncKind()) &&
416+
"Flag is not a memory or function constraint!");
417+
setData(0);
418+
}
419+
};
390420

391421
static std::vector<StringRef> getExtraInfoNames(unsigned ExtraInfo) {
392422
std::vector<StringRef> Result;
@@ -412,25 +442,6 @@ class InlineAsm final : public Value {
412442
return Result;
413443
}
414444

415-
static StringRef getKindName(Kind Kind) {
416-
switch (Kind) {
417-
case Kind::RegUse:
418-
return "reguse";
419-
case Kind::RegDef:
420-
return "regdef";
421-
case Kind::RegDefEarlyClobber:
422-
return "regdef-ec";
423-
case Kind::Clobber:
424-
return "clobber";
425-
case Kind::Imm:
426-
return "imm";
427-
case Kind::Mem:
428-
case Kind::Func:
429-
return "mem";
430-
}
431-
llvm_unreachable("Unknown operand kind");
432-
}
433-
434445
static StringRef getMemConstraintName(unsigned Constraint) {
435446
switch (Constraint) {
436447
case InlineAsm::Constraint_es:

llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,8 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
278278
for (; Val; --Val) {
279279
if (OpNo >= MI->getNumOperands())
280280
break;
281-
unsigned OpFlags = MI->getOperand(OpNo).getImm();
282-
OpNo += InlineAsm::getNumOperandRegisters(OpFlags) + 1;
281+
const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());
282+
OpNo += F.getNumOperandRegisters() + 1;
283283
}
284284

285285
// We may have a location metadata attached to the end of the
@@ -288,7 +288,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
288288
if (OpNo >= MI->getNumOperands() || MI->getOperand(OpNo).isMetadata()) {
289289
Error = true;
290290
} else {
291-
unsigned OpFlags = MI->getOperand(OpNo).getImm();
291+
const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());
292292
++OpNo; // Skip over the ID number.
293293

294294
// FIXME: Shouldn't arch-independent output template handling go into
@@ -302,7 +302,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
302302
} else if (MI->getOperand(OpNo).isMBB()) {
303303
const MCSymbol *Sym = MI->getOperand(OpNo).getMBB()->getSymbol();
304304
Sym->print(OS, AP->MAI);
305-
} else if (InlineAsm::isMemKind(OpFlags)) {
305+
} else if (F.isMemKind()) {
306306
Error = AP->PrintAsmMemoryOperand(
307307
MI, OpNo, Modifier[0] ? Modifier : nullptr, OS);
308308
} else {
@@ -379,14 +379,14 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
379379
const MachineOperand &MO = MI->getOperand(I);
380380
if (!MO.isImm())
381381
continue;
382-
unsigned Flags = MO.getImm();
383-
if (InlineAsm::getKind(Flags) == InlineAsm::Kind::Clobber) {
382+
const InlineAsm::Flag F(MO.getImm());
383+
if (F.isClobberKind()) {
384384
Register Reg = MI->getOperand(I + 1).getReg();
385385
if (!TRI->isAsmClobberable(*MF, Reg))
386386
RestrRegs.push_back(Reg);
387387
}
388388
// Skip to one before the next operand descriptor, if it exists.
389-
I += InlineAsm::getNumOperandRegisters(Flags);
389+
I += F.getNumOperandRegisters();
390390
}
391391

392392
if (!RestrRegs.empty()) {

0 commit comments

Comments
 (0)