[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