Skip to content

Add diagnostic help for inline asm operand constraint 'H' #88248

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

Closed
wants to merge 3 commits into from
Closed
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
12 changes: 9 additions & 3 deletions llvm/include/llvm/CodeGen/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,15 @@ class AsmPrinter : public MachineFunctionPass {
/// Print the specified operand of MI, an INLINEASM instruction, using the
/// specified assembler variant. Targets should override this to format as
/// appropriate. This method can return true if the operand is erroneous.
virtual bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &OS);

virtual AsmOperandErrorCode PrintAsmOperand(const MachineInstr *MI,
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally the mechanical change to use AsmOperandErrorCode instead of bool should be done first, before the behavior change

unsigned OpNo,
const char *ExtraCode,
raw_ostream &OS);

/// Print Operand Constraint Error related helpful message
virtual void diagnoseAsmOperandError(LLVMContext &C,
const AsmOperandErrorCode,
const char *AsmStr, uint64_t Loc);
/// Print the specified operand of MI, an INLINEASM instruction, using the
/// specified assembler variant as an address. Targets should override this to
/// format as appropriate. This method can return true if the operand is
Expand Down
16 changes: 15 additions & 1 deletion llvm/include/llvm/IR/InlineAsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,20 @@ class InlineAsm final : public Value {
}
};

/// Inline Asm specifies input & output constraint which can
/// specifiy target specific criteria for operand. If this criteria
/// does not match, we must throw error.
/// NO_ERROR represents Operand constraints are valid/applicable
/// OPERAND_ERROR represents some constraint(unspecified) failed
/// UNKNOWN_MODIFIER_ERROR represents use of unknown char constraint
/// CONSTRAINT_<char>_ERROR represents error regarding constraint.
enum class AsmOperandErrorCode {
NO_ERROR = 0,
OPERAND_ERROR,
UNKNOWN_MODIFIER_ERROR,
CONSTRAINT_H_ERROR,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why can't we just make the constraint code available to the error printing code? Having one of these for every possible case sounds like a headache.

Copy link
Contributor Author

@mahesh-attarde mahesh-attarde Apr 10, 2024

Choose a reason for hiding this comment

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

All constraint codes related to inline asm are defined in same file.
Will adding in AsmPrinter.h be reasonable ?

Copy link
Contributor

Choose a reason for hiding this comment

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

The constraint H seems too specific. Why not just let this be an arbitrary string carried by Error?

};

} // end namespace llvm

#endif // LLVM_IR_INLINEASM_H
#endif // LLVM_IR_INLINEASM_H
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing newline

54 changes: 33 additions & 21 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
unsigned OpNo = InlineAsm::MIOp_FirstOperand;

bool Error = false;

AsmOperandErrorCode OpErrorCode = AsmOperandErrorCode::NO_ERROR;
// Scan to find the machine operand number for the operand.
for (; Val; --Val) {
if (OpNo >= MI->getNumOperands())
Expand Down Expand Up @@ -306,15 +306,15 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
Error = AP->PrintAsmMemoryOperand(
MI, OpNo, Modifier[0] ? Modifier : nullptr, OS);
} else {
Error = AP->PrintAsmOperand(MI, OpNo,
Modifier[0] ? Modifier : nullptr, OS);
OpErrorCode = AP->PrintAsmOperand(
MI, OpNo, Modifier[0] ? Modifier : nullptr, OS);
}
}
if (Error) {
std::string msg;
raw_string_ostream Msg(msg);
Msg << "invalid operand in inline asm: '" << AsmStr << "'";
MMI->getModule()->getContext().emitError(LocCookie, Msg.str());
if (Error || (OpErrorCode != AsmOperandErrorCode::NO_ERROR)) {
if (Error)
OpErrorCode = AsmOperandErrorCode::OPERAND_ERROR;
AP->diagnoseAsmOperandError(MMI->getModule()->getContext(),
OpErrorCode, AsmStr, LocCookie);
}
}
break;
Expand Down Expand Up @@ -461,50 +461,62 @@ void AsmPrinter::PrintSymbolOperand(const MachineOperand &MO, raw_ostream &OS) {
printOffset(MO.getOffset(), OS);
}

