[clang] [llvm] [CodeGen] Add support for multiple constraints (PR #195592)

via cfe-commits cfe-commits at lists.llvm.org
Sun May 3 23:34:52 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Bill Wendling (bwendling)

<details>
<summary>Changes</summary>

Historically, LLVM prefered to use the more conservative inline assembly constraint when presented with multiple constraints, e.g., "rm". This leads to horrible code generation. For instance:

    void write(unsigned long flags) {
        asm("push %0 ; popf" : : "rm" (flags));
    }

generates:

    movq    %rdi, -8(%rsp)
    #APP
    pushq   -8(%rsp)
    popfq
    #NO_APP

because the "m" option is chosen by default, so that the back-ends have the best chance to generate semantically correct code if register pressure becomes too much.

The register allocators have grown up and are now able to fold registers in such instances. There's no longer the need to restrict us to the most conservative option, except for the fast register allocator. To that end, we restrict the front-end from preferring the conservative constraint (unless compiling at '-O0' where we don't care about code generation quality).

However, simply preferring the least restrictive option doesn't work in all situations. We could have a situation where the SelectionDAG isn't able to satisfy the "r" constraint and so we would like it to consider other constraint options if they exist.

In order to do that, we run each constraint through the logic that determines whether to use a constraint from least restrictive to most restrictive, stopping either we encounter a constraint that works or we run out of constraints to consider.

In the most extreme case, each operand would have all of their constraint options tried, making the complexity O(M * N), where M is the number of operands and N is the max number of constraints per operand. This is worse than the original O(M) complexity. However, we emphasize that O(M * N) is the *worst* case scenario.

With this change, the code above generates:

    #APP
    pushq  %rdi
    popfq
    #NO_APP

which is far more palatable.

Fixes: 20571

---

Patch is 119.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/195592.diff


18 Files Affected:

- (modified) clang/lib/CodeGen/CGStmt.cpp (+13-3) 
- (added) clang/test/CodeGen/asm-reg-mem-constraints.c (+74) 
- (modified) clang/test/CodeGen/asm.c (-25) 
- (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+16) 
- (modified) llvm/include/llvm/IR/InlineAsm.h (+7) 
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+115-67) 
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h (+4-2) 
- (modified) llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp (+59-31) 
- (added) llvm/test/CodeGen/X86/asm-constraints-rm-O0.ll (+855) 
- (added) llvm/test/CodeGen/X86/asm-constraints-rm.ll (+430) 
- (modified) llvm/test/CodeGen/X86/asm-modifier.ll (+2-2) 
- (added) llvm/test/CodeGen/X86/inline-asm-attributes.ll (+28) 
- (added) llvm/test/CodeGen/X86/inline-asm-bundles.ll (+14) 
- (added) llvm/test/CodeGen/X86/inline-asm-callbase.ll (+75) 
- (added) llvm/test/CodeGen/X86/inline-asm-memory.ll (+74) 
- (added) llvm/test/CodeGen/X86/inline-asm-rm-no-opt.ll (+55) 
- (added) llvm/test/CodeGen/X86/inline-asm-rm-opt.ll (+45) 
- (modified) llvm/test/CodeGen/X86/inlineasm-sched-bug.ll (+1-4) 


``````````diff
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 7b6035a6968b1..0c8197daf1407 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -2883,13 +2883,23 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
     if (!Constraints.empty())
       Constraints += ',';
 
-    // If this is a register output, then make the inline asm return it
-    // by-value.  If this is a memory result, return the value by-reference.
+    // - If this is a register output, then make the inline asm return it
+    //   by-value.
+    // - If this is a memory output, return the value by reference.
+    // - If this is a register and memory output, treat it like a register
+    //   output at -O[1-3]. This allows the optimizing register allocators to
+    //   choose a register, while the fast register allocator defaults to
+    //   memory.
     QualType QTy = OutExpr->getType();
     const bool IsScalarOrAggregate = hasScalarEvaluationKind(QTy) ||
                                      hasAggregateEvaluationKind(QTy);
