https://github.com/isanbard updated https://github.com/llvm/llvm-project/pull/229628
>From 095f83b8a7422ee2bd8869dea9fac1ab0e3bf8ce Mon Sep 17 00:00:00 2001 From: Bill Wendling <[email protected]> Date: Tue, 6 Oct 2026 06:12:08 -0700 Subject: [PATCH 1/2] [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 b595de3734bb9f..e994b6af23eb04 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -10504,8 +10504,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, @@ -10567,9 +10568,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(), @@ -10582,6 +10588,8 @@ computeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call, OpInfo.isIndirect = true; } } + + return false; } /// Prepare DAG-level operands. As part of this, assign virtual and physical @@ -10875,7 +10883,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 +} >From 8a0c12bc816aca775c88d485dec185c1c3106a06 Mon Sep 17 00:00:00 2001 From: Bill Wendling <[email protected]> Date: Thu, 15 Oct 2026 18:20:52 -0700 Subject: [PATCH 2/2] [CodeGen] Address review: error for "=m|r" direct outputs too A direct output whose chosen alternative is memory, e.g. "=m|r", still crashed SelectionDAG, which exempted multiple-alternative operands from the check and then asked for the address of an output with no value. Drop the exemption and test "=m|r" with both instruction selectors. Name the selector explicitly in the RUN lines rather than relying on -O levels, since X86 GlobalISel can't lower inline asm and both X86 lines ran SelectionDAG anyway. Drop "yet" from the error: Clang never emits a direct memory output, so there are no plans to support one. Assisted-by: Claude Opus 5.5 --- .../CodeGen/GlobalISel/InlineAsmLowering.cpp | 2 +- .../SelectionDAG/SelectionDAGBuilder.cpp | 9 +++---- .../inline-asm-direct-mem-output-error.ll | 14 ++++++++--- .../X86/inline-asm-direct-mem-output-error.ll | 24 +++++++++++-------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp index 5d66cb5e332276..210f693f7bf583 100644 --- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp @@ -340,7 +340,7 @@ bool InlineAsmLowering::lowerInlineAsm( // 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 " + "cannot handle direct memory outputs for " "constraint '" + Twine(OpInfo.ConstraintCode) + "'", GetOrCreateVRegs(Call)); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index e994b6af23eb04..c15e96738752cc 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -10570,10 +10570,11 @@ computeConstraintToUse(ConstraintDecisionInfo &Info, const CallBase &Call, !OpInfo.isIndirect) { // 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 << "'"; + // need a stack slot to be reloaded after the asm. That includes an + // output whose chosen alternative is memory, e.g. "=m|r". + if (OpInfo.Type != InlineAsm::isInput) { + Info.ErrorMsg << "cannot handle direct memory outputs for constraint '" + << OpInfo.ConstraintCode << "'"; return true; } 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 index 7ebe9f006943f9..12aa4a46da9989 100644 --- a/llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll +++ b/llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll @@ -1,13 +1,21 @@ -; RUN: not llc -mtriple=aarch64 -filetype=null < %s 2>&1 | FileCheck %s +; RUN: not llc -mtriple=aarch64 -global-isel=0 -filetype=null < %s 2>&1 \ +; RUN: | FileCheck --implicit-check-not=error: %s ; RUN: not llc -mtriple=aarch64 -global-isel -global-isel-abort=1 \ -; RUN: -filetype=null < %s 2>&1 | FileCheck %s +; RUN: -filetype=null < %s 2>&1 | FileCheck --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, with either ; instruction selector. -; CHECK: error: cannot handle direct memory outputs yet for constraint 'm' +; CHECK: error: cannot handle direct memory outputs for constraint 'm' define i64 @rm_output() { %r = call i64 asm "# $0", "=rm"() ret i64 %r } + +; With multiple alternatives, the memory one is chosen here, too. +; CHECK: error: cannot handle direct memory outputs for constraint 'm' +define i64 @multi_alt() { + %r = call i64 asm "# $0", "=m|r"() + 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 index 3f3a9168de38f8..00135acfdc4769 100644 --- a/llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll +++ b/llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll @@ -1,31 +1,35 @@ -; 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 +; X86 GlobalISel can't lower inline asm, so only SelectionDAG is tested here; +; see the AArch64 test for both instruction selectors. +; RUN: not llc -mtriple=x86_64-unknown-linux-gnu -global-isel=0 \ +; RUN: -filetype=null < %s 2>&1 | FileCheck --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' +; CHECK: error: cannot handle direct memory outputs 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' +; CHECK: error: cannot handle direct memory outputs 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' +; CHECK: error: cannot handle direct memory outputs for constraint 'm' define i32 @m_i32() { %r = call i32 asm "# $0", "=m"() ret i32 %r } + +; With multiple alternatives, the memory one is chosen here, too. +; CHECK: error: cannot handle direct memory outputs for constraint 'm' +define i32 @multi_alt() { + %r = call i32 asm "# $0", "=m|r"() + ret i32 %r +} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