void AsmPrinter::diagnoseAsmOperandError(LLVMContext &C,
const AsmOperandErrorCode ErrCode,
const char *AsmStr,
const uint64_t Loc) {
std::string msg;
raw_string_ostream Msg(msg);
Msg << "invalid operand in inline asm: '" << AsmStr << "'";
C.emitError(Loc, Msg.str());
}
/// PrintAsmOperand - Print the specified operand of MI, an INLINEASM
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/// PrintAsmOperand - Print the specified operand of MI, an INLINEASM
/// PrintAsmOperand - Print the specified operand of MI, an INLINEASM

/// instruction, using the specified assembler variant. Targets should
/// override this to format as appropriate for machine specific ExtraCodes
/// or when the arch-independent handling would be too complex otherwise.
bool AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) {
AsmOperandErrorCode AsmPrinter::PrintAsmOperand(const MachineInstr *MI,
unsigned OpNo,
const char *ExtraCode,
raw_ostream &O) {
// Does this asm operand have a single letter operand modifier?
if (ExtraCode && ExtraCode[0]) {
if (ExtraCode[1] != 0) return true; // Unknown modifier.
if (ExtraCode[1] != 0)
return AsmOperandErrorCode::UNKNOWN_MODIFIER_ERROR; // Unknown modifier.

// https://gcc.gnu.org/onlinedocs/gccint/Output-Template.html
const MachineOperand &MO = MI->getOperand(OpNo);
switch (ExtraCode[0]) {
default:
return true; // Unknown modifier.
return AsmOperandErrorCode::UNKNOWN_MODIFIER_ERROR; // Unknown modifier.
case 'a': // Print as memory address.
if (MO.isReg()) {
PrintAsmMemoryOperand(MI, OpNo, nullptr, O);
return false;
return AsmOperandErrorCode::NO_ERROR;
}
[[fallthrough]]; // GCC allows '%a' to behave like '%c' with immediates.
case 'c': // Substitute immediate value without immediate syntax
if (MO.isImm()) {
O << MO.getImm();
return false;
return AsmOperandErrorCode::NO_ERROR;
}
if (MO.isGlobal()) {
PrintSymbolOperand(MO, O);
return false;
return AsmOperandErrorCode::NO_ERROR;
}
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
case 'n': // Negate the immediate constant.
if (!MO.isImm())
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
O << -MO.getImm();
return false;
return AsmOperandErrorCode::NO_ERROR;
case 's': // The GCC deprecated s modifier
if (!MO.isImm())
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
O << ((32 - MO.getImm()) & 31);
return false;
return AsmOperandErrorCode::NO_ERROR;
}
}
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
}

bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
Expand Down
48 changes: 30 additions & 18 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,9 @@ class AArch64AsmPrinter : public AsmPrinter {
const TargetRegisterClass *RC, unsigned AltName,
raw_ostream &O);

bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
const char *ExtraCode, raw_ostream &O) override;
AsmOperandErrorCode PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
const char *ExtraCode,
raw_ostream &O) override;
bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
const char *ExtraCode, raw_ostream &O) override;

Expand Down Expand Up @@ -915,33 +916,38 @@ bool AArch64AsmPrinter::printAsmRegInClass(const MachineOperand &MO,
return false;
}

bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
const char *ExtraCode, raw_ostream &O) {
AsmOperandErrorCode AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI,
unsigned OpNum,
const char *ExtraCode,
raw_ostream &O) {
const MachineOperand &MO = MI->getOperand(OpNum);

// First try the generic code, which knows about modifiers like 'c' and 'n'.
if (!AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O))
return false;
if (AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O) ==
AsmOperandErrorCode::NO_ERROR)
return AsmOperandErrorCode::NO_ERROR;

// Does this asm operand have a single letter operand modifier?
if (ExtraCode && ExtraCode[0]) {
if (ExtraCode[1] != 0)
return true; // Unknown modifier.
return AsmOperandErrorCode::UNKNOWN_MODIFIER_ERROR; // Unknown modifier.

switch (ExtraCode[0]) {
default:
return true; // Unknown modifier.
return AsmOperandErrorCode::UNKNOWN_MODIFIER_ERROR; // Unknown modifier.
case 'w': // Print W register
case 'x': // Print X register
if (MO.isReg())
return printAsmMRegister(MO, ExtraCode[0], O);
return (printAsmMRegister(MO, ExtraCode[0], O)
? AsmOperandErrorCode::OPERAND_ERROR
: AsmOperandErrorCode::NO_ERROR);
if (MO.isImm() && MO.getImm() == 0) {
unsigned Reg = ExtraCode[0] == 'w' ? AArch64::WZR : AArch64::XZR;
O << AArch64InstPrinter::getRegisterName(Reg);
return false;
return AsmOperandErrorCode::NO_ERROR;
}
printOperand(MI, OpNum, O);
return false;
return AsmOperandErrorCode::NO_ERROR;
case 'b': // Print B register.
case 'h': // Print H register.
case 's': // Print S register.
Expand Down Expand Up @@ -970,12 +976,14 @@ bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
RC = &AArch64::ZPRRegClass;
break;
default:
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
}
return printAsmRegInClass(MO, RC, AArch64::NoRegAltName, O);
return (printAsmRegInClass(MO, RC, AArch64::NoRegAltName, O)
? AsmOperandErrorCode::OPERAND_ERROR
: AsmOperandErrorCode::NO_ERROR);
}
printOperand(MI, OpNum, O);
return false;
return AsmOperandErrorCode::NO_ERROR;
}
}