-    if (!Info.allowsMemory() && IsScalarOrAggregate) {
+    const bool RegisterMemoryConstraints =
+        CGM.getCodeGenOpts().OptimizationLevel != 0 &&
+        llvm::is_contained(OutputConstraint, 'r') &&
+        llvm::is_contained(OutputConstraint, 'm');
 
+    if (IsScalarOrAggregate &&
+        (!Info.allowsMemory() || RegisterMemoryConstraints)) {
       Constraints += "=" + OutputConstraint;
       ResultRegQualTys.push_back(QTy);
       ResultRegDests.push_back(Dest);
diff --git a/clang/test/CodeGen/asm-reg-mem-constraints.c b/clang/test/CodeGen/asm-reg-mem-constraints.c
new file mode 100644
index 0000000000000..9f35dee93a72e
--- /dev/null
+++ b/clang/test/CodeGen/asm-reg-mem-constraints.c
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm %s -o - | FileCheck --check-prefixes=O0 %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O2 %s -o - | FileCheck --check-prefixes=O2 %s
+
+void test_1(unsigned long flags) {
+  // O0-LABEL: @test_1
+  // O0:         call void asm sideeffect "", "rm,~{dirflag},~{fpsr},~{flags}"(i32 %0)
+  //
+  // O2-LABEL: @test_1
+  // O2:         call void asm sideeffect "", "rm,~{dirflag},~{fpsr},~{flags}"(i32 %flags)
+  asm ("" : : "rm" (flags));
+}
+
+unsigned long test_2(void) {
+  // O0-LABEL: @test_2
+  // O0:         call void asm "", "=*rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %out)
+  //
+  // O2-LABEL: @test_2
+  // O2:         %0 = tail call i32 asm "", "=rm,~{dirflag},~{fpsr},~{flags}"()
+  unsigned long out;
+  asm ("" : "=rm" (out));
+  return out;
+}
+
+void test_3(unsigned long flags) {
+  // O0-LABEL: @test_3
+  // O0:         call void asm sideeffect "", "imr,~{dirflag},~{fpsr},~{flags}"(i32 %0)
+  //
+  // O2-LABEL: @test_3
+  // O2:         call void asm sideeffect "", "imr,~{dirflag},~{fpsr},~{flags}"(i32 %flags)
+  asm ("" : : "g" (flags));
+}
+
+unsigned long test_4(void) {
+  // O0-LABEL: @test_4
+  // O0:         call void asm "", "=*imr,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %out)
+  //
+  // O2-LABEL: @test_4
+  // O2:         %0 = tail call i32 asm "", "=imr,~{dirflag},~{fpsr},~{flags}"()
+  unsigned long out;
+  asm ("" : "=g" (out));
+  return out;
+}
+
+void test_5(int len) {
+  // O0-LABEL: @test_5
+  // O0:         call void asm sideeffect "", "=*&rm,0,~{dirflag},~{fpsr},~{flags}"
+  //
+  // O2-LABEL: @test_5
+  // O2:         %0 = tail call i32 asm sideeffect "", "=&rm,0,~{dirflag},~{fpsr},~{flags}"(i32 %len)
+  __asm__ volatile ("" : "+&&rm" (len));
+}
+
+void test_6(int len) {
+  // O0-LABEL: @test_6
+  // O0:         call void asm sideeffect "", "=*%rm,=*rm,0,1,~{dirflag},~{fpsr},~{flags}"
+  //
+  // O2-LABEL: @test_6
+  // O2:         %0 = tail call { i32, i32 } asm sideeffect "", "=%rm,=rm,0,1,~{dirflag},~{fpsr},~{flags}"(i32 %len, i32 %len)
+  __asm__ volatile ("" : "+%%rm" (len), "+rm" (len));
+}
+
+// PR3908
+void test_7(int r) {
+  // O0-LABEL: @test_7
+  // O0:         call i32 asm "# PR3908 $1 $3 $2 $0", "=r,mx,mr,x,0,~{dirflag},~{fpsr},~{flags}"
+  // O0-SAME:      (i32 0, i32 0, double 0.000000e+00, i32 %{{.*}})
+  //
+  // O2-LABEL: @test_7
+  // O2:         %0 = tail call i32 asm "# PR3908 $1 $3 $2 $0", "=r,mx,mr,x,0,~{dirflag},~{fpsr},~{flags}"
+  // O2-SAME:      (i32 0, i32 0, double 0.000000e+00, i32 %{{.*}})
+  __asm__ ("# PR3908 %[lf] %[xx] %[li] %[r]"
+           : [r] "+r" (r)
+           : [lf] "mx" (0), [li] "mr" (0), [xx] "x" ((double)(0)));
+}
diff --git a/clang/test/CodeGen/asm.c b/clang/test/CodeGen/asm.c
index d7465b22fbbf6..4c1cd2d493136 100644
--- a/clang/test/CodeGen/asm.c
+++ b/clang/test/CodeGen/asm.c
@@ -306,28 +306,3 @@ void t31(void) {
   // CHECK:         call void asm sideeffect "T31 CC NAMED MODIFIER: ${0:c}", "i,~{dirflag},~{fpsr},~{flags}"
   __asm__ volatile ("T31 CC NAMED MODIFIER: %cc[input]" : : [input] "i"  (4));
 }
-
-// TODO: Move the "rm" tests into a new testcase file once work to better
-// support "rm" constraints is done.
-
-void t32(int len) {
-  // CHECK-LABEL: @t32
-  // CHECK:         call void asm sideeffect "", "=*&rm,0,~{dirflag},~{fpsr},~{flags}"
-  __asm__ volatile ("" : "+&&rm" (len));
-}
-
-void t33(int len) {
-  // CHECK-LABEL: @t33
-  // CHECK:         call void asm sideeffect "", "=*%rm,=*rm,0,1,~{dirflag},~{fpsr},~{flags}"
-  __asm__ volatile ("" : "+%%rm" (len), "+rm" (len));
-}
-
-// PR3908
-void t34(int r) {
-  // CHECK-LABEL: @t34
-  // CHECK:         call i32 asm "PR3908 $1 $3 $2 $0", "=r,mx,mr,x,0,~{dirflag},~{fpsr},~{flags}"
-  // CHECK-SAME:      (i32 0, i32 0, double 0.000000e+00, i32 %{{.*}})
-  __asm__ ("PR3908 %[lf] %[xx] %[li] %[r]"
-           : [r] "+r" (r)
-           : [lf] "mx" (0), [li] "mr" (0), [xx] "x" ((double)(0)));
-}
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 9b0bfa111f2d5..4955fb26bc3f9 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -5302,6 +5302,17 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
     /// The ValueType for the operand value.
     MVT ConstraintVT = MVT::Other;
 
+    /// The register may be folded. This is used if the constraint has register
+    /// and memory constraints where we prefer using a register, but can fall
+    /// back to a memory slot under register pressure.
+    bool MayFoldRegister = false;
+
+    /// The index to the last matched constraint code.
+    long ConstraintIndex = -1;
+
+    /// The constraint was successfully assigned to the operand.
+    bool Finalized = false;
+
     /// Copy constructor for copying from a ConstraintInfo.
     AsmOperandInfo(InlineAsm::ConstraintInfo Info)
         : InlineAsm::ConstraintInfo(std::move(Info)) {}
@@ -5313,6 +5324,11 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
     /// If this is an input matching constraint, this method returns the output
     /// operand it matches.
     LLVM_ABI unsigned getMatchedOperand() const;
+
+    /// Return true if there are no more constraints to try.
+    bool atFinalConstraint() const {
+      return ConstraintIndex >= static_cast<long>(Codes.size() - 1);
+    }
   };
 
   using AsmOperandInfoVector = std::vector<AsmOperandInfo>;
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 564f2e7df2dd3..bab538e852467 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -181,6 +181,13 @@ class InlineAsm final : public Value {
     bool hasArg() const {
       return Type == isInput || (Type == isOutput && isIndirect);
     }
+
+    /// hasRegMemConstraints - Returns true if the constraint codes have
+    /// register and memory constraints. This is useful to let the register
+    /// allocator that it can use memory under register pressure.
+    bool hasRegMemConstraints() const {
+      return is_contained(Codes, "r") && is_contained(Codes, "m");
+    }
   };
 
   /// ParseConstraints - Split up the constraint string into the specific
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 458fd21c5ab6d..62b1ecb3b92f1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -1032,7 +1032,8 @@ void RegsForValue::getCopyToRegs(SDValue Val, SelectionDAG &DAG,
 }
 
 void RegsForValue::AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
-                                        unsigned MatchingIdx, const SDLoc &dl,
+                                        unsigned MatchingIdx,
+                                        bool MayFoldRegister, const SDLoc &dl,
                                         SelectionDAG &DAG,
                                         std::vector<SDValue> &Ops) const {
   const TargetLowering &TLI = DAG.getTargetLoweringInfo();
@@ -1049,6 +1050,7 @@ void RegsForValue::AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
     const MachineRegisterInfo &MRI = DAG.getMachineFunction().getRegInfo();
     const TargetRegisterClass *RC = MRI.getRegClass(Regs.front());
     Flag.setRegClass(RC->getID());
+    Flag.setRegMayBeFolded(MayFoldRegister);
   }
 
   SDValue Res = DAG.getTargetConstant(Flag, dl, MVT::i32);
