[llvm] [CodeGen][NFC] Refactor inline asm constraint determination (PR #195104)
Bill Wendling via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 30 08:31:41 PDT 2026
https://github.com/bwendling updated https://github.com/llvm/llvm-project/pull/195104
>From 6578076f95271f9d60070e9d6c4f1311f9fec0f3 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 30 Apr 2026 07:51:25 -0700
Subject: [PATCH 1/2] [CodeGen][NFC] Refactor inline asm constraint
determination
The original function was very large and unwieldy. This also sets the
function up for future improvements.
---
.../SelectionDAG/SelectionDAGBuilder.cpp | 376 ++++++++++--------
.../SelectionDAG/SelectionDAGBuilder.h | 3 +
2 files changed, 221 insertions(+), 158 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index fb454afd141e2..423000e665b67 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -105,6 +105,7 @@
#include <cstddef>
#include <limits>
#include <optional>
+#include <sstream>
#include <tuple>
using namespace llvm;
@@ -10193,32 +10194,34 @@ static bool isFunction(SDValue Op) {
return false;
}
-/// visitInlineAsm - Handle a call to an InlineAsm object.
-void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
- const BasicBlock *EHPadBB) {
- const InlineAsm *IA = cast<InlineAsm>(Call.getCalledOperand());
+namespace {
- /// ConstraintOperands - Information about all of the constraints.
+struct ConstraintDecisionInfo {
SmallVector<SDISelAsmOperandInfo, 16> ConstraintOperands;
+ std::vector<SDValue> AsmNodeOperands;
+ SDValue Glue, Chain;
+ bool HasSideEffect = false;
+ MCSymbol *BeginLabel = nullptr;
+ std::stringstream ErrorMsg;
+};
- const TargetLowering &TLI = DAG.getTargetLoweringInfo();
- TargetLowering::AsmOperandInfoVector TargetConstraints = TLI.ParseConstraints(
- DAG.getDataLayout(), DAG.getSubtarget().getRegisterInfo(), Call);
-
- // First Pass: Calculate HasSideEffects and ExtraFlags (AlignStack,
- // AsmDialect, MayLoad, MayStore).
- bool HasSideEffect = IA->hasSideEffects();
- ExtraFlags ExtraInfo(Call);
+} // end anonymous namespace
+/// Construct operand info objects.
+static bool
+ConstructOperandInfo(ConstraintDecisionInfo &Info,
+ TargetLowering::AsmOperandInfoVector &TargetConstraints,
+ SelectionDAGBuilder &Builder, const TargetLowering &TLI,
+ ExtraFlags &ExtraInfo) {
for (auto &T : TargetConstraints) {
- ConstraintOperands.push_back(SDISelAsmOperandInfo(T));
- SDISelAsmOperandInfo &OpInfo = ConstraintOperands.back();
+ Info.ConstraintOperands.push_back(SDISelAsmOperandInfo(T));
+ SDISelAsmOperandInfo &OpInfo = Info.ConstraintOperands.back();
if (OpInfo.CallOperandVal)
- OpInfo.CallOperand = getValue(OpInfo.CallOperandVal);
+ OpInfo.CallOperand = Builder.getValue(OpInfo.CallOperandVal);
- if (!HasSideEffect)
- HasSideEffect = OpInfo.hasMemory(TLI);
+ if (!Info.HasSideEffect)
+ Info.HasSideEffect = OpInfo.hasMemory(TLI);
// Determine if this InlineAsm MayLoad or MayStore based on the constraints.
// FIXME: Could we compute this on OpInfo rather than T?
@@ -10226,45 +10229,33 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// Compute the constraint code and ConstraintType to use.
TLI.ComputeConstraintToUse(T, SDValue());
- if (T.ConstraintType == TargetLowering::C_Immediate &&
- OpInfo.CallOperand && !isa<ConstantSDNode>(OpInfo.CallOperand))
+ if (T.ConstraintType == TargetLowering::C_Immediate && OpInfo.CallOperand &&
+ !isa<ConstantSDNode>(OpInfo.CallOperand)) {
// We've delayed emitting a diagnostic like the "n" constraint because
// inlining could cause an integer showing up.
- return emitInlineAsmError(Call, "constraint '" + Twine(T.ConstraintCode) +
- "' expects an integer constant "
- "expression");
+ Info.ErrorMsg << "constraint '" << T.ConstraintCode
+ << "' expects an integer constant expression";
+ return true;
+ }
ExtraInfo.update(T);
}
- // We won't need to flush pending loads if this asm doesn't touch
- // memory and is nonvolatile.
- SDValue Glue, Chain = (HasSideEffect) ? getRoot() : DAG.getRoot();
-
- bool EmitEHLabels = isa<InvokeInst>(Call);
- if (EmitEHLabels) {
- assert(EHPadBB && "InvokeInst must have an EHPadBB");
- }
- bool IsCallBr = isa<CallBrInst>(Call);
-
- if (IsCallBr || EmitEHLabels) {
- // If this is a callbr or invoke we need to flush pending exports since
- // inlineasm_br and invoke are terminators.
- // We need to do this before nodes are glued to the inlineasm_br node.
- Chain = getControlRoot();
- }
-
- MCSymbol *BeginLabel = nullptr;
- if (EmitEHLabels) {
- Chain = lowerStartEH(Chain, EHPadBB, BeginLabel);
- }
+ return false;
+}
- int OpNo = -1;
- SmallVector<StringRef> AsmStrs;
+/// Compute which constraint option to use for each operand.
+static void
+ComputeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
+ TargetLowering::AsmOperandInfoVector &TargetConstraints,
+ SelectionDAGBuilder &Builder, const TargetLowering &TLI,
+ const TargetMachine &TM, SelectionDAG &DAG) {
+ const auto *IA = cast<InlineAsm>(Call.getCalledOperand());
+ SmallVector<StringRef, 4> AsmStrs;
IA->collectAsmStrs(AsmStrs);
- // Second pass over the constraints: compute which constraint option to use.
- for (SDISelAsmOperandInfo &OpInfo : ConstraintOperands) {
+ int OpNo = -1;
+ for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
if (OpInfo.hasArg() || OpInfo.Type == InlineAsm::isOutput)
OpNo++;
@@ -10273,7 +10264,8 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// other is floating point, or their sizes are different, flag it as an
// error.
if (OpInfo.hasMatchingInput()) {
- SDISelAsmOperandInfo &Input = ConstraintOperands[OpInfo.MatchingInput];
+ SDISelAsmOperandInfo &Input =
+ Info.ConstraintOperands[OpInfo.MatchingInput];
patchMatchingInput(OpInfo, Input, DAG);
}
@@ -10320,7 +10312,8 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
"Can only indirectify direct input operands!");
// Memory operands really want the address of the value.
- Chain = getAddressForMemoryInput(Chain, getCurSDLoc(), OpInfo, DAG);
+ Info.Chain = getAddressForMemoryInput(Info.Chain, Builder.getCurSDLoc(),
+ OpInfo, DAG);
// There is no longer a Value* corresponding to this operand.
OpInfo.CallOperandVal = nullptr;
@@ -10328,58 +10321,46 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// It is now an indirect operand.
OpInfo.isIndirect = true;
}
-
}
+}
- // AsmNodeOperands - The operands for the ISD::INLINEASM node.
- std::vector<SDValue> AsmNodeOperands;
- AsmNodeOperands.push_back(SDValue()); // reserve space for input chain
- AsmNodeOperands.push_back(DAG.getTargetExternalSymbol(
- IA->getAsmString().data(), TLI.getProgramPointerTy(DAG.getDataLayout())));
-
- // If we have a !srcloc metadata node associated with it, we want to attach
- // this to the ultimately generated inline asm machineinstr. To do this, we
- // pass in the third operand as this (potentially null) inline asm MDNode.
- const MDNode *SrcLoc = Call.getMetadata("srcloc");
- AsmNodeOperands.push_back(DAG.getMDNode(SrcLoc));
-
- // Remember the HasSideEffect, AlignStack, AsmDialect, MayLoad and MayStore
- // bits as operand 3.
- AsmNodeOperands.push_back(DAG.getTargetConstant(
- ExtraInfo.get(), getCurSDLoc(), TLI.getPointerTy(DAG.getDataLayout())));
-
- // Third pass: Loop over operands to prepare DAG-level operands.. As part of
- // this, assign virtual and physical registers for inputs and otput.
- for (SDISelAsmOperandInfo &OpInfo : ConstraintOperands) {
+/// Prepare DAG-level operands. As part of this, assign virtual and physical
+/// registers for inputs and output.
+static bool PrepareDAGLevelOperands(ConstraintDecisionInfo &Info,
+ const CallBase &Call,
+ SelectionDAGBuilder &Builder,
+ const TargetLowering &TLI,
+ SelectionDAG &DAG) {
+ SDLoc DL = Builder.getCurSDLoc();
+ for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
// Assign Registers.
SDISelAsmOperandInfo &RefOpInfo =
OpInfo.isMatchingInputConstraint()
- ? ConstraintOperands[OpInfo.getMatchedOperand()]
+ ? Info.ConstraintOperands[OpInfo.getMatchedOperand()]
: OpInfo;
- const auto RegError =
- getRegistersForValue(DAG, getCurSDLoc(), OpInfo, RefOpInfo);
+ const auto RegError = getRegistersForValue(DAG, DL, OpInfo, RefOpInfo);
if (RegError) {
const MachineFunction &MF = DAG.getMachineFunction();
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
const char *RegName = TRI.getName(*RegError);
- emitInlineAsmError(Call, "register '" + Twine(RegName) +
- "' allocated for constraint '" +
- Twine(OpInfo.ConstraintCode) +
- "' does not match required type");
- return;
+ Info.ErrorMsg << "register '" << RegName << "' allocated for constraint '"
+ << OpInfo.ConstraintCode
+ << "' does not match required type";
+ return true;
}
auto DetectWriteToReservedRegister = [&]() {
const MachineFunction &MF = DAG.getMachineFunction();
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+
for (Register Reg : OpInfo.AssignedRegs.Regs) {
if (Reg.isPhysical() && TRI.isInlineAsmReadOnlyReg(MF, Reg)) {
- const char *RegName = TRI.getName(Reg);
- emitInlineAsmError(Call, "write to reserved register '" +
- Twine(RegName) + "'");
+ Info.ErrorMsg << "write to reserved register '" << TRI.getName(Reg)
+ << "'";
return true;
}
}
+
return false;
};
assert((OpInfo.ConstraintType != TargetLowering::C_Address ||
@@ -10398,29 +10379,28 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// Add information to the INLINEASM node to know about this output.
InlineAsm::Flag OpFlags(InlineAsm::Kind::Mem, 1);
OpFlags.setMemConstraint(ConstraintID);
- AsmNodeOperands.push_back(DAG.getTargetConstant(OpFlags, getCurSDLoc(),
- MVT::i32));
- AsmNodeOperands.push_back(OpInfo.CallOperand);
+ Info.AsmNodeOperands.push_back(
+ DAG.getTargetConstant(OpFlags, DL, MVT::i32));
+ Info.AsmNodeOperands.push_back(OpInfo.CallOperand);
} else {
// Otherwise, this outputs to a register (directly for C_Register /
// C_RegisterClass, and a target-defined fashion for
// C_Immediate/C_Other). Find a register that we can use.
if (OpInfo.AssignedRegs.Regs.empty()) {
- emitInlineAsmError(
- Call, "couldn't allocate output register for constraint '" +
- Twine(OpInfo.ConstraintCode) + "'");
- return;
+ Info.ErrorMsg << "couldn't allocate output register for "
+ << "constraint '" << OpInfo.ConstraintCode << "'";
+ return true;
}
if (DetectWriteToReservedRegister())
- return;
+ return true;
// Add information to the INLINEASM node to know that this register is
// set.
OpInfo.AssignedRegs.AddInlineAsmOperands(
OpInfo.isEarlyClobber ? InlineAsm::Kind::RegDefEarlyClobber
: InlineAsm::Kind::RegDef,
- false, 0, getCurSDLoc(), DAG, AsmNodeOperands);
+ false, 0, DL, DAG, Info.AsmNodeOperands);
}
break;
@@ -10432,52 +10412,52 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// If this is required to match an output register we have already set,
// just use its register.
auto CurOp = findMatchingInlineAsmOperand(OpInfo.getMatchedOperand(),
- AsmNodeOperands);
- InlineAsm::Flag Flag(AsmNodeOperands[CurOp]->getAsZExtVal());
+ Info.AsmNodeOperands);
+ InlineAsm::Flag Flag(Info.AsmNodeOperands[CurOp]->getAsZExtVal());
if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {
if (OpInfo.isIndirect) {
// This happens on gcc/testsuite/gcc.dg/pr8788-1.c
- emitInlineAsmError(Call, "inline asm not supported yet: "
- "don't know how to handle tied "
- "indirect register inputs");
- return;
+ Info.ErrorMsg << "inline asm not supported yet: don't know how "
+ << "to handle tied indirect register inputs";
+ return true;
}
SmallVector<Register, 4> Regs;
MachineFunction &MF = DAG.getMachineFunction();
MachineRegisterInfo &MRI = MF.getRegInfo();
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
- auto *R = cast<RegisterSDNode>(AsmNodeOperands[CurOp+1]);
+ auto *R = cast<RegisterSDNode>(Info.AsmNodeOperands[CurOp + 1]);
Register TiedReg = R->getReg();
MVT RegVT = R->getSimpleValueType(0);
const TargetRegisterClass *RC =
TiedReg.isVirtual() ? MRI.getRegClass(TiedReg)
: RegVT != MVT::Untyped ? TLI.getRegClassFor(RegVT)
: TRI.getMinimalPhysRegClass(TiedReg);
- for (unsigned i = 0, e = Flag.getNumOperandRegisters(); i != e; ++i)
+ for (unsigned I = 0, E = Flag.getNumOperandRegisters(); I != E; ++I)
Regs.push_back(MRI.createVirtualRegister(RC));
RegsForValue MatchedRegs(Regs, RegVT, InOperandVal.getValueType());
- SDLoc dl = getCurSDLoc();
// Use the produced MatchedRegs object to
- MatchedRegs.getCopyToRegs(InOperandVal, DAG, dl, Chain, &Glue, &Call);
+ MatchedRegs.getCopyToRegs(InOperandVal, DAG, DL, Info.Chain,
+ &Info.Glue, &Call);
MatchedRegs.AddInlineAsmOperands(InlineAsm::Kind::RegUse, true,
- OpInfo.getMatchedOperand(), dl, DAG,
- AsmNodeOperands);
+ OpInfo.getMatchedOperand(), DL, DAG,
+ Info.AsmNodeOperands);
break;
}
assert(Flag.isMemKind() && "Unknown matching constraint!");
assert(Flag.getNumOperandRegisters() == 1 &&
"Unexpected number of operands");
+
// Add information to the INLINEASM node to know about this input.
// See InlineAsm.h isUseOperandTiedToDef.
Flag.clearMemConstraint();
Flag.setMatchingOp(OpInfo.getMatchedOperand());
- AsmNodeOperands.push_back(DAG.getTargetConstant(
- Flag, getCurSDLoc(), TLI.getPointerTy(DAG.getDataLayout())));
- AsmNodeOperands.push_back(AsmNodeOperands[CurOp+1]);
+ Info.AsmNodeOperands.push_back(DAG.getTargetConstant(
+ Flag, DL, TLI.getPointerTy(DAG.getDataLayout())));
+ Info.AsmNodeOperands.push_back(Info.AsmNodeOperands[CurOp + 1]);
break;
}
@@ -10494,22 +10474,21 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
if (Ops.empty()) {
if (OpInfo.ConstraintType == TargetLowering::C_Immediate)
if (isa<ConstantSDNode>(InOperandVal)) {
- emitInlineAsmError(Call, "value out of range for constraint '" +
- Twine(OpInfo.ConstraintCode) + "'");
- return;
+ Info.ErrorMsg << "value out of range for constraint '"
+ << OpInfo.ConstraintCode << "'";
+ return true;
}
- emitInlineAsmError(Call,
- "invalid operand for inline asm constraint '" +
- Twine(OpInfo.ConstraintCode) + "'");
- return;
+ Info.ErrorMsg << "invalid operand for inline asm constraint '"
+ << OpInfo.ConstraintCode << "'";
+ return true;
}
// Add information to the INLINEASM node to know about this input.
InlineAsm::Flag ResOpType(InlineAsm::Kind::Imm, Ops.size());
- AsmNodeOperands.push_back(DAG.getTargetConstant(
- ResOpType, getCurSDLoc(), TLI.getPointerTy(DAG.getDataLayout())));
- llvm::append_range(AsmNodeOperands, Ops);
+ Info.AsmNodeOperands.push_back(DAG.getTargetConstant(
+ ResOpType, DL, TLI.getPointerTy(DAG.getDataLayout())));
+ llvm::append_range(Info.AsmNodeOperands, Ops);
break;
}
@@ -10529,10 +10508,9 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// Add information to the INLINEASM node to know about this input.
InlineAsm::Flag ResOpType(InlineAsm::Kind::Mem, 1);
ResOpType.setMemConstraint(ConstraintID);
- AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType,
- getCurSDLoc(),
- MVT::i32));
- AsmNodeOperands.push_back(InOperandVal);
+ Info.AsmNodeOperands.push_back(
+ DAG.getTargetConstant(ResOpType, DL, MVT::i32));
+ Info.AsmNodeOperands.push_back(InOperandVal);
break;
}
@@ -10548,7 +10526,7 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
if (isFunction(InOperandVal)) {
auto *GA = cast<GlobalAddressSDNode>(InOperandVal);
ResOpType = InlineAsm::Flag(InlineAsm::Kind::Func, 1);
- AsmOp = DAG.getTargetGlobalAddress(GA->getGlobal(), getCurSDLoc(),
+ AsmOp = DAG.getTargetGlobalAddress(GA->getGlobal(), DL,
InOperandVal.getValueType(),
GA->getOffset());
}
@@ -10556,67 +10534,149 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// Add information to the INLINEASM node to know about this input.
ResOpType.setMemConstraint(ConstraintID);
- AsmNodeOperands.push_back(
- DAG.getTargetConstant(ResOpType, getCurSDLoc(), MVT::i32));
-
- AsmNodeOperands.push_back(AsmOp);
+ Info.AsmNodeOperands.push_back(
+ DAG.getTargetConstant(ResOpType, DL, MVT::i32));
+ Info.AsmNodeOperands.push_back(AsmOp);
break;
}
if (OpInfo.ConstraintType != TargetLowering::C_RegisterClass &&
OpInfo.ConstraintType != TargetLowering::C_Register) {
- emitInlineAsmError(Call, "unknown asm constraint '" +
- Twine(OpInfo.ConstraintCode) + "'");
- return;
+ Info.ErrorMsg << "unknown asm constraint '" << OpInfo.ConstraintCode
+ << "'";
+ return true;
}
// TODO: Support this.
if (OpInfo.isIndirect) {
- emitInlineAsmError(
- Call, "Don't know how to handle indirect register inputs yet "
- "for constraint '" +
- Twine(OpInfo.ConstraintCode) + "'");
- return;
+ Info.ErrorMsg << "Don't know how to handle indirect register inputs "
+ << "yet for constraint '" << OpInfo.ConstraintCode << "'";
+ return true;
}
// Copy the input into the appropriate registers.
if (OpInfo.AssignedRegs.Regs.empty()) {
- emitInlineAsmError(Call,
- "couldn't allocate input reg for constraint '" +
- Twine(OpInfo.ConstraintCode) + "'");
- return;
+ Info.ErrorMsg << "couldn't allocate input reg for constraint '"
+ << OpInfo.ConstraintCode << "'";
+ return true;
}
if (DetectWriteToReservedRegister())
- return;
-
- SDLoc dl = getCurSDLoc();
-
- OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, dl, Chain, &Glue,
- &Call);
+ return true;
- OpInfo.AssignedRegs.AddInlineAsmOperands(InlineAsm::Kind::RegUse, false,
- 0, dl, DAG, AsmNodeOperands);
+ OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, DL, Info.Chain,
+ &Info.Glue, &Call);
+ OpInfo.AssignedRegs.AddInlineAsmOperands(
+ InlineAsm::Kind::RegUse, false, 0, DL, DAG, Info.AsmNodeOperands);
break;
}
+
case InlineAsm::isClobber:
// Add the clobbered value to the operand list, so that the register
// allocator is aware that the physreg got clobbered.
if (!OpInfo.AssignedRegs.Regs.empty())
- OpInfo.AssignedRegs.AddInlineAsmOperands(InlineAsm::Kind::Clobber,
- false, 0, getCurSDLoc(), DAG,
- AsmNodeOperands);
+ OpInfo.AssignedRegs.AddInlineAsmOperands(
+ InlineAsm::Kind::Clobber, false, 0, DL, DAG, Info.AsmNodeOperands);
break;
}
}
+ return false;
+}
+
+/// DetermineConstraints - ASM operands may have more than one constraint. We
+/// want to choose the "best" constraint for each operand to avoid horrible
+/// code generation---e.g., for "rm" we would like to use "r". This function
+/// tries different constraints in order from best to worst. If a given
+/// constraint isn't possible, e.g., because no registers are available, then
+/// the function returns 'true' and is rerun on the next constraint.
+///
+/// Each operand which has a suitable constraint is marked as "finalized". This
+/// helps reduce the number of times we need to run this function, keeping the
+/// complexity at O(n), where 'n' is the total number of constraints on inputs
+/// and outputs (i.e., for "rm", n == 2).
+static bool
+DetermineConstraints(ConstraintDecisionInfo &Info,
+ TargetLowering::AsmOperandInfoVector &TargetConstraints,
+ const CallBase &Call, SelectionDAGBuilder &Builder,
+ const TargetLowering &TLI, const TargetMachine &TM,
+ SelectionDAG &DAG, const BasicBlock *EHPadBB) {
+ const auto *IA = cast<InlineAsm>(Call.getCalledOperand());
+ ExtraFlags ExtraInfo(Call);
+
+ // First pass: Construct operand info objects.
+ Info.HasSideEffect = IA->hasSideEffects();
+ if (ConstructOperandInfo(Info, TargetConstraints, Builder, TLI, ExtraInfo))
+ return true;
+
+ // We won't need to flush pending loads if this asm doesn't touch
+ // memory and is nonvolatile.
+ Info.Chain = Info.HasSideEffect ? Builder.getRoot() : DAG.getRoot();
+
+ bool IsCallBr = isa<CallBrInst>(Call);
+ bool EmitEHLabels = isa<InvokeInst>(Call);
+ if (IsCallBr || EmitEHLabels)
+ // If this is a callbr or invoke we need to flush pending exports since
+ // inlineasm_br and invoke are terminators.
+ // We need to do this before nodes are glued to the inlineasm_br node.
+ Info.Chain = Builder.getControlRoot();
+
+ if (EmitEHLabels)
+ Info.Chain = Builder.lowerStartEH(Info.Chain, EHPadBB, Info.BeginLabel);
+
+ // Second pass: Compute which constraint option to use.
+ ComputeConstraintToUse(Info, Call, TargetConstraints, Builder, TLI, TM, DAG);
+
+ // AsmNodeOperands - The operands for the ISD::INLINEASM node.
+ Info.AsmNodeOperands.push_back(SDValue()); // reserve space for input chain
+ Info.AsmNodeOperands.push_back(DAG.getTargetExternalSymbol(
+ IA->getAsmString().data(), TLI.getProgramPointerTy(DAG.getDataLayout())));
+
+ // If we have a !srcloc metadata node associated with it, we want to attach
+ // this to the ultimately generated inline asm machineinstr. To do this, we
+ // pass in the third operand as this (potentially null) inline asm MDNode.
+ const MDNode *SrcLoc = Call.getMetadata("srcloc");
+ Info.AsmNodeOperands.push_back(DAG.getMDNode(SrcLoc));
+
+ // Remember the HasSideEffect, AlignStack, AsmDialect, MayLoad and MayStore
+ // bits as operand 3.
+ Info.AsmNodeOperands.push_back(
+ DAG.getTargetConstant(ExtraInfo.get(), Builder.getCurSDLoc(),
+ TLI.getPointerTy(DAG.getDataLayout())));
+
+ // Third pass: Prepare DAG-level operands
+ return PrepareDAGLevelOperands(Info, Call, Builder, TLI, DAG);
+}
+
+/// visitInlineAsm - Handle a call to an InlineAsm object.
+void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
+ const BasicBlock *EHPadBB) {
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ TargetLowering::AsmOperandInfoVector TargetConstraints = TLI.ParseConstraints(
+ DAG.getDataLayout(), DAG.getSubtarget().getRegisterInfo(), Call);
+
+ bool EmitEHLabels = isa<InvokeInst>(Call);
+ if (EmitEHLabels)
+ assert(EHPadBB && "InvokeInst must have an EHPadBB");
+
+ ConstraintDecisionInfo Info;
+ if (DetermineConstraints(Info, TargetConstraints, Call, *this, TLI, TM, DAG,
+ EHPadBB))
+ return emitInlineAsmError(Call, Info.ErrorMsg.str());
+
+ SDValue Glue = Info.Glue;
+ SDValue Chain = Info.Chain;
+
// Finish up input operands. Set the input chain and add the flag last.
- AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
- if (Glue.getNode()) AsmNodeOperands.push_back(Glue);
+ Info.AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
+ if (Glue.getNode())
+ Info.AsmNodeOperands.push_back(Glue);
+ bool IsCallBr = isa<CallBrInst>(Call);
unsigned ISDOpc = IsCallBr ? ISD::INLINEASM_BR : ISD::INLINEASM;
- Chain = DAG.getNode(ISDOpc, getCurSDLoc(),
- DAG.getVTList(MVT::Other, MVT::Glue), AsmNodeOperands);
+ Chain =
+ DAG.getNode(ISDOpc, getCurSDLoc(), DAG.getVTList(MVT::Other, MVT::Glue),
+ Info.AsmNodeOperands);
Glue = Chain.getValue(1);
// Do additional work to generate outputs.
@@ -10664,7 +10724,7 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
};
// Deal with output operands.
- for (SDISelAsmOperandInfo &OpInfo : ConstraintOperands) {
+ for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
if (OpInfo.Type == InlineAsm::isOutput) {
SDValue Val;
// Skip trivial output operands.
@@ -10725,13 +10785,13 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
if (!OutChains.empty())
Chain = DAG.getNode(ISD::TokenFactor, getCurSDLoc(), MVT::Other, OutChains);
- if (EmitEHLabels) {
- Chain = lowerEndEH(Chain, cast<InvokeInst>(&Call), EHPadBB, BeginLabel);
- }
+ if (EmitEHLabels)
+ Chain =
+ lowerEndEH(Chain, cast<InvokeInst>(&Call), EHPadBB, Info.BeginLabel);
// Only Update Root if inline assembly has a memory effect.
- if (ResultValues.empty() || HasSideEffect || !OutChains.empty() || IsCallBr ||
- EmitEHLabels)
+ if (ResultValues.empty() || Info.HasSideEffect || !OutChains.empty() ||
+ IsCallBr || EmitEHLabels)
DAG.setRoot(Chain);
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
index bab0509dd138f..21aac333a73cd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
@@ -733,8 +733,11 @@ class SelectionDAGBuilder {
DIExpression *Expr, const DebugLoc &dl,
unsigned DbgSDNodeOrder);
+public:
SDValue lowerStartEH(SDValue Chain, const BasicBlock *EHPadBB,
MCSymbol *&BeginLabel);
+
+private:
SDValue lowerEndEH(SDValue Chain, const InvokeInst *II,
const BasicBlock *EHPadBB, MCSymbol *BeginLabel);
>From d2af47db255392ca7e2a274fd8da4632d36d61c0 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 30 Apr 2026 08:31:25 -0700
Subject: [PATCH 2/2] Use LLVM-specific ways to create a string stream. Use
'getRegAsmName' instead of just 'getName'. And correctly capitalize.
---
.../SelectionDAG/SelectionDAGBuilder.cpp | 39 ++++++++-----------
1 file changed, 16 insertions(+), 23 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 423000e665b67..214648b013fbd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -105,7 +105,6 @@
#include <cstddef>
#include <limits>
#include <optional>
-#include <sstream>
#include <tuple>
using namespace llvm;
@@ -10202,14 +10201,18 @@ struct ConstraintDecisionInfo {
SDValue Glue, Chain;
bool HasSideEffect = false;
MCSymbol *BeginLabel = nullptr;
- std::stringstream ErrorMsg;
+
+ SmallVector<char> Buffer;
+ raw_svector_ostream ErrorMsg;
+
+ ConstraintDecisionInfo() : ErrorMsg(Buffer) {}
};
} // end anonymous namespace
/// Construct operand info objects.
static bool
-ConstructOperandInfo(ConstraintDecisionInfo &Info,
+constructOperandInfo(ConstraintDecisionInfo &Info,
TargetLowering::AsmOperandInfoVector &TargetConstraints,
SelectionDAGBuilder &Builder, const TargetLowering &TLI,
ExtraFlags &ExtraInfo) {
@@ -10246,7 +10249,7 @@ ConstructOperandInfo(ConstraintDecisionInfo &Info,
/// Compute which constraint option to use for each operand.
static void
-ComputeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
+computeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
TargetLowering::AsmOperandInfoVector &TargetConstraints,
SelectionDAGBuilder &Builder, const TargetLowering &TLI,
const TargetMachine &TM, SelectionDAG &DAG) {
@@ -10326,7 +10329,7 @@ ComputeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
/// Prepare DAG-level operands. As part of this, assign virtual and physical
/// registers for inputs and output.
-static bool PrepareDAGLevelOperands(ConstraintDecisionInfo &Info,
+static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
const CallBase &Call,
SelectionDAGBuilder &Builder,
const TargetLowering &TLI,
@@ -10355,8 +10358,8 @@ static bool PrepareDAGLevelOperands(ConstraintDecisionInfo &Info,
for (Register Reg : OpInfo.AssignedRegs.Regs) {
if (Reg.isPhysical() && TRI.isInlineAsmReadOnlyReg(MF, Reg)) {
- Info.ErrorMsg << "write to reserved register '" << TRI.getName(Reg)
- << "'";
+ Info.ErrorMsg << "write to reserved register '"
+ << TRI.getRegAsmName(Reg) << "'";
return true;
}
}
@@ -10584,19 +10587,9 @@ static bool PrepareDAGLevelOperands(ConstraintDecisionInfo &Info,
return false;
}
-/// DetermineConstraints - ASM operands may have more than one constraint. We
-/// want to choose the "best" constraint for each operand to avoid horrible
-/// code generation---e.g., for "rm" we would like to use "r". This function
-/// tries different constraints in order from best to worst. If a given
-/// constraint isn't possible, e.g., because no registers are available, then
-/// the function returns 'true' and is rerun on the next constraint.
-///
-/// Each operand which has a suitable constraint is marked as "finalized". This
-/// helps reduce the number of times we need to run this function, keeping the
-/// complexity at O(n), where 'n' is the total number of constraints on inputs
-/// and outputs (i.e., for "rm", n == 2).
+/// DetermineConstraints - Find the constraints to use for inline asm operands.
static bool
-DetermineConstraints(ConstraintDecisionInfo &Info,
+determineConstraints(ConstraintDecisionInfo &Info,
TargetLowering::AsmOperandInfoVector &TargetConstraints,
const CallBase &Call, SelectionDAGBuilder &Builder,
const TargetLowering &TLI, const TargetMachine &TM,
@@ -10606,7 +10599,7 @@ DetermineConstraints(ConstraintDecisionInfo &Info,
// First pass: Construct operand info objects.
Info.HasSideEffect = IA->hasSideEffects();
- if (ConstructOperandInfo(Info, TargetConstraints, Builder, TLI, ExtraInfo))
+ if (constructOperandInfo(Info, TargetConstraints, Builder, TLI, ExtraInfo))
return true;
// We won't need to flush pending loads if this asm doesn't touch
@@ -10625,7 +10618,7 @@ DetermineConstraints(ConstraintDecisionInfo &Info,
Info.Chain = Builder.lowerStartEH(Info.Chain, EHPadBB, Info.BeginLabel);
// Second pass: Compute which constraint option to use.
- ComputeConstraintToUse(Info, Call, TargetConstraints, Builder, TLI, TM, DAG);
+ computeConstraintToUse(Info, Call, TargetConstraints, Builder, TLI, TM, DAG);
// AsmNodeOperands - The operands for the ISD::INLINEASM node.
Info.AsmNodeOperands.push_back(SDValue()); // reserve space for input chain
@@ -10645,7 +10638,7 @@ DetermineConstraints(ConstraintDecisionInfo &Info,
TLI.getPointerTy(DAG.getDataLayout())));
// Third pass: Prepare DAG-level operands
- return PrepareDAGLevelOperands(Info, Call, Builder, TLI, DAG);
+ return prepareDAGLevelOperands(Info, Call, Builder, TLI, DAG);
}
/// visitInlineAsm - Handle a call to an InlineAsm object.
@@ -10660,7 +10653,7 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
assert(EHPadBB && "InvokeInst must have an EHPadBB");
ConstraintDecisionInfo Info;
- if (DetermineConstraints(Info, TargetConstraints, Call, *this, TLI, TM, DAG,
+ if (determineConstraints(Info, TargetConstraints, Call, *this, TLI, TM, DAG,
EHPadBB))
return emitInlineAsmError(Call, Info.ErrorMsg.str());
More information about the llvm-commits
mailing list