[llvm-branch-commits] [llvm] [CodeGen] Report an error for a direct inline asm output in memory (PR #229628)

Bill Wendling via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Oct 6 19:10:58 PDT 2026


https://github.com/isanbard updated https://github.com/llvm/llvm-project/pull/229628

>From b383cf6f32f300127dab4eb24f48e8e2323da674 Mon Sep 17 00:00:00 2001
From: Bill Wendling <isanbard at gmail.com>
Date: Tue, 6 Oct 2026 06:12:08 -0700
Subject: [PATCH] [CodeGen] Report an error for a direct inline asm output in
 memory

An inline asm output returned by value has no memory to write to, yet a
constraint such as "=rm" picks memory, the most general constraint, as
does "=m". SelectionDAG asserted on that ("Can only indirectify direct
input operands!"), and GlobalISel dereferenced a null pointer. Clang
never emits such an output, since it passes the address of a memory
output, but other IR can. Report "cannot handle direct memory outputs
yet for constraint 'm'" instead, like the other inline asm errors there.

Assisted-by: Claude Opus 5.5
---
 .../CodeGen/GlobalISel/InlineAsmLowering.cpp  | 10 ++++++
 .../SelectionDAG/SelectionDAGBuilder.cpp      | 22 +++++++++----
 .../inline-asm-direct-mem-output-error.ll     | 13 ++++++++
 .../X86/inline-asm-direct-mem-output-error.ll | 31 +++++++++++++++++++
 4 files changed, 70 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll
 create mode 100644 llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll

diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 19eae930cc26b1..5d66cb5e332276 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -337,6 +337,16 @@ bool InlineAsmLowering::lowerInlineAsm(
     switch (OpInfo.Type) {
     case InlineAsm::isOutput:
       if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
+        // A direct output is the call's result, with no address to write to.
+        if (!OpInfo.isIndirect) {
+          emitInlineAsmError(MIRBuilder, Call,
+                             "cannot handle direct memory outputs yet for "
+                             "constraint '" +
+                                 Twine(OpInfo.ConstraintCode) + "'",
+                             GetOrCreateVRegs(Call));
+          return true;
+        }
+
         const InlineAsm::ConstraintCode ConstraintID =
             TLI->getInlineAsmMemConstraint(OpInfo.ConstraintCode);
         assert(ConstraintID != InlineAsm::ConstraintCode::Unknown &&
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index dfbf9a401b53ea..de009524f97c37 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10491,8 +10491,9 @@ constructOperandInfo(ConstraintDecisionInfo &Info,
   return false;
 }
 
-/// Compute which constraint option to use for each operand.
-static void
+/// Compute which constraint option to use for each operand. Returns true (and
+/// sets Info.ErrorMsg) on failure.
+static bool
 computeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
                        TargetLowering::AsmOperandInfoVector &TargetConstraints,
                        SelectionDAGBuilder &Builder, const TargetLowering &TLI,
@@ -10554,9 +10555,14 @@ 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!");
+      // Only an input has a value to store to memory. A direct output, which
+      // Clang never emits with a memory constraint but other IR can, would
+      // need a stack slot to be reloaded after the asm.
+      if (!OpInfo.isMultipleAlternative && OpInfo.Type != InlineAsm::isInput) {
+        Info.ErrorMsg << "cannot handle direct memory outputs yet for "
+                      << "constraint '" << OpInfo.ConstraintCode << "'";
+        return true;
+      }
 
       // Memory operands really want the address of the value.
       Info.Chain = getAddressForMemoryInput(Info.Chain, Builder.getCurSDLoc(),
@@ -10569,6 +10575,8 @@ computeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call,
       OpInfo.isIndirect = true;
     }
   }
+
+  return false;
 }
 
 /// Prepare DAG-level operands. As part of this, assign virtual and physical
@@ -10862,7 +10870,9 @@ 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);
+  if (computeConstraintToUse(Info, Call, TargetConstraints, Builder, TLI, TM,
+                             DAG))
+    return true;
 
   // AsmNodeOperands - The operands for the ISD::INLINEASM node.
   Info.AsmNodeOperands.push_back(SDValue()); // reserve space for input chain
diff --git a/llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll b/llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll
new file mode 100644
index 00000000000000..7ebe9f006943f9
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll
@@ -0,0 +1,13 @@
+; RUN: not llc -mtriple=aarch64 -filetype=null < %s 2>&1 | FileCheck %s
+; RUN: not llc -mtriple=aarch64 -global-isel -global-isel-abort=1 \
+; RUN:   -filetype=null < %s 2>&1 | FileCheck %s
+
+; A direct output is the asm's result, so there is no memory to write it to.
+; Picking a memory constraint for one is an error, not a crash, with either
+; instruction selector.
+
+; CHECK: error: cannot handle direct memory outputs yet for constraint 'm'
+define i64 @rm_output() {
+  %r = call i64 asm "# $0", "=rm"()
+  ret i64 %r
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll b/llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll
new file mode 100644
index 00000000000000..3f3a9168de38f8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll
@@ -0,0 +1,31 @@
+; RUN: not llc -mtriple=x86_64-unknown-linux-gnu -O0 -filetype=null < %s 2>&1 \
+; RUN:   | FileCheck --check-prefix=O0 --implicit-check-not=error: %s
+; RUN: not llc -mtriple=x86_64-unknown-linux-gnu -filetype=null < %s 2>&1 \
+; RUN:   | FileCheck --check-prefix=O2 --implicit-check-not=error: %s
+
+; A direct output is the asm's result, so there is no memory to write it to.
+; Picking a memory constraint for one is an error, not a crash.
+
+; "rm" picks memory, the most general constraint.
+; O0: error: cannot handle direct memory outputs yet for constraint 'm'
+; O2: error: cannot handle direct memory outputs yet for constraint 'm'
+define i32 @rm_i32() {
+  %r = call i32 asm "# $0", "=rm"()
+  ret i32 %r
+}
+
+; The same for a value no 'r' register can hold.
+; O0: error: cannot handle direct memory outputs yet for constraint 'm'
+; O2: error: cannot handle direct memory outputs yet for constraint 'm'
+define x86_fp80 @rm_x86_fp80() {
+  %r = call x86_fp80 asm "# $0", "=rm"()
+  ret x86_fp80 %r
+}
+
+; "=m" has no other choice.
+; O0: error: cannot handle direct memory outputs yet for constraint 'm'
+; O2: error: cannot handle direct memory outputs yet for constraint 'm'
+define i32 @m_i32() {
+  %r = call i32 asm "# $0", "=m"()
+  ret i32 %r
+}



More information about the llvm-branch-commits mailing list