@@ -10195,6 +10197,8 @@ static bool isFunction(SDValue Op) {
 
 namespace {
 
+/// ConstraintDecisionInfo - A struct that holds information while determining
+/// which constraint to use for an inline asm operand.
 struct ConstraintDecisionInfo {
   SmallVector<SDISelAsmOperandInfo, 16> ConstraintOperands;
   std::vector<SDValue> AsmNodeOperands;
@@ -10206,6 +10210,14 @@ struct ConstraintDecisionInfo {
   raw_svector_ostream ErrorMsg;
 
   ConstraintDecisionInfo() : ErrorMsg(Buffer) {}
+
+  void reset() {
+    ConstraintOperands.clear();
+    AsmNodeOperands.clear();
+    Glue = SDValue();
+    Chain = SDValue();
+    BeginLabel = nullptr;
+  }
 };
 
 } // end anonymous namespace
@@ -10236,8 +10248,9 @@ constructOperandInfo(ConstraintDecisionInfo &Info,
         !isa<ConstantSDNode>(OpInfo.CallOperand)) {
       // We've delayed emitting a diagnostic like the "n" constraint because
       // inlining could cause an integer showing up.
-      Info.ErrorMsg << "constraint '" << T.ConstraintCode
-                    << "' expects an integer constant expression";
+      if (OpInfo.atFinalConstraint())
+        Info.ErrorMsg << "constraint '" << T.ConstraintCode
+                      << "' expects an integer constant expression";
       return true;
     }
 
@@ -10310,9 +10323,9 @@ computeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
     // need to provide an address for the memory input.
     if (OpInfo.ConstraintType == TargetLowering::C_Memory &&
         !OpInfo.isIndirect) {
-      assert((OpInfo.isMultipleAlternative ||
-              (OpInfo.Type == InlineAsm::isInput)) &&
-             "Can only indirectify direct input operands!");
+      assert(
+          (OpInfo.isMultipleAlternative || OpInfo.Type == InlineAsm::isInput) &&
+          "Can only indirectify direct input operands!");
 
       // Memory operands really want the address of the value.
       Info.Chain = getAddressForMemoryInput(Info.Chain, Builder.getCurSDLoc(),
@@ -10334,6 +10347,15 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
                                     SelectionDAGBuilder &Builder,
                                     const TargetLowering &TLI,
                                     SelectionDAG &DAG) {
+  // Registers before tied operands can't be folded, because the tied operand
+  // will move, which the back-end isn't able to properly account for.
+  bool Clear = false;
+  for (SDISelAsmOperandInfo &OpInfo : llvm::reverse(Info.ConstraintOperands)) {
+    Clear |= OpInfo.isMatchingInputConstraint();
+    if (Clear)
+      OpInfo.MayFoldRegister = false;
+  }
+
   SDLoc DL = Builder.getCurSDLoc();
   for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
     // Assign Registers.
@@ -10343,12 +10365,14 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
             : OpInfo;
     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);
-      Info.ErrorMsg << "register '" << RegName << "' allocated for constraint '"
-                    << OpInfo.ConstraintCode
-                    << "' does not match required type";
+      if (OpInfo.atFinalConstraint()) {
+        const MachineFunction &MF = DAG.getMachineFunction();
+        const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+        const char *RegName = TRI.getName(*RegError);
+        Info.ErrorMsg << "register '" << RegName
+                      << "' allocated for constraint '" << OpInfo.ConstraintCode
+                      << "' does not match required type";
+      }
       return true;
     }
 
@@ -10358,8 +10382,10 @@ 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.getRegAsmName(Reg) << "'";
+          if (OpInfo.atFinalConstraint()) {
+            StringRef RegName = TRI.getRegAsmName(Reg);
+            Info.ErrorMsg << "write to reserved register '" << RegName << "'";
+          }
           return true;
         }
       }
