Skip to content

[InlineAsm] refactor InlineAsm class NFC #65649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 139 additions & 128 deletions llvm/include/llvm/IR/InlineAsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef LLVM_IR_INLINEASM_H
#define LLVM_IR_INLINEASM_H

#include "llvm/ADT/Bitfields.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Value.h"
Expand Down Expand Up @@ -196,23 +197,6 @@ class InlineAsm final : public Value {
return V->getValueID() == Value::InlineAsmVal;
}

// These are helper methods for dealing with flags in the INLINEASM SDNode
// in the backend.
//
// The encoding of the flag word is currently:
// Bits 2-0 - A Kind::* value indicating the kind of the operand.
// Bits 15-3 - The number of SDNode operands associated with this inline
// assembly operand.
// If bit 31 is set:
// Bit 30-16 - The operand number that this operand must match.
// When bits 2-0 are Kind::Mem, the Constraint_* value must be
// obtained from the flags for this operand number.
// Else if bits 2-0 are Kind::Mem:
// Bit 30-16 - A Constraint_* value indicating the original constraint
// code.
// Else:
// Bit 30-16 - The register class ID to use for the operand.

enum : uint32_t {
// Fixed operands on an INLINEASM SDNode.
Op_InputChain = 0,
Expand Down Expand Up @@ -241,6 +225,7 @@ class InlineAsm final : public Value {
// Addresses are included here as they need to be treated the same by the
// backend, the only difference is that they are not used to actaully
// access memory by the instruction.
// TODO: convert to enum?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's ok with you, I'd prefer to do this in a distinct PR, akin to:

commit 2fad6e6 ("[InlineAsm] wrap Kind in enum class NFC")

Constraint_Unknown = 0,
Constraint_es,
Constraint_i,
Expand Down Expand Up @@ -274,15 +259,12 @@ class InlineAsm final : public Value {
Constraint_ZT,

Constraints_Max = Constraint_ZT,
Constraints_ShiftAmount = 16,

Flag_MatchingOperand = 0x80000000
};

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

static unsigned getFlagWord(Kind Kind, unsigned NumOps) {
assert(((NumOps << 3) & ~0xffff) == 0 && "Too many inline asm operands!");
return static_cast<unsigned>(Kind) | (NumOps << 3);
}

static bool isRegDefKind(unsigned Flag) {
return getKind(Flag) == Kind::RegDef;
}
static bool isImmKind(unsigned Flag) { return getKind(Flag) == Kind::Imm; }
static bool isMemKind(unsigned Flag) { return getKind(Flag) == Kind::Mem; }
static bool isFuncKind(unsigned Flag) { return getKind(Flag) == Kind::Func; }
static bool isRegDefEarlyClobberKind(unsigned Flag) {
return getKind(Flag) == Kind::RegDefEarlyClobber;
}
static bool isClobberKind(unsigned Flag) {
return getKind(Flag) == Kind::Clobber;
}

/// getFlagWordForMatchingOp - Augment an existing flag word returned by
/// getFlagWord with information indicating that this input operand is tied
/// to a previous output operand.
static unsigned getFlagWordForMatchingOp(unsigned InputFlag,
unsigned MatchedOperandNo) {
assert(MatchedOperandNo <= 0x7fff && "Too big matched operand");
assert((InputFlag & ~0xffff) == 0 && "High bits already contain data");
return InputFlag | Flag_MatchingOperand | (MatchedOperandNo << 16);
}

/// getFlagWordForRegClass - Augment an existing flag word returned by
/// getFlagWord with the required register class for the following register
/// operands.
/// A tied use operand cannot have a register class, use the register class
/// from the def operand instead.
static unsigned getFlagWordForRegClass(unsigned InputFlag, unsigned RC) {
// Store RC + 1, reserve the value 0 to mean 'no register class'.
++RC;
assert(!isImmKind(InputFlag) && "Immediates cannot have a register class");
assert(!isMemKind(InputFlag) && "Memory operand cannot have a register class");
assert(RC <= 0x7fff && "Too large register class ID");
assert((InputFlag & ~0xffff) == 0 && "High bits already contain data");
return InputFlag | (RC << 16);
}
// These are helper methods for dealing with flags in the INLINEASM SDNode
// in the backend.
//
// The encoding of Flag is currently:
// Bits 2-0 - A Kind::* value indicating the kind of the operand.
// Bits 15-3 - The number of SDNode operands associated with this inline
// assembly operand.
// If bit 31 is set:
// Bit 30-16 - The operand number that this operand must match.
// When bits 2-0 are Kind::Mem, the Constraint_* value must be
// obtained from the flags for this operand number.
// Else if bits 2-0 are Kind::Mem:
// Bit 30-16 - A Constraint_* value indicating the original constraint
// code.
// Else:
// Bit 30-16 - The register class ID to use for the operand.
//
// Bits 30-16 are called "Data" for lack of a better name. The getter is
// intentionally private; the public methods that rely on that private method
// should be used to check invariants first before accessing Data.
class Flag {
uint32_t Storage;
using KindField = Bitfield::Element<Kind, 0, 3, Kind::Func>;
using NumOperands = Bitfield::Element<unsigned, 3, 13>;
using Data = Bitfield::Element<unsigned, 16, 15>;
using IsMatched = Bitfield::Element<bool, 31, 1>;

unsigned getData() const { return Bitfield::get<Data>(Storage); }
bool isMatched() const { return Bitfield::get<IsMatched>(Storage); }
void setKind(Kind K) { Bitfield::set<KindField>(Storage, K); }
void setNumOperands(unsigned N) { Bitfield::set<NumOperands>(Storage, N); }
void setData(unsigned D) { Bitfield::set<Data>(Storage, D); }
void setIsMatched(bool B) { Bitfield::set<IsMatched>(Storage, B); }

public:
Flag() : Storage(0) {}
explicit Flag(uint32_t F) : Storage(F) {}
Flag(enum Kind K, unsigned NumOps) {
setKind(K);
setNumOperands(NumOps);
setData(0);
setIsMatched(false);
}
operator uint32_t() { return Storage; }
Kind getKind() const { return Bitfield::get<KindField>(Storage); }
bool isRegUseKind() const { return getKind() == Kind::RegUse; }
bool isRegDefKind() const { return getKind() == Kind::RegDef; }
bool isRegDefEarlyClobberKind() const {
return getKind() == Kind::RegDefEarlyClobber;
}
bool isClobberKind() const { return getKind() == Kind::Clobber; }
bool isImmKind() const { return getKind() == Kind::Imm; }
bool isMemKind() const { return getKind() == Kind::Mem; }
bool isFuncKind() const { return getKind() == Kind::Func; }
StringRef getKindName() const {
switch (getKind()) {
case Kind::RegUse:
return "reguse";
case Kind::RegDef:
return "regdef";
case Kind::RegDefEarlyClobber:
return "regdef-ec";
case Kind::Clobber:
return "clobber";
case Kind::Imm:
return "imm";
case Kind::Mem:
case Kind::Func:
return "mem";
}
}

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

static unsigned convertMemFlagWordToMatchingFlagWord(unsigned InputFlag) {
assert(isMemKind(InputFlag));
return InputFlag & ~(0x7fff << Constraints_ShiftAmount);
}
/// isUseOperandTiedToDef - Return true if the flag of the inline asm
/// operand indicates it is an use operand that's matched to a def operand.
bool isUseOperandTiedToDef(unsigned &Idx) const {
if (!isMatched())
return false;
Idx = getData();
return true;
}

static Kind getKind(unsigned Flags) { return static_cast<Kind>(Flags & 7); }
/// hasRegClassConstraint - Returns true if the flag contains a register
/// class constraint. Sets RC to the register class ID.
bool hasRegClassConstraint(unsigned &RC) const {
if (isMatched())
return false;
// setRegClass() uses 0 to mean no register class, and otherwise stores
// RC + 1.
if (!getData())
return false;
RC = getData() - 1;
return true;
}

static unsigned getMemoryConstraintID(unsigned Flag) {
assert((isMemKind(Flag) || isFuncKind(Flag)) &&
"Not expected mem or function flang!");
return (Flag >> Constraints_ShiftAmount) & 0x7fff;
}
// TODO: convert to enum?
unsigned getMemoryConstraintID() const {
assert((isMemKind() || isFuncKind()) &&
"Not expected mem or function flag!");
return getData();
}

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

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

/// hasRegClassConstraint - Returns true if the flag contains a register
/// class constraint. Sets RC to the register class ID.
static bool hasRegClassConstraint(unsigned Flag, unsigned &RC) {
if (Flag & Flag_MatchingOperand)
return false;
unsigned High = Flag >> 16;
// getFlagWordForRegClass() uses 0 to mean no register class, and otherwise
// stores RC + 1.
if (!High)
return false;
RC = High - 1;
return true;
}
/// setMemConstraint - Augment an existing flag with the constraint code for
/// a memory constraint.
void setMemConstraint(unsigned Constraint) {
assert((isMemKind() || isFuncKind()) &&
"Flag is not a memory or function constraint!");
assert(Constraint <= Constraints_Max && "Unknown constraint ID");
assert(getData() == 0 && "Mem constraint already set");
setData(Constraint);
}
/// clearMemConstraint - Similar to setMemConstraint(0), but without the
/// assertion checking that the constraint has not been set previously.
void clearMemConstraint() {
assert((isMemKind() || isFuncKind()) &&
"Flag is not a memory or function constraint!");
setData(0);
}
};

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

static StringRef getKindName(Kind Kind) {
switch (Kind) {
case Kind::RegUse:
return "reguse";
case Kind::RegDef:
return "regdef";
case Kind::RegDefEarlyClobber:
return "regdef-ec";
case Kind::Clobber:
return "clobber";
case Kind::Imm:
return "imm";
case Kind::Mem:
case Kind::Func:
return "mem";
}
llvm_unreachable("Unknown operand kind");
}

static StringRef getMemConstraintName(unsigned Constraint) {
switch (Constraint) {
case InlineAsm::Constraint_es:
Expand Down
14 changes: 7 additions & 7 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
for (; Val; --Val) {
if (OpNo >= MI->getNumOperands())
break;
unsigned OpFlags = MI->getOperand(OpNo).getImm();
OpNo += InlineAsm::getNumOperandRegisters(OpFlags) + 1;
const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());
OpNo += F.getNumOperandRegisters() + 1;
}

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

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

if (!RestrRegs.empty()) {
Expand Down
Loading