Expand All @@ -987,11 +995,13 @@ bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
// If this is a w or x register, print an x register.
if (AArch64::GPR32allRegClass.contains(Reg) ||
AArch64::GPR64allRegClass.contains(Reg))
return printAsmMRegister(MO, 'x', O);
return (printAsmMRegister(MO, 'x', O) ? AsmOperandErrorCode::OPERAND_ERROR
: AsmOperandErrorCode::NO_ERROR);

// If this is an x register tuple, print an x register.
if (AArch64::GPR64x8ClassRegClass.contains(Reg))
return printAsmMRegister(MO, 't', O);
return (printAsmMRegister(MO, 't', O) ? AsmOperandErrorCode::OPERAND_ERROR
: AsmOperandErrorCode::NO_ERROR);

unsigned AltName = AArch64::NoRegAltName;
const TargetRegisterClass *RegClass;
Expand All @@ -1007,11 +1017,13 @@ bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
}

// If this is a b, h, s, d, or q register, print it as a v register.
return printAsmRegInClass(MO, RegClass, AltName, O);
return (printAsmRegInClass(MO, RegClass, AltName, O)
? AsmOperandErrorCode::OPERAND_ERROR
: AsmOperandErrorCode::NO_ERROR);
}

printOperand(MI, OpNum, O);
return false;
return AsmOperandErrorCode::NO_ERROR;
}

bool AArch64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
Expand Down
21 changes: 12 additions & 9 deletions llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1240,21 +1240,24 @@ void AMDGPUAsmPrinter::getAmdKernelCode(amd_kernel_code_t &Out,
Out.kernarg_segment_alignment = Log2(std::max(Align(16), MaxKernArgAlign));
}

bool AMDGPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) {
AsmOperandErrorCode AMDGPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
unsigned OpNo,
const char *ExtraCode,
raw_ostream &O) {
// First try the generic code, which knows about modifiers like 'c' and 'n'.
if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, O))
return false;
if (AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, O) ==
AsmOperandErrorCode::NO_ERROR)
return AsmOperandErrorCode::NO_ERROR;

if (ExtraCode && ExtraCode[0]) {
if (ExtraCode[1] != 0)
return true; // Unknown modifier.
return AsmOperandErrorCode::UNKNOWN_MODIFIER_ERROR; // Unknown modifier.

switch (ExtraCode[0]) {
case 'r':
break;
default:
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
}
}

Expand All @@ -1263,7 +1266,7 @@ bool AMDGPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
if (MO.isReg()) {
AMDGPUInstPrinter::printRegOperand(MO.getReg(), O,
*MF->getSubtarget().getRegisterInfo());
return false;
return AsmOperandErrorCode::NO_ERROR;
} else if (MO.isImm()) {
int64_t Val = MO.getImm();
if (AMDGPU::isInlinableIntLiteral(Val)) {
Expand All @@ -1275,9 +1278,9 @@ bool AMDGPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
} else {
O << format("0x%" PRIx64, static_cast<uint64_t>(Val));
}
return false;
return AsmOperandErrorCode::NO_ERROR;
}
return true;
return AsmOperandErrorCode::OPERAND_ERROR;
}

void AMDGPUAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ class AMDGPUAsmPrinter final : public AsmPrinter {

void emitEndOfAsmFile(Module &M) override;

bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) override;
AsmOperandErrorCode PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode,
raw_ostream &O) override;

protected:
void getAnalysisUsage(AnalysisUsage &AU) const override;
Expand Down
Loading