@@ -10390,8 +10416,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
         // C_RegisterClass, and a target-defined fashion for
         // C_Immediate/C_Other). Find a register that we can use.
         if (OpInfo.AssignedRegs.Regs.empty()) {
-          Info.ErrorMsg << "could not allocate output register for "
-                        << "constraint '" << OpInfo.ConstraintCode << "'";
+          if (OpInfo.atFinalConstraint())
+            Info.ErrorMsg << "could not allocate output register for "
+                          << "constraint '" << OpInfo.ConstraintCode << "'";
           return true;
         }
 
@@ -10403,7 +10430,7 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
         OpInfo.AssignedRegs.AddInlineAsmOperands(
             OpInfo.isEarlyClobber ? InlineAsm::Kind::RegDefEarlyClobber
                                   : InlineAsm::Kind::RegDef,
-            false, 0, DL, DAG, Info.AsmNodeOperands);
+            false, 0, OpInfo.MayFoldRegister, DL, DAG, Info.AsmNodeOperands);
       }
       break;
 
@@ -10420,8 +10447,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
         if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {
           if (OpInfo.isIndirect) {
             // This happens on gcc/testsuite/gcc.dg/pr8788-1.c
-            Info.ErrorMsg << "inline asm not supported yet: cannot handle "
-                          << "tied indirect register inputs";
+            if (OpInfo.atFinalConstraint())
+              Info.ErrorMsg << "inline asm not supported yet: cannot handle "
+                            << "tied indirect register inputs";
             return true;
           }
 
@@ -10444,9 +10472,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
           // Use the produced MatchedRegs object to
           MatchedRegs.getCopyToRegs(InOperandVal, DAG, DL, Info.Chain,
                                     &Info.Glue, &Call);
-          MatchedRegs.AddInlineAsmOperands(InlineAsm::Kind::RegUse, true,
-                                           OpInfo.getMatchedOperand(), DL, DAG,
-                                           Info.AsmNodeOperands);
+          MatchedRegs.AddInlineAsmOperands(
+              InlineAsm::Kind::RegUse, true, OpInfo.getMatchedOperand(),
+              OpInfo.MayFoldRegister, DL, DAG, Info.AsmNodeOperands);
           break;
         }
 
