[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 18:56:08 PDT 2026
https://github.com/isanbard created https://github.com/llvm/llvm-project/pull/229628
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
>From e6e6daa9c2275f1d5c2d3cecd38ebe5d6a3e7071 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 19eae930cc26b10..5d66cb5e3322765 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 dfbf9a401b53eab..de009524f97c377 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 000000000000000..7ebe9f006943f9f
--- /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 000000000000000..3f3a9168de38f84
--- /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