[llvm] [GlobalISel] Diagnose inline assembly constraint lowering errors (PR #135782)

Pierre van Houtryve via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 28 06:07:28 PDT 2025


https://github.com/Pierre-vh updated https://github.com/llvm/llvm-project/pull/135782

>From 5cd97ac8a8f0e77cf540df0f99d1ad34f4f04b20 Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Tue, 15 Apr 2025 14:10:12 +0200
Subject: [PATCH 1/3] [GlobalISel] Diagnose inline assembly constraint lowering
 errors

Instead of printing something to dbgs (which is not visible to all users),
emit a diagnostic like the DAG does. We still crash later because we fail to
select the inline assembly, but at least now users will know why it's crashing.

In a future patch we could also recover from the error like the DAG does, so the
lowering can keep going until it either crashes or gives a different error later.
---
 .../CodeGen/GlobalISel/InlineAsmLowering.cpp  | 75 +++++++++++--------
 1 file changed, 42 insertions(+), 33 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 81f25b21a0409..97d665c8fbf94 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -16,6 +16,7 @@
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Module.h"
 
 #define DEBUG_TYPE "inline-asm-lowering"
@@ -212,6 +213,17 @@ static bool buildAnyextOrCopy(Register Dst, Register Src,
   return true;
 }
 
+static bool reportInlineAsmConstraintError(const CallBase &Call,
+                                           const GISelAsmOperandInfo &Info,
+                                           Twine Msg) {
+  LLVMContext &Ctx = Call.getContext();
+  Ctx.diagnose(DiagnosticInfoInlineAsm(
+      Call, "invalid constraint '" + Info.ConstraintCode + "': " + Msg));
+  // TODO(?): Allow selection to continue by recovering/leaving the gMIR in a
+  // good state, like the DAG does.
+  return false;
+}
+
 bool InlineAsmLowering::lowerInlineAsm(
     MachineIRBuilder &MIRBuilder, const CallBase &Call,
     std::function<ArrayRef<Register>(const Value &Val)> GetOrCreateVRegs)
@@ -243,8 +255,8 @@ bool InlineAsmLowering::lowerInlineAsm(
       OpInfo.CallOperandVal = const_cast<Value *>(Call.getArgOperand(ArgNo));
 
       if (isa<BasicBlock>(OpInfo.CallOperandVal)) {
-        LLVM_DEBUG(dbgs() << "Basic block input operands not supported yet\n");
-        return false;
+        return reportInlineAsmConstraintError(
+            Call, OpInfo, "basic block input operands not supported yet");
       }
 
       Type *OpTy = OpInfo.CallOperandVal->getType();
@@ -258,9 +270,8 @@ bool InlineAsmLowering::lowerInlineAsm(
 
       // FIXME: Support aggregate input operands
       if (!OpTy->isSingleValueType()) {
-        LLVM_DEBUG(
-            dbgs() << "Aggregate input operands are not supported yet\n");
-        return false;
+        return reportInlineAsmConstraintError(
+            Call, OpInfo, "aggregate input operands not supported yet");
       }
 
       OpInfo.ConstraintVT =
@@ -344,9 +355,8 @@ bool InlineAsmLowering::lowerInlineAsm(
 
         // Find a register that we can use.
         if (OpInfo.Regs.empty()) {
-          LLVM_DEBUG(dbgs()
-                     << "Couldn't allocate output register for constraint\n");
-          return false;
+          return reportInlineAsmConstraintError(
+              Call, OpInfo, "couldn't allocate output register for constraint");
         }
 
         // Add information to the INLINEASM instruction to know that this
@@ -389,13 +399,14 @@ bool InlineAsmLowering::lowerInlineAsm(
 
         const InlineAsm::Flag MatchedOperandFlag(Inst->getOperand(InstFlagIdx).getImm());
         if (MatchedOperandFlag.isMemKind()) {
-          LLVM_DEBUG(dbgs() << "Matching input constraint to mem operand not "
-                               "supported. This should be target specific.\n");
-          return false;
+          return reportInlineAsmConstraintError(
+              Call, OpInfo,
+              "matching input constraint to mem operand not supported; this "
+              "should be target specific");
         }
         if (!MatchedOperandFlag.isRegDefKind() && !MatchedOperandFlag.isRegDefEarlyClobberKind()) {
-          LLVM_DEBUG(dbgs() << "Unknown matching constraint\n");
-          return false;
+          return reportInlineAsmConstraintError(Call, OpInfo,
+                                                "unknown matching constraint");
         }
 
         // We want to tie input to register in next operand.
@@ -425,9 +436,10 @@ bool InlineAsmLowering::lowerInlineAsm(
 
       if (OpInfo.ConstraintType == TargetLowering::C_Other &&
           OpInfo.isIndirect) {
-        LLVM_DEBUG(dbgs() << "Indirect input operands with unknown constraint "
-                             "not supported yet\n");
-        return false;
+        return reportInlineAsmConstraintError(
+            Call, OpInfo,
+            "indirect input operands with unknown constraint not supported "
+            "yet");
       }
 
       if (OpInfo.ConstraintType == TargetLowering::C_Immediate ||
@@ -437,9 +449,8 @@ bool InlineAsmLowering::lowerInlineAsm(
         if (!lowerAsmOperandForConstraint(OpInfo.CallOperandVal,
                                           OpInfo.ConstraintCode, Ops,
                                           MIRBuilder)) {
-          LLVM_DEBUG(dbgs() << "Don't support constraint: "
-                            << OpInfo.ConstraintCode << " yet\n");
-          return false;
+          return reportInlineAsmConstraintError(Call, OpInfo,
+                                                "unsupported constraint");
         }
 
         assert(Ops.size() > 0 &&
@@ -456,9 +467,9 @@ bool InlineAsmLowering::lowerInlineAsm(
       if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
 
         if (!OpInfo.isIndirect) {
-          LLVM_DEBUG(dbgs()
-                     << "Cannot indirectify memory input operands yet\n");
-          return false;
+          return reportInlineAsmConstraintError(
+              Call, OpInfo,
+              "indirect memory input operands are not supported yet");
         }
 
         assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!");
@@ -482,18 +493,15 @@ bool InlineAsmLowering::lowerInlineAsm(
              "Unknown constraint type!");
 
       if (OpInfo.isIndirect) {
-        LLVM_DEBUG(dbgs() << "Can't handle indirect register inputs yet "
-                             "for constraint '"
-                          << OpInfo.ConstraintCode << "'\n");
-        return false;
+        return reportInlineAsmConstraintError(
+            Call, OpInfo, "indirect register inputs are not supported yet");
       }
 
       // Copy the input into the appropriate registers.
       if (OpInfo.Regs.empty()) {
-        LLVM_DEBUG(
-            dbgs()
-            << "Couldn't allocate input register for register constraint\n");
-        return false;
+        return reportInlineAsmConstraintError(
+            Call, OpInfo,
+            "couldn't allocate input register for register constraint");
       }
 
       unsigned NumRegs = OpInfo.Regs.size();
@@ -503,9 +511,10 @@ bool InlineAsmLowering::lowerInlineAsm(
              "source registers");
 
       if (NumRegs > 1) {
-        LLVM_DEBUG(dbgs() << "Input operands with multiple input registers are "
-                             "not supported yet\n");
-        return false;
+        return reportInlineAsmConstraintError(
+            Call, OpInfo,
+            "input operands with multiple input registers are not supported "
+            "yet");
       }
 
       InlineAsm::Flag Flag(InlineAsm::Kind::RegUse, NumRegs);

>From 2a3f542b81147d72cd121e6e5c718df52d8f753e Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Fri, 25 Apr 2025 10:32:27 +0200
Subject: [PATCH 2/3] add some tests

---
 .../CodeGen/GlobalISel/InlineAsmLowering.cpp  | 66 +++++++++----------
 .../GlobalISel/inline-asm-lowering-errors.ll  | 13 ++++
 2 files changed, 44 insertions(+), 35 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll

diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 97d665c8fbf94..d4a38f180f011 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -213,17 +213,6 @@ static bool buildAnyextOrCopy(Register Dst, Register Src,
   return true;
 }
 
-static bool reportInlineAsmConstraintError(const CallBase &Call,
-                                           const GISelAsmOperandInfo &Info,
-                                           Twine Msg) {
-  LLVMContext &Ctx = Call.getContext();
-  Ctx.diagnose(DiagnosticInfoInlineAsm(
-      Call, "invalid constraint '" + Info.ConstraintCode + "': " + Msg));
-  // TODO(?): Allow selection to continue by recovering/leaving the gMIR in a
-  // good state, like the DAG does.
-  return false;
-}
-
 bool InlineAsmLowering::lowerInlineAsm(
     MachineIRBuilder &MIRBuilder, const CallBase &Call,
     std::function<ArrayRef<Register>(const Value &Val)> GetOrCreateVRegs)
@@ -243,6 +232,16 @@ bool InlineAsmLowering::lowerInlineAsm(
   TargetLowering::AsmOperandInfoVector TargetConstraints =
       TLI->ParseConstraints(DL, TRI, Call);
 
+  const auto ConstraintError = [&](const GISelAsmOperandInfo &Info, Twine Msg) {
+    LLVMContext &Ctx = MIRBuilder.getContext();
+    Ctx.diagnose(DiagnosticInfoInlineAsm(
+        Call, "invalid constraint '" + Info.ConstraintCode + "' in '" +
+                  MF.getName() + "': " + Msg));
+    // TODO: Recover if fallback isn't used. Otherwise let the fallback to DAG
+    // kick in.
+    return false;
+  };
+
   ExtraFlags ExtraInfo(Call);
   unsigned ArgNo = 0; // ArgNo - The argument of the CallInst.
   unsigned ResNo = 0; // ResNo - The result number of the next output.
@@ -255,8 +254,8 @@ bool InlineAsmLowering::lowerInlineAsm(
       OpInfo.CallOperandVal = const_cast<Value *>(Call.getArgOperand(ArgNo));
 
       if (isa<BasicBlock>(OpInfo.CallOperandVal)) {
-        return reportInlineAsmConstraintError(
-            Call, OpInfo, "basic block input operands not supported yet");
+        return ConstraintError(OpInfo,
+                               "basic block input operands not supported yet");
       }
 
       Type *OpTy = OpInfo.CallOperandVal->getType();
@@ -270,8 +269,8 @@ bool InlineAsmLowering::lowerInlineAsm(
 
       // FIXME: Support aggregate input operands
       if (!OpTy->isSingleValueType()) {
-        return reportInlineAsmConstraintError(
-            Call, OpInfo, "aggregate input operands not supported yet");
+        return ConstraintError(OpInfo,
+                               "aggregate input operands not supported yet");
       }
 
       OpInfo.ConstraintVT =
@@ -355,8 +354,8 @@ bool InlineAsmLowering::lowerInlineAsm(
 
         // Find a register that we can use.
         if (OpInfo.Regs.empty()) {
-          return reportInlineAsmConstraintError(
-              Call, OpInfo, "couldn't allocate output register for constraint");
+          return ConstraintError(
+              OpInfo, "could not allocate output register for constraint");
         }
 
         // Add information to the INLINEASM instruction to know that this
@@ -399,14 +398,13 @@ bool InlineAsmLowering::lowerInlineAsm(
 
         const InlineAsm::Flag MatchedOperandFlag(Inst->getOperand(InstFlagIdx).getImm());
         if (MatchedOperandFlag.isMemKind()) {
-          return reportInlineAsmConstraintError(
-              Call, OpInfo,
+          return ConstraintError(
+              OpInfo,
               "matching input constraint to mem operand not supported; this "
               "should be target specific");
         }
         if (!MatchedOperandFlag.isRegDefKind() && !MatchedOperandFlag.isRegDefEarlyClobberKind()) {
-          return reportInlineAsmConstraintError(Call, OpInfo,
-                                                "unknown matching constraint");
+          return ConstraintError(OpInfo, "unknown matching constraint");
         }
 
         // We want to tie input to register in next operand.
@@ -436,8 +434,8 @@ bool InlineAsmLowering::lowerInlineAsm(
 
       if (OpInfo.ConstraintType == TargetLowering::C_Other &&
           OpInfo.isIndirect) {
-        return reportInlineAsmConstraintError(
-            Call, OpInfo,
+        return ConstraintError(
+            OpInfo,
             "indirect input operands with unknown constraint not supported "
             "yet");
       }
@@ -449,8 +447,7 @@ bool InlineAsmLowering::lowerInlineAsm(
         if (!lowerAsmOperandForConstraint(OpInfo.CallOperandVal,
                                           OpInfo.ConstraintCode, Ops,
                                           MIRBuilder)) {
-          return reportInlineAsmConstraintError(Call, OpInfo,
-                                                "unsupported constraint");
+          return ConstraintError(OpInfo, "unsupported constraint");
         }
 
         assert(Ops.size() > 0 &&
@@ -467,9 +464,8 @@ bool InlineAsmLowering::lowerInlineAsm(
       if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
 
         if (!OpInfo.isIndirect) {
-          return reportInlineAsmConstraintError(
-              Call, OpInfo,
-              "indirect memory input operands are not supported yet");
+          return ConstraintError(
+              OpInfo, "indirect memory input operands are not supported yet");
         }
 
         assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!");
@@ -493,15 +489,15 @@ bool InlineAsmLowering::lowerInlineAsm(
              "Unknown constraint type!");
 
       if (OpInfo.isIndirect) {
-        return reportInlineAsmConstraintError(
-            Call, OpInfo, "indirect register inputs are not supported yet");
+        return ConstraintError(
+            OpInfo, "indirect register inputs are not supported yet");
       }
 
       // Copy the input into the appropriate registers.
       if (OpInfo.Regs.empty()) {
-        return reportInlineAsmConstraintError(
-            Call, OpInfo,
-            "couldn't allocate input register for register constraint");
+        return ConstraintError(
+            OpInfo,
+            "could not allocate input register for register constraint");
       }
 
       unsigned NumRegs = OpInfo.Regs.size();
@@ -511,8 +507,8 @@ bool InlineAsmLowering::lowerInlineAsm(
              "source registers");
 
       if (NumRegs > 1) {
-        return reportInlineAsmConstraintError(
-            Call, OpInfo,
+        return ConstraintError(
+            OpInfo,
             "input operands with multiple input registers are not supported "
             "yet");
       }
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll
new file mode 100644
index 0000000000000..95f9b9ba5505a
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll
@@ -0,0 +1,13 @@
+; RUN: llc -S -mtriple=amdgcn -mcpu=fiji -O0 -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2> %t.err
+
+; CHECK: error: invalid constraint '' in 'aggregates': aggregate input operands not supported yet
+define amdgpu_kernel void @aggregates([4 x i8] %val) {
+  tail call void asm sideeffect "s_nop", "r"([4 x i8] %val)
+  ret void
+}
+
+; CHECK: error: error: invalid constraint '{s999}' in 'bad_output': could not allocate output register for constraint
+define amdgpu_kernel void @bad_output() {
+  tail call i32 asm sideeffect "s_nop", "={s999}"()
+  ret void
+}

>From 4ffeb798a790bc2de4216804700c418c582c9881 Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Mon, 28 Apr 2025 15:07:04 +0200
Subject: [PATCH 3/3] fix tests

---
 .../CodeGen/GlobalISel/InlineAsmLowering.cpp  |  3 +--
 .../GlobalISel/inline-asm-lowering-errors.ll  | 24 ++++++++++++++++---
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index d4a38f180f011..1b103c8fc6226 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -235,8 +235,7 @@ bool InlineAsmLowering::lowerInlineAsm(
   const auto ConstraintError = [&](const GISelAsmOperandInfo &Info, Twine Msg) {
     LLVMContext &Ctx = MIRBuilder.getContext();
     Ctx.diagnose(DiagnosticInfoInlineAsm(
-        Call, "invalid constraint '" + Info.ConstraintCode + "' in '" +
-                  MF.getName() + "': " + Msg));
+        Call, "invalid constraint '" + Info.ConstraintCode + "': " + Msg));
     // TODO: Recover if fallback isn't used. Otherwise let the fallback to DAG
     // kick in.
     return false;
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll
index 95f9b9ba5505a..fad7a920153fe 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll
@@ -1,13 +1,31 @@
-; RUN: llc -S -mtriple=amdgcn -mcpu=fiji -O0 -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2> %t.err
+; RUN: not llc -mtriple=amdgcn -mcpu=fiji -O0 -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2>&1 | FileCheck %s
 
-; CHECK: error: invalid constraint '' in 'aggregates': aggregate input operands not supported yet
+; CHECK: error: invalid constraint '': aggregate input operands not supported yet
 define amdgpu_kernel void @aggregates([4 x i8] %val) {
   tail call void asm sideeffect "s_nop", "r"([4 x i8] %val)
   ret void
 }
 
-; CHECK: error: error: invalid constraint '{s999}' in 'bad_output': could not allocate output register for constraint
+; CHECK: error: invalid constraint '{s999}': could not allocate output register for constraint
 define amdgpu_kernel void @bad_output() {
   tail call i32 asm sideeffect "s_nop", "={s999}"()
   ret void
 }
+
+; CHECK: error: invalid constraint '{s998}': could not allocate input register for register constraint
+define amdgpu_kernel void @bad_input() {
+  tail call void asm sideeffect "s_nop", "{s998}"(i32 undef)
+  ret void
+}
+; CHECK: error: invalid constraint '{s997}': indirect register inputs are not supported yet
+define amdgpu_kernel void @indirect_input() {
+  tail call void asm sideeffect "s_nop", "*{s997}"(ptr elementtype(i32) undef)
+  ret void
+}
+
+; CHECK: error: invalid constraint 'i': unsupported constraint
+define amdgpu_kernel void @badimm() {
+  tail call void asm sideeffect "s_nop", "i"(i32 undef)
+  ret
+  void
+}



More information about the llvm-commits mailing list