[llvm-branch-commits] [llvm] [TargetLowering][X86] Prefer 'r' over 'm' for foldable "rm" inline as… (PR #229632)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 6 18:58:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-aarch64
Author: Bill Wendling (isanbard)
<details>
<summary>Changes</summary>
…m operands
An "rm" (register-or-memory) inline asm operand has always resolved to 'm', because getConstraintPreferences() picks the most general constraint present, and 'm' is more general than 'r'. That's safe, since memory can't run out, but it forces a value that could stay in a register through a stack slot even when there's no register pressure (https://github.com/llvm/llvm-project/issues/20571).
Prefer 'r' instead where the register allocator can fold the register back to a stack slot when it runs out of registers, and mark the register operand foldable (InlineAsm::Flag::setRegMayBeFolded()) so it does. Both allocators can: the greedy allocator folds an operand when it spills its value, and the fast allocator folds operands up front when the asm's register operands wouldn't fit.
ParseConstraints() sets AsmOperandInfo::MayFoldRegister for an operand whose constraint codes are exactly {r, m}, above -O0, on a target that opts in through the new supportsRegMemInlineAsmFolding() hook, which needs TargetInstrInfo::getFrameIndexOperands() and is only enabled for X86. It leaves out operands where the fold can't work:
- An indirect operand, which Clang emits for every "=rm" output and for an "rm" input of aggregate type, is already in memory.
- A value no 'r' register can hold, e.g. an x87 or vector value.
- A value that takes two registers, e.g. __int128 on x86-64 or a 64-bit integer on i686, since the allocator folds a single register operand.
The tied input of a "+rm" operand has its own constraint ("0"), so only the output is marked.
SelectionDAG and GlobalISel, which shares getConstraintPreferences(), both mark the operand foldable, though no in-tree target both lowers inline asm with GlobalISel and supports the fold yet.
inlineasm-sched-bug.ll uses an "rm" input, which now stays in a register instead of round-tripping through a stack slot.
Assisted-by: Claude Opus 5.5
---
Patch is 107.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/229632.diff
15 Files Affected:
- (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+18)
- (modified) llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp (+7)
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+10-4)
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h (+4-2)
- (modified) llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp (+51-2)
- (modified) llvm/lib/Target/X86/X86ISelLowering.h (+4)
- (modified) llvm/test/CodeGen/AArch64/inline-asm-direct-mem-output-error.ll (+2-1)
- (added) llvm/test/CodeGen/X86/asm-constraints-rm-callbr-fold.ll (+74)
- (added) llvm/test/CodeGen/X86/asm-constraints-rm-isel.ll (+97)
- (added) llvm/test/CodeGen/X86/asm-constraints-rm-pressure.ll (+689)
- (added) llvm/test/CodeGen/X86/asm-constraints-rm.ll (+35)
- (added) llvm/test/CodeGen/X86/asm-constraints-torture.ll (+767)
- (added) llvm/test/CodeGen/X86/inline-asm-callbase.ll (+227)
- (modified) llvm/test/CodeGen/X86/inline-asm-direct-mem-output-error.ll (+2-3)
- (modified) llvm/test/CodeGen/X86/inlineasm-sched-bug.ll (+1-4)
``````````diff
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index ab92757abeb66..1c0efaeadb52a 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -5473,6 +5473,16 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
/// The ValueType for the operand value.
MVT ConstraintVT = MVT::Other;
+ /// True if this "rm" operand should prefer a register, leaving the
+ /// register allocator to fold it to a stack slot if it runs out of
+ /// registers. ParseConstraints() sets this for a direct operand whose
+ /// value fits in one register, on a target that can fold it.
+ /// getConstraintPreferences() then picks 'r', and instruction selection
+ /// marks the register operand foldable (see
+ /// InlineAsm::Flag::setRegMayBeFolded()). The tied input of a "+rm"
+ /// operand has its own constraint ("0"), so only the output gets this.
+ bool MayFoldRegister = false;
+
/// Copy constructor for copying from a ConstraintInfo.
AsmOperandInfo(InlineAsm::ConstraintInfo Info)
: InlineAsm::ConstraintInfo(std::move(Info)) {}
@@ -5517,6 +5527,14 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
/// Given a constraint, return the type of constraint it is for this target.
virtual ConstraintType getConstraintType(StringRef Constraint) const;
+ /// Return true if the register allocator can fold an inline asm register
+ /// operand to a stack slot on this target, which needs an override of
+ /// TargetInstrInfo::getFrameIndexOperands(). Only then may an "rm" operand
+ /// prefer a register (see AsmOperandInfo::MayFoldRegister): that choice
+ /// relies on the allocator falling back to memory when it runs out of
+ /// registers.
+ virtual bool supportsRegMemInlineAsmFolding() const { return false; }
+
using ConstraintPair = std::pair<StringRef, TargetLowering::ConstraintType>;
using ConstraintGroup = SmallVector<ConstraintPair>;
/// Given an OpInfo with list of constraints codes as strings, return a
diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 5d66cb5e33227..9aa2bfdf265d4 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -393,6 +393,11 @@ bool InlineAsmLowering::lowerInlineAsm(
// tied operands that can use the regclass information from the def.
const TargetRegisterClass *RC = MRI->getRegClass(OpInfo.Regs.front());
Flag.setRegClass(RC->getID());
+
+ // An "rm" operand that preferred a register may still be folded to
+ // a stack slot by the register allocator.
+ if (OpInfo.MayFoldRegister)
+ Flag.setRegMayBeFolded(true);
}
Inst.addImm(Flag);
@@ -576,6 +581,8 @@ bool InlineAsmLowering::lowerInlineAsm(
// Put the register class of the virtual registers in the flag word.
const TargetRegisterClass *RC = MRI->getRegClass(OpInfo.Regs.front());
Flag.setRegClass(RC->getID());
+ if (OpInfo.MayFoldRegister)
+ Flag.setRegMayBeFolded(true);
}
Inst.addImm(Flag);
if (!buildAnyextOrCopy(OpInfo.Regs[0], SourceRegs[0], MIRBuilder))
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index de009524f97c3..24af1b5d43467 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -1036,7 +1036,8 @@ void RegsForValue::getCopyToRegs(SDValue Val, SelectionDAG &DAG,
void RegsForValue::AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
unsigned MatchingIdx, const SDLoc &dl,
SelectionDAG &DAG,
- std::vector<SDValue> &Ops) const {
+ std::vector<SDValue> &Ops,
+ bool MayFoldRegister) const {
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
InlineAsm::Flag Flag(Code, Regs.size());
@@ -1051,6 +1052,10 @@ void RegsForValue::AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
const MachineRegisterInfo &MRI = DAG.getMachineFunction().getRegInfo();
const TargetRegisterClass *RC = MRI.getRegClass(Regs.front());
Flag.setRegClass(RC->getID());
+ if (MayFoldRegister) {
+ assert(Regs.size() == 1 && "only a single register can be folded");
+ Flag.setRegMayBeFolded(true);
+ }
}
SDValue Res = DAG.getTargetConstant(Flag, dl, MVT::i32);
@@ -10655,7 +10660,7 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
OpInfo.AssignedRegs.AddInlineAsmOperands(
OpInfo.isEarlyClobber ? InlineAsm::Kind::RegDefEarlyClobber
: InlineAsm::Kind::RegDef,
- false, 0, DL, DAG, Info.AsmNodeOperands);
+ false, 0, DL, DAG, Info.AsmNodeOperands, OpInfo.MayFoldRegister);
}
break;
@@ -10821,8 +10826,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, DL, Info.Chain,
&Info.Glue, &Call);
- OpInfo.AssignedRegs.AddInlineAsmOperands(
- InlineAsm::Kind::RegUse, false, 0, DL, DAG, Info.AsmNodeOperands);
+ OpInfo.AssignedRegs.AddInlineAsmOperands(InlineAsm::Kind::RegUse, false,
+ 0, DL, DAG, Info.AsmNodeOperands,
+ OpInfo.MayFoldRegister);
break;
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
index c9bb96b86b7bd..7981c24e83865 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
@@ -826,10 +826,12 @@ struct RegsForValue {
/// Add this value to the specified inlineasm node operand list. This adds the
/// code marker, matching input operand index (if applicable), and includes
- /// the number of values added into it.
+ /// the number of values added into it. If \p MayFoldRegister, the register
+ /// allocator may fold the (single) register to a stack slot.
void AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
unsigned MatchingIdx, const SDLoc &dl,
- SelectionDAG &DAG, std::vector<SDValue> &Ops) const;
+ SelectionDAG &DAG, std::vector<SDValue> &Ops,
+ bool MayFoldRegister = false) const;
/// Check if the total RegCount is greater than one.
bool occupiesMultipleRegs() const {
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index e89be8b69929f..0b5a4534d3af9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -6228,6 +6228,42 @@ unsigned TargetLowering::AsmOperandInfo::getMatchedOperand() const {
return atoi(ConstraintCode.c_str());
}
+/// Return true if \p OpInfo is an "rm" operand that should prefer a register,
+/// leaving the register allocator to fold it to a stack slot if it runs out of
+/// registers (see AsmOperandInfo::MayFoldRegister).
+static bool
+shouldPreferFoldableRegister(const TargetLowering &TLI,
+ const TargetRegisterInfo *TRI,
+ const TargetLowering::AsmOperandInfo &OpInfo) {
+ // Codes stays empty for a constraint with alternatives ("r|m") until one is
+ // selected, so this only ever matches a plain "rm".
+ if (OpInfo.Codes.size() != 2 || !is_contained(OpInfo.Codes, "r") ||
+ !is_contained(OpInfo.Codes, "m"))
+ return false;
+
+ // Without a fold to fall back on, 'm' is the only choice that can't run out
+ // of registers. Above -O0 only, which leaves -O0 code as it was.
+ if (!TLI.supportsRegMemInlineAsmFolding() ||
+ TLI.getTargetMachine().getOptLevel() == CodeGenOptLevel::None)
+ return false;
+
+ // An indirect operand is already in memory: a register would only add a
+ // load or store around the asm, and isn't supported for an indirect input.
+ if (OpInfo.isIndirect)
+ return false;
+
+ // The register allocator folds one register operand, so the value must fit
+ // in a single register, and some types (e.g. x87 and vector types) can't go
+ // in an 'r' register at all.
+ MVT VT = OpInfo.ConstraintVT;
+ if (VT == MVT::Other || VT.isScalableVector())
+ return false;
+
+ const TargetRegisterClass *RC =
+ TLI.getRegForInlineAsmConstraint(TRI, "r", VT).second;
+ return RC && VT.getFixedSizeInBits() <= TRI->getRegSizeInBits(*RC);
+}
+
/// Split up the constraint string from the inline assembly value into the
/// specific constraints and their prefixes, and also tie in the associated
/// operand values.
@@ -6326,6 +6362,8 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
OpInfo.ConstraintVT = VT.isSimple() ? VT.getSimpleVT() : MVT::Other;
ArgNo++;
}
+
+ OpInfo.MayFoldRegister = shouldPreferFoldableRegister(*this, TRI, OpInfo);
}
// If we have multiple alternative constraints, select the best alternative.
@@ -6424,7 +6462,8 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
/// preferrable (when they can be emitted). A higher return value means a
/// stronger preference for one constraint type relative to another.
/// FIXME: We should prefer registers over memory but doing so may lead to
-/// unrecoverable register exhaustion later.
+/// unrecoverable register exhaustion later, unless the register allocator can
+/// fold the register to memory (see AsmOperandInfo::MayFoldRegister).
/// https://github.com/llvm/llvm-project/issues/20571
static unsigned getConstraintPiority(TargetLowering::ConstraintType CT) {
switch (CT) {
@@ -6538,7 +6577,10 @@ TargetLowering::ConstraintWeight
/// 1) If there is an 'other' constraint, and if the operand is valid for
/// that constraint, use it. This makes us take advantage of 'i'
/// constraints when available.
-/// 2) Otherwise, pick the most general constraint present. This prefers
+/// 2) For an "rm" operand that the register allocator can fold to a stack
+/// slot (see AsmOperandInfo::MayFoldRegister), pick 'r': the allocator
+/// still falls back to memory if it runs out of registers.
+/// 3) Otherwise, pick the most general constraint present. This prefers
/// 'm' over 'r', for example.
///
TargetLowering::ConstraintGroup TargetLowering::getConstraintPreferences(
@@ -6546,6 +6588,13 @@ TargetLowering::ConstraintGroup TargetLowering::getConstraintPreferences(
ConstraintGroup Ret;
Ret.reserve(OpInfo.Codes.size());
+
+ if (OpInfo.MayFoldRegister) {
+ Ret.emplace_back("r", getConstraintType("r"));
+ Ret.emplace_back("m", getConstraintType("m"));
+ return Ret;
+ }
+
for (StringRef Code : OpInfo.Codes) {
TargetLowering::ConstraintType CType = getConstraintType(Code);
diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index ba04080573c42..45897dd0f527e 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -414,6 +414,10 @@ namespace llvm {
ConstraintType getConstraintType(StringRef Constraint) const override;
+ // X86InstrInfo::getFrameIndexOperands() lets the register allocator fold
+ // an inline asm register operand to a stack slot.
+ bool supportsRegMemInlineAsmFolding() const override { return true; }
+
/// Examine constraint string and operand type and determine a weight value.
/// The operand object must already have been set up with the operand type.
ConstraintWeight
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 7ebe9f006943f..2165b79681476 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
@@ -4,7 +4,8 @@
; 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.
+; instruction selector. AArch64 can't fold a register operand to memory, so
+; "rm" keeps picking memory.
; CHECK: error: cannot handle direct memory outputs yet for constraint 'm'
define i64 @rm_output() {
diff --git a/llvm/test/CodeGen/X86/asm-constraints-rm-callbr-fold.ll b/llvm/test/CodeGen/X86/asm-constraints-rm-callbr-fold.ll
new file mode 100644
index 0000000000000..f1f7608b8f718
--- /dev/null
+++ b/llvm/test/CodeGen/X86/asm-constraints-rm-callbr-fold.ll
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu --regalloc=fast \
+; RUN: -verify-machineinstrs -verify-regalloc < %s | FileCheck %s
+
+; The fast allocator folds this "=&rm" output to a stack slot, since the six
+; "r" inputs and the clobbers leave no register for it. The reload of %r after
+; the callbr only covers the fallthrough path: on the indirect path, %r must
+; come from the slot the asm wrote, which has to be %r's own spill slot, so
+; that the indirect block's ordinary reload of %r finds it.
+define i64 @test_callbr_rm_indirect_use(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f) {
+; CHECK-LABEL: test_callbr_rm_indirect_use:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: pushq %rbp
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: pushq %r15
+; CHECK-NEXT: .cfi_def_cfa_offset 24
+; CHECK-NEXT: pushq %r14
+; CHECK-NEXT: .cfi_def_cfa_offset 32
+; CHECK-NEXT: pushq %r13
+; CHECK-NEXT: .cfi_def_cfa_offset 40
+; CHECK-NEXT: pushq %r12
+; CHECK-NEXT: .cfi_def_cfa_offset 48
+; CHECK-NEXT: pushq %rbx
+; CHECK-NEXT: .cfi_def_cfa_offset 56
+; CHECK-NEXT: .cfi_offset %rbx, -56
+; CHECK-NEXT: .cfi_offset %r12, -48
+; CHECK-NEXT: .cfi_offset %r13, -40
+; CHECK-NEXT: .cfi_offset %r14, -32
+; CHECK-NEXT: .cfi_offset %r15, -24
+; CHECK-NEXT: .cfi_offset %rbp, -16
+; CHECK-NEXT: movq %rdi, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill
+; CHECK-NEXT: #APP # 8-byte Folded Spill
+; CHECK-NEXT: # -{{[0-9]+}}(%rsp) %rdi %rsi %rdx %rcx %r8 %r9 .LBB0_3
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rax # 8-byte Reload
+; CHECK-NEXT: movq %rax, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill
+; CHECK-NEXT: # %bb.1: # %fallthrough
+; CHECK-NEXT: xorl %eax, %eax
+; CHECK-NEXT: # kill: def $rax killed $eax
+; CHECK-NEXT: .LBB0_2: # %fallthrough
+; CHECK-NEXT: popq %rbx
+; CHECK-NEXT: .cfi_def_cfa_offset 48
+; CHECK-NEXT: popq %r12
+; CHECK-NEXT: .cfi_def_cfa_offset 40
+; CHECK-NEXT: popq %r13
+; CHECK-NEXT: .cfi_def_cfa_offset 32
+; CHECK-NEXT: popq %r14
+; CHECK-NEXT: .cfi_def_cfa_offset 24
+; CHECK-NEXT: popq %r15
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: popq %rbp
+; CHECK-NEXT: .cfi_def_cfa_offset 8
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB0_3: # Inline asm indirect target
+; CHECK-NEXT: # %indirect
+; CHECK-NEXT: # Label of block must be emitted
+; CHECK-NEXT: .cfi_def_cfa_offset 56
+; CHECK-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rax # 8-byte Reload
+; CHECK-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rcx # 8-byte Reload
+; CHECK-NEXT: addq %rcx, %rax
+; CHECK-NEXT: jmp .LBB0_2
+entry:
+ %r = callbr i64 asm sideeffect "# $0 $1 $2 $3 $4 $5 $6 ${7:l}",
+ "=&rm,r,r,r,r,r,r,!i,~{rax},~{rbx},~{rbp},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15}"
+ (i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f)
+ to label %fallthrough [label %indirect]
+
+fallthrough:
+ ret i64 0
+
+indirect:
+ %sum = add i64 %r, %a
+ ret i64 %sum
+}
diff --git a/llvm/test/CodeGen/X86/asm-constraints-rm-isel.ll b/llvm/test/CodeGen/X86/asm-constraints-rm-isel.ll
new file mode 100644
index 0000000000000..22a492a7baaf5
--- /dev/null
+++ b/llvm/test/CodeGen/X86/asm-constraints-rm-isel.ll
@@ -0,0 +1,97 @@
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stop-after=finalize-isel < %s \
+; RUN: | FileCheck --check-prefixes=CHECK,X64 %s
+; RUN: llc -mtriple=i686-unknown-linux-gnu -stop-after=finalize-isel < %s \
+; RUN: | FileCheck --check-prefixes=CHECK,X86 %s
+
+; An "rm" operand picks a register, marked foldable so that the register
+; allocator can still put it in a stack slot, but only when the allocator can
+; fold it: a direct operand whose value fits in one register. Everything else
+; keeps using memory.
+
+%struct.S = type { [3 x i8] }
+
+define void @input_i32(i32 %x) {
+; CHECK-LABEL: name: input_i32
+; CHECK: INLINEASM {{.*}}, reguse:GR32{{[_A-Z0-9]*}} foldable, %{{[0-9]+}}
+ call void asm sideeffect "# $0", "rm"(i32 %x)
+ ret void
+}
+
+; Two registers on i686, which can't be folded as one.
+define void @input_i64(i64 %x) {
+; CHECK-LABEL: name: input_i64
+; X64: INLINEASM {{.*}}, reguse:GR64{{[_A-Z0-9]*}} foldable, %{{[0-9]+}}
+; X86: INLINEASM {{.*}}, mem:m, %stack.0,
+ call void asm sideeffect "# $0", "rm"(i64 %x)
+ ret void
+}
+
+define void @input_double(double %x) {
+; CHECK-LABEL: name: input_double
+; X64: INLINEASM {{.*}}, reguse:GR64{{[_A-Z0-9]*}} foldable, %{{[0-9]+}}
+; X86: INLINEASM {{.*}}, mem:m, %stack.0,
+ call void asm sideeffect "# $0", "rm"(double %x)
+ ret void
+}
+
+define void @input_i128(i128 %x) {
+; CHECK-LABEL: name: input_i128
+; CHECK: INLINEASM {{.*}}, mem:m, %stack.0,
+ call void asm sideeffect "# $0", "rm"(i128 %x)
+ ret void
+}
+
+; No 'r' register can hold these at all.
+define void @input_x86_fp80(x86_fp80 %x) {
+; CHECK-LABEL: name: input_x86_fp80
+; CHECK: INLINEASM {{.*}}, mem:m, %stack.0,
+ call void asm sideeffect "# $0", "rm"(x86_fp80 %x)
+ ret void
+}
+
+define void @input_v4f32(<4 x float> %x) {
+; CHECK-LABEL: name: input_v4f32
+; CHECK: INLINEASM {{.*}}, mem:m, %stack.0,
+ call void asm sideeffect "# $0", "rm"(<4 x float> %x)
+ ret void
+}
+
+; An indirect operand already names memory, as Clang emits for an "rm" input
+; of aggregate type and for every "=rm" output.
+define void @input_indirect(ptr %p) {
+; CHECK-LABEL: name: input_indirect
+; CHECK: INLINEASM {{.*}}, mem:m, {{(killed )?}}%0,
+ call void asm sideeffect "# $0", "*rm"(ptr elementtype(%struct.S) %p)
+ ret void
+}
+
+define void @output_indirect(ptr %p) {
+; CHECK-LABEL: name: output_indirect
+; CHECK: INLINEASM {{.*}}, mem:m, {{(killed )?}}%0,
+ call void asm sideeffect "# $0", "=*rm"(ptr elementtype(i64) %p)
+ ret void
+}
+
+define i32 @output_i32() {
+; CHECK-LABEL: name: output_i32
+; CHECK: INLINEASM {{.*}}, regdef:GR32{{[_A-Z0-9]*}} foldable, def %{{[0-9]+}}
+ %r = call i32 asm sideeffect "# $0", "=rm"()
+ ret i32 %r
+}
+
+; A tied pair is folded through its output.
+define i32 @inout_i32(i32 %x) {
+; CHECK-LABEL: name: inout_i32
+; CHECK: INLINEASM {{.*}}, regdef:GR32{{[_A-Z0-9]*}} foldable, def %{{[0-9]+}}, reguse tiedto:$0, %{{[0-9]+}}(tied-def 3)
+ %r = call i32 asm sideeffect "# $0", "=rm,0"(i32 %x)
+ ret i32 %r
+}
+
+; Clang's form of "+rm": an indirect output can't use memory when tied, so it
+; was already a register, which stays unfoldable.
+define void @inout_indirect_i32(ptr %p, i32 %x) {
+; CHECK-LABEL: name: inout_indirect_i32
+; CHECK: INLINEASM {{.*}}, regdef:GR32{{[_A-Z0-9]*}}, def %{{[0-9]+}}, reguse tiedto:$0, %{{[0-9]+}}(tied-def 3)
+ call void asm sideeffect "# $0", "=*rm,0"(ptr elementtype(i32) %p, i32 %x)
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/asm-constraints-rm-pressure.ll b/llvm/test/CodeGen/X86/asm-constraints-rm-pressure.ll
new file mode 100644
index 0000000000000..00a6a626147cc
--- /dev/null
+++ b/llvm/test/CodeGen/X86/asm-constraints-rm-pressure.ll
@@ -0,0 +1,689 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu --regalloc=greedy -verify-machineinstrs < %s | FileCheck --check-prefix=GREEDY_RA %s
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu --regalloc=fast -verify-machineinstrs < %s | ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/229632
More information about the llvm-branch-commits
mailing list