https://github.com/isanbard updated https://github.com/llvm/llvm-project/pull/229628
>From 4b2180568b14cd5ffd89668b5d46f66fe9a28bea Mon Sep 17 00:00:00 2001 From: Bill Wendling <[email protected]> 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 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 +} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