@@ -10569,8 +10597,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
 
       OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, DL, Info.Chain,
                                         &Info.Glue, &Call);
-      OpInfo.AssignedRegs.AddInlineAsmOperands(
-          InlineAsm::Kind::RegUse, false, 0, DL, DAG, Info.AsmNodeOperands);
+      OpInfo.AssignedRegs.AddInlineAsmOperands(InlineAsm::Kind::RegUse, false,
+                                               0, OpInfo.MayFoldRegister, DL,
+                                               DAG, Info.AsmNodeOperands);
       break;
     }
 
@@ -10578,16 +10607,29 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
       // 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, DL, DAG, Info.AsmNodeOperands);
+        OpInfo.AssignedRegs.AddInlineAsmOperands(InlineAsm::Kind::Clobber,
+                                                 false, 0, false, DL, DAG,
+                                                 Info.AsmNodeOperands);
       break;
     }
+
+    OpInfo.Finalized = true;
   }
 
   return false;
 }
 
-/// DetermineConstraints - Find the constraints to use for inline asm operands.
+/// 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,
@@ -10652,9 +10694,12 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
          "InvokeInst must have an EHPadBB");
 
   ConstraintDecisionInfo Info;
-  if (determineConstraints(Info, TargetConstraints, Call, *this, TLI, TM, DAG,
-                           EHPadBB))
-    return emitInlineAsmError(Call, Info.ErrorMsg.str());
+  while (determineConstraints(Info, TargetConstraints, Call, *this, TLI, TM,
+                              DAG, EHPadBB)) {
+    if (Info.ErrorMsg.buffer().size() != 0)
+      return emitInlineAsmError(Call, Info.ErrorMsg.str());
+    Info.reset();
+  }
 
   SDValue Glue = Info.Glue;
   SDValue Chain = Info.Chain;
@@ -10717,46 +10762,49 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
 
   // Deal with output operands.
   for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
-    if (OpInfo.Type == InlineAsm::isOutput) {
-      SDValue Val;
-      // Skip trivial output operands.
-      if (OpInfo.AssignedRegs.Regs.empty())
-        continue;
+    if (OpInfo.Type != InlineAsm::isOutput)
+      continue;
 
-      switch (OpInfo.ConstraintType) {
-      case TargetLowering::C_Register:
-      case TargetLowering::C_RegisterClass:
-        Val = OpInfo.AssignedRegs.getCopyFromRegs(DAG, FuncInfo, getCurSDLoc(),
-                                                  Chain, &Glue, &Call);
-        break;
-      case TargetLowering::C_Immediate:
-      case TargetLowering::C_Other:
-        Val = TLI.LowerAsmOutputForConstraint(Chain, Glue, getCurSDLoc(),
-                                              OpInfo, DAG);
-        break;
-      case TargetLowering::C_Memory:
-        break; // Already handled.
-      case TargetLowering::C_Address:
-        break; // Silence warning.
-      case TargetLowering::C_Unknown:
-        assert(false && "Unexpected unknown constraint");
-      }
+    SDValue Val;
 
-      // Indirect output manifest as stores. Record output chains.
-      if (OpInfo.isIndirect) {
-      ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/195592


More information about the cfe-commits mailing list