[clang] [llvm] [CodeGen] Add support for multiple constraints (PR #195592)
Bill Wendling via cfe-commits
cfe-commits at lists.llvm.org
Mon May 4 08:30:37 PDT 2026
https://github.com/bwendling updated https://github.com/llvm/llvm-project/pull/195592
>From e4f6361e5bfd087b388237518ac4fb21913b2553 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 22 Apr 2026 20:01:52 -0700
Subject: [PATCH 1/2] [CodeGen] Add support for multiple constraints
Historically, LLVM prefered to use the more conservative inline assembly
constraint when presented with multiple constraints, e.g., "rm". This
leads to horrible code generation. For instance:
void write(unsigned long flags) {
asm("push %0 ; popf" : : "rm" (flags));
}
generates:
movq %rdi, -8(%rsp)
#APP
pushq -8(%rsp)
popfq
#NO_APP
because the "m" option is chosen by default, so that the back-ends have
the best chance to generate semantically correct code if register
pressure becomes too much.
The register allocators have grown up and are now able to fold registers
in such instances. There's no longer the need to restrict us to the most
conservative option, except for the fast register allocator. To that
end, we restrict the front-end from preferring the conservative
constraint (unless compiling at '-O0' where we don't care about code
generation quality).
However, simply preferring the least restrictive option doesn't work in
all situations. We could have a situation where the SelectionDAG isn't
able to satisfy the "r" constraint and so we would like it to consider
other constraint options if they exist.
In order to do that, we run each constraint through the logic that
determines whether to use a constraint from least restrictive to most
restrictive, stopping either we encounter a constraint that works or we
run out of constraints to consider.
In the most extreme case, each operand would have all of their
constraint options tried, making the complexity O(M * N), where M is the
number of operands and N is the max number of constraints per operand.
This is worse than the original O(M) complexity. However, we emphasize
that O(M * N) is the *worst* case scenario.
With this change, the code above generates:
#APP
pushq %rdi
popfq
#NO_APP
which is far more palatable.
Fixes: 20571
---
clang/lib/CodeGen/CGStmt.cpp | 16 +-
clang/test/CodeGen/asm-reg-mem-constraints.c | 74 ++
clang/test/CodeGen/asm.c | 25 -
llvm/include/llvm/CodeGen/TargetLowering.h | 16 +
llvm/include/llvm/IR/InlineAsm.h | 7 +
.../SelectionDAG/SelectionDAGBuilder.cpp | 182 ++--
.../SelectionDAG/SelectionDAGBuilder.h | 6 +-
.../CodeGen/SelectionDAG/TargetLowering.cpp | 90 +-
.../test/CodeGen/X86/asm-constraints-rm-O0.ll | 855 ++++++++++++++++++
llvm/test/CodeGen/X86/asm-constraints-rm.ll | 430 +++++++++
llvm/test/CodeGen/X86/asm-modifier.ll | 4 +-
.../test/CodeGen/X86/inline-asm-attributes.ll | 28 +
llvm/test/CodeGen/X86/inline-asm-bundles.ll | 14 +
llvm/test/CodeGen/X86/inline-asm-callbase.ll | 75 ++
llvm/test/CodeGen/X86/inline-asm-memory.ll | 74 ++
llvm/test/CodeGen/X86/inline-asm-rm-no-opt.ll | 55 ++
llvm/test/CodeGen/X86/inline-asm-rm-opt.ll | 45 +
llvm/test/CodeGen/X86/inlineasm-sched-bug.ll | 5 +-
18 files changed, 1867 insertions(+), 134 deletions(-)
create mode 100644 clang/test/CodeGen/asm-reg-mem-constraints.c
create mode 100644 llvm/test/CodeGen/X86/asm-constraints-rm-O0.ll
create mode 100644 llvm/test/CodeGen/X86/asm-constraints-rm.ll
create mode 100644 llvm/test/CodeGen/X86/inline-asm-attributes.ll
create mode 100644 llvm/test/CodeGen/X86/inline-asm-bundles.ll
create mode 100644 llvm/test/CodeGen/X86/inline-asm-callbase.ll
create mode 100644 llvm/test/CodeGen/X86/inline-asm-memory.ll
create mode 100644 llvm/test/CodeGen/X86/inline-asm-rm-no-opt.ll
create mode 100644 llvm/test/CodeGen/X86/inline-asm-rm-opt.ll
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 7b6035a6968b1..0c8197daf1407 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -2883,13 +2883,23 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
if (!Constraints.empty())
Constraints += ',';
- // If this is a register output, then make the inline asm return it
- // by-value. If this is a memory result, return the value by-reference.
+ // - If this is a register output, then make the inline asm return it
+ // by-value.
+ // - If this is a memory output, return the value by reference.
+ // - If this is a register and memory output, treat it like a register
+ // output at -O[1-3]. This allows the optimizing register allocators to
+ // choose a register, while the fast register allocator defaults to
+ // memory.
QualType QTy = OutExpr->getType();
const bool IsScalarOrAggregate = hasScalarEvaluationKind(QTy) ||
hasAggregateEvaluationKind(QTy);
- if (!Info.allowsMemory() && IsScalarOrAggregate) {
+ const bool RegisterMemoryConstraints =
+ CGM.getCodeGenOpts().OptimizationLevel != 0 &&
+ llvm::is_contained(OutputConstraint, 'r') &&
+ llvm::is_contained(OutputConstraint, 'm');
+ if (IsScalarOrAggregate &&
+ (!Info.allowsMemory() || RegisterMemoryConstraints)) {
Constraints += "=" + OutputConstraint;
ResultRegQualTys.push_back(QTy);
ResultRegDests.push_back(Dest);
diff --git a/clang/test/CodeGen/asm-reg-mem-constraints.c b/clang/test/CodeGen/asm-reg-mem-constraints.c
new file mode 100644
index 0000000000000..9f35dee93a72e
--- /dev/null
+++ b/clang/test/CodeGen/asm-reg-mem-constraints.c
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm %s -o - | FileCheck --check-prefixes=O0 %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O2 %s -o - | FileCheck --check-prefixes=O2 %s
+
+void test_1(unsigned long flags) {
+ // O0-LABEL: @test_1
+ // O0: call void asm sideeffect "", "rm,~{dirflag},~{fpsr},~{flags}"(i32 %0)
+ //
+ // O2-LABEL: @test_1
+ // O2: call void asm sideeffect "", "rm,~{dirflag},~{fpsr},~{flags}"(i32 %flags)
+ asm ("" : : "rm" (flags));
+}
+
+unsigned long test_2(void) {
+ // O0-LABEL: @test_2
+ // O0: call void asm "", "=*rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %out)
+ //
+ // O2-LABEL: @test_2
+ // O2: %0 = tail call i32 asm "", "=rm,~{dirflag},~{fpsr},~{flags}"()
+ unsigned long out;
+ asm ("" : "=rm" (out));
+ return out;
+}
+
+void test_3(unsigned long flags) {
+ // O0-LABEL: @test_3
+ // O0: call void asm sideeffect "", "imr,~{dirflag},~{fpsr},~{flags}"(i32 %0)
+ //
+ // O2-LABEL: @test_3
+ // O2: call void asm sideeffect "", "imr,~{dirflag},~{fpsr},~{flags}"(i32 %flags)
+ asm ("" : : "g" (flags));
+}
+
+unsigned long test_4(void) {
+ // O0-LABEL: @test_4
+ // O0: call void asm "", "=*imr,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %out)
+ //
+ // O2-LABEL: @test_4
+ // O2: %0 = tail call i32 asm "", "=imr,~{dirflag},~{fpsr},~{flags}"()
+ unsigned long out;
+ asm ("" : "=g" (out));
+ return out;
+}
+
+void test_5(int len) {
+ // O0-LABEL: @test_5
+ // O0: call void asm sideeffect "", "=*&rm,0,~{dirflag},~{fpsr},~{flags}"
+ //
+ // O2-LABEL: @test_5
+ // O2: %0 = tail call i32 asm sideeffect "", "=&rm,0,~{dirflag},~{fpsr},~{flags}"(i32 %len)
+ __asm__ volatile ("" : "+&&rm" (len));
+}
+
+void test_6(int len) {
+ // O0-LABEL: @test_6
+ // O0: call void asm sideeffect "", "=*%rm,=*rm,0,1,~{dirflag},~{fpsr},~{flags}"
+ //
+ // O2-LABEL: @test_6
+ // O2: %0 = tail call { i32, i32 } asm sideeffect "", "=%rm,=rm,0,1,~{dirflag},~{fpsr},~{flags}"(i32 %len, i32 %len)
+ __asm__ volatile ("" : "+%%rm" (len), "+rm" (len));
+}
+
+// PR3908
+void test_7(int r) {
+ // O0-LABEL: @test_7
+ // O0: call i32 asm "# PR3908 $1 $3 $2 $0", "=r,mx,mr,x,0,~{dirflag},~{fpsr},~{flags}"
+ // O0-SAME: (i32 0, i32 0, double 0.000000e+00, i32 %{{.*}})
+ //
+ // O2-LABEL: @test_7
+ // O2: %0 = tail call i32 asm "# PR3908 $1 $3 $2 $0", "=r,mx,mr,x,0,~{dirflag},~{fpsr},~{flags}"
+ // O2-SAME: (i32 0, i32 0, double 0.000000e+00, i32 %{{.*}})
+ __asm__ ("# PR3908 %[lf] %[xx] %[li] %[r]"
+ : [r] "+r" (r)
+ : [lf] "mx" (0), [li] "mr" (0), [xx] "x" ((double)(0)));
+}
diff --git a/clang/test/CodeGen/asm.c b/clang/test/CodeGen/asm.c
index d7465b22fbbf6..4c1cd2d493136 100644
--- a/clang/test/CodeGen/asm.c
+++ b/clang/test/CodeGen/asm.c
@@ -306,28 +306,3 @@ void t31(void) {
// CHECK: call void asm sideeffect "T31 CC NAMED MODIFIER: ${0:c}", "i,~{dirflag},~{fpsr},~{flags}"
__asm__ volatile ("T31 CC NAMED MODIFIER: %cc[input]" : : [input] "i" (4));
}
-
-// TODO: Move the "rm" tests into a new testcase file once work to better
-// support "rm" constraints is done.
-
-void t32(int len) {
- // CHECK-LABEL: @t32
- // CHECK: call void asm sideeffect "", "=*&rm,0,~{dirflag},~{fpsr},~{flags}"
- __asm__ volatile ("" : "+&&rm" (len));
-}
-
-void t33(int len) {
- // CHECK-LABEL: @t33
- // CHECK: call void asm sideeffect "", "=*%rm,=*rm,0,1,~{dirflag},~{fpsr},~{flags}"
- __asm__ volatile ("" : "+%%rm" (len), "+rm" (len));
-}
-
-// PR3908
-void t34(int r) {
- // CHECK-LABEL: @t34
- // CHECK: call i32 asm "PR3908 $1 $3 $2 $0", "=r,mx,mr,x,0,~{dirflag},~{fpsr},~{flags}"
- // CHECK-SAME: (i32 0, i32 0, double 0.000000e+00, i32 %{{.*}})
- __asm__ ("PR3908 %[lf] %[xx] %[li] %[r]"
- : [r] "+r" (r)
- : [lf] "mx" (0), [li] "mr" (0), [xx] "x" ((double)(0)));
-}
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 9b0bfa111f2d5..4955fb26bc3f9 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -5302,6 +5302,17 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
/// The ValueType for the operand value.
MVT ConstraintVT = MVT::Other;
+ /// The register may be folded. This is used if the constraint has register
+ /// and memory constraints where we prefer using a register, but can fall
+ /// back to a memory slot under register pressure.
+ bool MayFoldRegister = false;
+
+ /// The index to the last matched constraint code.
+ long ConstraintIndex = -1;
+
+ /// The constraint was successfully assigned to the operand.
+ bool Finalized = false;
+
/// Copy constructor for copying from a ConstraintInfo.
AsmOperandInfo(InlineAsm::ConstraintInfo Info)
: InlineAsm::ConstraintInfo(std::move(Info)) {}
@@ -5313,6 +5324,11 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
/// If this is an input matching constraint, this method returns the output
/// operand it matches.
LLVM_ABI unsigned getMatchedOperand() const;
+
+ /// Return true if there are no more constraints to try.
+ bool atFinalConstraint() const {
+ return ConstraintIndex >= static_cast<long>(Codes.size() - 1);
+ }
};
using AsmOperandInfoVector = std::vector<AsmOperandInfo>;
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 564f2e7df2dd3..bab538e852467 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -181,6 +181,13 @@ class InlineAsm final : public Value {
bool hasArg() const {
return Type == isInput || (Type == isOutput && isIndirect);
}
+
+ /// hasRegMemConstraints - Returns true if the constraint codes have
+ /// register and memory constraints. This is useful to let the register
+ /// allocator that it can use memory under register pressure.
+ bool hasRegMemConstraints() const {
+ return is_contained(Codes, "r") && is_contained(Codes, "m");
+ }
};
/// ParseConstraints - Split up the constraint string into the specific
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 458fd21c5ab6d..62b1ecb3b92f1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -1032,7 +1032,8 @@ void RegsForValue::getCopyToRegs(SDValue Val, SelectionDAG &DAG,
}
void RegsForValue::AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
- unsigned MatchingIdx, const SDLoc &dl,
+ unsigned MatchingIdx,
+ bool MayFoldRegister, const SDLoc &dl,
SelectionDAG &DAG,
std::vector<SDValue> &Ops) const {
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
@@ -1049,6 +1050,7 @@ 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());
+ Flag.setRegMayBeFolded(MayFoldRegister);
}
SDValue Res = DAG.getTargetConstant(Flag, dl, MVT::i32);
@@ -10195,6 +10197,8 @@ static bool isFunction(SDValue Op) {
namespace {
+/// ConstraintDecisionInfo - A struct that holds information while determining
+/// which constraint to use for an inline asm operand.
struct ConstraintDecisionInfo {
SmallVector<SDISelAsmOperandInfo, 16> ConstraintOperands;
std::vector<SDValue> AsmNodeOperands;
@@ -10206,6 +10210,14 @@ struct ConstraintDecisionInfo {
raw_svector_ostream ErrorMsg;
ConstraintDecisionInfo() : ErrorMsg(Buffer) {}
+
+ void reset() {
+ ConstraintOperands.clear();
+ AsmNodeOperands.clear();
+ Glue = SDValue();
+ Chain = SDValue();
+ BeginLabel = nullptr;
+ }
};
} // end anonymous namespace
@@ -10236,8 +10248,9 @@ constructOperandInfo(ConstraintDecisionInfo &Info,
!isa<ConstantSDNode>(OpInfo.CallOperand)) {
// We've delayed emitting a diagnostic like the "n" constraint because
// inlining could cause an integer showing up.
- Info.ErrorMsg << "constraint '" << T.ConstraintCode
- << "' expects an integer constant expression";
+ if (OpInfo.atFinalConstraint())
+ Info.ErrorMsg << "constraint '" << T.ConstraintCode
+ << "' expects an integer constant expression";
return true;
}
@@ -10310,9 +10323,9 @@ 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!");
+ assert(
+ (OpInfo.isMultipleAlternative || OpInfo.Type == InlineAsm::isInput) &&
+ "Can only indirectify direct input operands!");
// Memory operands really want the address of the value.
Info.Chain = getAddressForMemoryInput(Info.Chain, Builder.getCurSDLoc(),
@@ -10334,6 +10347,15 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
SelectionDAGBuilder &Builder,
const TargetLowering &TLI,
SelectionDAG &DAG) {
+ // Registers before tied operands can't be folded, because the tied operand
+ // will move, which the back-end isn't able to properly account for.
+ bool Clear = false;
+ for (SDISelAsmOperandInfo &OpInfo : llvm::reverse(Info.ConstraintOperands)) {
+ Clear |= OpInfo.isMatchingInputConstraint();
+ if (Clear)
+ OpInfo.MayFoldRegister = false;
+ }
+
SDLoc DL = Builder.getCurSDLoc();
for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
// Assign Registers.
@@ -10343,12 +10365,14 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
: OpInfo;
const auto RegError = getRegistersForValue(DAG, DL, OpInfo, RefOpInfo);
if (RegError) {
- const MachineFunction &MF = DAG.getMachineFunction();
- const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
- const char *RegName = TRI.getName(*RegError);
- Info.ErrorMsg << "register '" << RegName << "' allocated for constraint '"
- << OpInfo.ConstraintCode
- << "' does not match required type";
+ if (OpInfo.atFinalConstraint()) {
+ const MachineFunction &MF = DAG.getMachineFunction();
+ const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+ const char *RegName = TRI.getName(*RegError);
+ Info.ErrorMsg << "register '" << RegName
+ << "' allocated for constraint '" << OpInfo.ConstraintCode
+ << "' does not match required type";
+ }
return true;
}
@@ -10358,8 +10382,10 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
for (Register Reg : OpInfo.AssignedRegs.Regs) {
if (Reg.isPhysical() && TRI.isInlineAsmReadOnlyReg(MF, Reg)) {
- Info.ErrorMsg << "write to reserved register '"
- << TRI.getRegAsmName(Reg) << "'";
+ if (OpInfo.atFinalConstraint()) {
+ StringRef RegName = TRI.getRegAsmName(Reg);
+ Info.ErrorMsg << "write to reserved register '" << RegName << "'";
+ }
return true;
}
}
@@ -10390,8 +10416,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
// C_RegisterClass, and a target-defined fashion for
// C_Immediate/C_Other). Find a register that we can use.
if (OpInfo.AssignedRegs.Regs.empty()) {
- Info.ErrorMsg << "could not allocate output register for "
- << "constraint '" << OpInfo.ConstraintCode << "'";
+ if (OpInfo.atFinalConstraint())
+ Info.ErrorMsg << "could not allocate output register for "
+ << "constraint '" << OpInfo.ConstraintCode << "'";
return true;
}
@@ -10403,7 +10430,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, OpInfo.MayFoldRegister, DL, DAG, Info.AsmNodeOperands);
}
break;
@@ -10420,8 +10447,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {
if (OpInfo.isIndirect) {
// This happens on gcc/testsuite/gcc.dg/pr8788-1.c
- Info.ErrorMsg << "inline asm not supported yet: cannot handle "
- << "tied indirect register inputs";
+ if (OpInfo.atFinalConstraint())
+ Info.ErrorMsg << "inline asm not supported yet: cannot handle "
+ << "tied indirect register inputs";
return true;
}
@@ -10444,9 +10472,9 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
// Use the produced MatchedRegs object to
MatchedRegs.getCopyToRegs(InOperandVal, DAG, DL, Info.Chain,
&Info.Glue, &Call);
- MatchedRegs.AddInlineAsmOperands(InlineAsm::Kind::RegUse, true,
- OpInfo.getMatchedOperand(), DL, DAG,
- Info.AsmNodeOperands);
+ MatchedRegs.AddInlineAsmOperands(
+ InlineAsm::Kind::RegUse, true, OpInfo.getMatchedOperand(),
+ OpInfo.MayFoldRegister, DL, DAG, Info.AsmNodeOperands);
break;
}
@@ -10569,8 +10597,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, OpInfo.MayFoldRegister, DL,
+ DAG, Info.AsmNodeOperands);
break;
}
@@ -10578,16 +10607,29 @@ static bool prepareDAGLevelOperands(ConstraintDecisionInfo &Info,
// Add the clobbered value to the operand list, so that the register
// allocator is aware that the physreg got clobbered.
if (!OpInfo.AssignedRegs.Regs.empty())
- OpInfo.AssignedRegs.AddInlineAsmOperands(
- InlineAsm::Kind::Clobber, false, 0, DL, DAG, Info.AsmNodeOperands);
+ OpInfo.AssignedRegs.AddInlineAsmOperands(InlineAsm::Kind::Clobber,
+ false, 0, false, DL, DAG,
+ Info.AsmNodeOperands);
break;
}
+
+ OpInfo.Finalized = true;
}
return false;
}
-/// DetermineConstraints - Find the constraints to use for inline asm operands.
+/// determineConstraints - ASM operands may have more than one constraint. We
+/// want to choose the "best" constraint for each operand to avoid horrible
+/// code generation---e.g., for "rm" we would like to use "r". This function
+/// tries different constraints in order from best to worst. If a given
+/// constraint isn't possible, e.g., because no registers are available, then
+/// the function returns 'true' and is rerun on the next constraint.
+///
+/// Each operand which has a suitable constraint is marked as "finalized". This
+/// helps reduce the number of times we need to run this function, keeping the
+/// complexity at O(n), where 'n' is the total number of constraints on inputs
+/// and outputs (i.e., for "rm", n == 2).
static bool
determineConstraints(ConstraintDecisionInfo &Info,
TargetLowering::AsmOperandInfoVector &TargetConstraints,
@@ -10652,9 +10694,12 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
"InvokeInst must have an EHPadBB");
ConstraintDecisionInfo Info;
- if (determineConstraints(Info, TargetConstraints, Call, *this, TLI, TM, DAG,
- EHPadBB))
- return emitInlineAsmError(Call, Info.ErrorMsg.str());
+ while (determineConstraints(Info, TargetConstraints, Call, *this, TLI, TM,
+ DAG, EHPadBB)) {
+ if (Info.ErrorMsg.buffer().size() != 0)
+ return emitInlineAsmError(Call, Info.ErrorMsg.str());
+ Info.reset();
+ }
SDValue Glue = Info.Glue;
SDValue Chain = Info.Chain;
@@ -10717,46 +10762,49 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
// Deal with output operands.
for (SDISelAsmOperandInfo &OpInfo : Info.ConstraintOperands) {
- if (OpInfo.Type == InlineAsm::isOutput) {
- SDValue Val;
- // Skip trivial output operands.
- if (OpInfo.AssignedRegs.Regs.empty())
- continue;
+ if (OpInfo.Type != InlineAsm::isOutput)
+ continue;
- switch (OpInfo.ConstraintType) {
- case TargetLowering::C_Register:
- case TargetLowering::C_RegisterClass:
- Val = OpInfo.AssignedRegs.getCopyFromRegs(DAG, FuncInfo, getCurSDLoc(),
- Chain, &Glue, &Call);
- break;
- case TargetLowering::C_Immediate:
- case TargetLowering::C_Other:
- Val = TLI.LowerAsmOutputForConstraint(Chain, Glue, getCurSDLoc(),
- OpInfo, DAG);
- break;
- case TargetLowering::C_Memory:
- break; // Already handled.
- case TargetLowering::C_Address:
- break; // Silence warning.
- case TargetLowering::C_Unknown:
- assert(false && "Unexpected unknown constraint");
- }
+ SDValue Val;
- // Indirect output manifest as stores. Record output chains.
- if (OpInfo.isIndirect) {
- const Value *Ptr = OpInfo.CallOperandVal;
- assert(Ptr && "Expected value CallOperandVal for indirect asm operand");
- SDValue Store = DAG.getStore(Chain, getCurSDLoc(), Val, getValue(Ptr),
- MachinePointerInfo(Ptr));
- OutChains.push_back(Store);
+ // Skip trivial output operands.
+ if (OpInfo.AssignedRegs.Regs.empty())
+ continue;
+
+ switch (OpInfo.ConstraintType) {
+ case TargetLowering::C_Register:
+ case TargetLowering::C_RegisterClass:
+ Val = OpInfo.AssignedRegs.getCopyFromRegs(DAG, FuncInfo, getCurSDLoc(),
+ Chain, &Glue, &Call);
+ break;
+ case TargetLowering::C_Immediate:
+ case TargetLowering::C_Other:
+ Val = TLI.LowerAsmOutputForConstraint(Chain, Glue, getCurSDLoc(), OpInfo,
+ DAG);
+ break;
+ case TargetLowering::C_Memory:
+ break; // Already handled.
+ case TargetLowering::C_Address:
+ break; // Silence warning.
+ case TargetLowering::C_Unknown:
+ assert(false && "Unexpected unknown constraint");
+ }
+
+ // Indirect output manifest as stores. Record output chains.
+ if (OpInfo.isIndirect) {
+ const Value *Ptr = OpInfo.CallOperandVal;
+ assert(Ptr && "Expected value CallOperandVal for indirect asm operand");
+ SDValue Store = DAG.getStore(Chain, getCurSDLoc(), Val, getValue(Ptr),
+ MachinePointerInfo(Ptr));
+ OutChains.push_back(Store);
+ } else {
+ // generate CopyFromRegs to associated registers.
+ assert(!Call.getType()->isVoidTy() && "Bad inline asm!");
+ if (Val.getOpcode() == ISD::MERGE_VALUES) {
+ for (const SDValue &V : Val->op_values())
+ handleRegAssign(V);
} else {
- // generate CopyFromRegs to associated registers.
- assert(!Call.getType()->isVoidTy() && "Bad inline asm!");
- if (Val.getOpcode() == ISD::MERGE_VALUES) {
- for (const SDValue &V : Val->op_values())
- handleRegAssign(V);
- } else
- handleRegAssign(Val);
+ handleRegAssign(Val);
}
}
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
index 21aac333a73cd..a13ad9dd44113 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
@@ -741,6 +741,7 @@ class SelectionDAGBuilder {
SDValue lowerEndEH(SDValue Chain, const InvokeInst *II,
const BasicBlock *EHPadBB, MCSymbol *BeginLabel);
+private:
std::pair<bool, bool> getTargetIntrinsicCallProperties(const CallBase &I);
SmallVector<SDValue, 8> getTargetIntrinsicOperands(
const CallBase &I, bool HasChain, bool OnlyLoad,
@@ -827,8 +828,9 @@ struct RegsForValue {
/// code marker, matching input operand index (if applicable), and includes
/// the number of values added into it.
void AddInlineAsmOperands(InlineAsm::Kind Code, bool HasMatching,
- unsigned MatchingIdx, const SDLoc &dl,
- SelectionDAG &DAG, std::vector<SDValue> &Ops) const;
+ unsigned MatchingIdx, bool MayFoldRegister,
+ const SDLoc &dl, SelectionDAG &DAG,
+ std::vector<SDValue> &Ops) 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 6aaf1bed7aae8..ac2d3f5ba7fd3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -6019,6 +6019,19 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
OpInfo.ConstraintVT = MVT::Other;
+ // Special treatment for all platforms that can fold a register into a
+ // spill. This is used for a register-memory constraint, where we would
+ // vastly prefer to use 'r' over 'm'. The non-fast register allocators are
+ // able to handle the 'r' default by folding. The fast register allocator
+ // needs special handling to convert the instruction to use 'm' instead.
+ //
+ // This also applies to read-write "+rm" constraints (which generate a
+ // direct "=rm" output with a matching tied input). The register allocator
+ // can fold both the output and its tied input to the same memory slot when
+ // under pressure.
+ if (OpInfo.hasRegMemConstraints())
+ OpInfo.MayFoldRegister = true;
+
// Compute the value type for each operand.
switch (OpInfo.Type) {
case InlineAsm::isOutput: {
@@ -6180,31 +6193,6 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
return ConstraintOperands;
}
-/// Return a number indicating our preference for chosing a type of constraint
-/// over another, for the purpose of sorting them. Immediates are almost always
-/// 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.
-/// https://github.com/llvm/llvm-project/issues/20571
-static unsigned getConstraintPiority(TargetLowering::ConstraintType CT) {
- switch (CT) {
- case TargetLowering::C_Immediate:
- case TargetLowering::C_Other:
- return 4;
- case TargetLowering::C_Memory:
- case TargetLowering::C_Address:
- return 3;
- case TargetLowering::C_RegisterClass:
- return 2;
- case TargetLowering::C_Register:
- return 1;
- case TargetLowering::C_Unknown:
- return 0;
- }
- llvm_unreachable("Invalid constraint type");
-}
-
/// Examine constraint type and operand type and determine a weight value.
/// This object must already have been set up with the operand type
/// and the current alternative constraint selected.
@@ -6282,6 +6270,7 @@ TargetLowering::ConstraintWeight
/// operand (e.g. "imr") try to pick the 'best' one.
/// This is somewhat tricky: constraints (TargetLowering::ConstraintType) fall
/// into seven classes:
+///
/// Register -> one specific register
/// RegisterClass -> a group of regs
/// Memory -> memory
@@ -6289,6 +6278,7 @@ TargetLowering::ConstraintWeight
/// Immediate -> immediate values
/// Other -> magic values (such as "Flag Output Operands")
/// Unknown -> something we don't recognize yet and can't handle
+///
/// Ideally, we would pick the most specific constraint possible: if we have
/// something that fits into a register, we would pick it. The problem here
/// is that if we have something that could either be in a register or in
@@ -6310,12 +6300,17 @@ TargetLowering::ConstraintGroup TargetLowering::getConstraintPreferences(
for (StringRef Code : OpInfo.Codes) {
TargetLowering::ConstraintType CType = getConstraintType(Code);
- // Indirect 'other' or 'immediate' constraints are not allowed.
+ // Indirect 'other' or 'immediate' constraints are not allowed for outputs.
if (OpInfo.isIndirect && !(CType == TargetLowering::C_Memory ||
CType == TargetLowering::C_Register ||
CType == TargetLowering::C_RegisterClass))
continue;
+ if (OpInfo.Type == InlineAsm::isOutput &&
+ (CType == TargetLowering::C_Other ||
+ CType == TargetLowering::C_Immediate))
+ continue;
+
// Things with matching constraints can only be registers, per gcc
// documentation. This mainly affects "g" constraints.
if (CType == TargetLowering::C_Memory && OpInfo.hasMatchingInput())
@@ -6324,8 +6319,33 @@ TargetLowering::ConstraintGroup TargetLowering::getConstraintPreferences(
Ret.emplace_back(Code, CType);
}
- llvm::stable_sort(Ret, [](ConstraintPair a, ConstraintPair b) {
- return getConstraintPiority(a.second) > getConstraintPiority(b.second);
+ // Return a number indicating our preference for choosing a type of
+ // constraint over another, for the purpose of sorting them. Immediates are
+ // almost always preferrable (when they can be emitted). A higher return
+ // value means a stronger preference for one constraint type relative to
+ // another.
+ const TargetMachine &TM = getTargetMachine();
+ bool PreferRegs =
+ TM.getOptLevel() != CodeGenOptLevel::None && OpInfo.MayFoldRegister;
+ auto getConstraintPriority = [&](TargetLowering::ConstraintType CT) {
+ switch (CT) {
+ case TargetLowering::C_Immediate:
+ case TargetLowering::C_Other:
+ return 4;
+ case TargetLowering::C_Memory:
+ case TargetLowering::C_Address:
+ return PreferRegs ? 1 : 3;
+ case TargetLowering::C_RegisterClass:
+ return PreferRegs ? 3 : 2;
+ case TargetLowering::C_Register:
+ return PreferRegs ? 2 : 1;
+ case TargetLowering::C_Unknown:
+ return 0;
+ }
+ llvm_unreachable("Invalid constraint type");
+ };
+ llvm::stable_sort(Ret, [&](ConstraintPair a, ConstraintPair b) {
+ return getConstraintPriority(a.second) > getConstraintPriority(b.second);
});
return Ret;
@@ -6356,22 +6376,29 @@ void TargetLowering::ComputeConstraintToUse(AsmOperandInfo &OpInfo,
SelectionDAG *DAG) const {
assert(!OpInfo.Codes.empty() && "Must have at least one constraint");
+ if (OpInfo.atFinalConstraint())
+ return;
+
// Single-letter constraints ('r') are very common.
if (OpInfo.Codes.size() == 1) {
OpInfo.ConstraintCode = OpInfo.Codes[0];
OpInfo.ConstraintType = getConstraintType(OpInfo.ConstraintCode);
- } else {
+ OpInfo.ConstraintIndex = 0;
+ } else if (!OpInfo.Finalized) {
ConstraintGroup G = getConstraintPreferences(OpInfo);
- if (G.empty())
+ if (G.empty()) {
+ OpInfo.ConstraintIndex = OpInfo.Codes.size() - 1;
return;
+ }
- unsigned BestIdx = 0;
+ unsigned BestIdx = OpInfo.ConstraintIndex + 1;
for (const unsigned E = G.size();
BestIdx < E && (G[BestIdx].second == TargetLowering::C_Other ||
G[BestIdx].second == TargetLowering::C_Immediate);
++BestIdx) {
if (lowerImmediateIfPossible(G[BestIdx], Op, DAG, *this))
break;
+
// If we're out of constraints, just pick the first one.
if (BestIdx + 1 == E) {
BestIdx = 0;
@@ -6381,6 +6408,7 @@ void TargetLowering::ComputeConstraintToUse(AsmOperandInfo &OpInfo,
OpInfo.ConstraintCode = G[BestIdx].first;
OpInfo.ConstraintType = G[BestIdx].second;
+ OpInfo.ConstraintIndex = BestIdx;
}
// 'X' matches anything.
diff --git a/llvm/test/CodeGen/X86/asm-constraints-rm-O0.ll b/llvm/test/CodeGen/X86/asm-constraints-rm-O0.ll
new file mode 100644
index 0000000000000..6fc3086378459
--- /dev/null
+++ b/llvm/test/CodeGen/X86/asm-constraints-rm-O0.ll
@@ -0,0 +1,855 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --filter "^\t#" --version 4
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -O0 < %s | FileCheck %s
+
+%struct.foo = type { i32, i32, i32, i32, i32 }
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test1(ptr noundef %foo) {
+; CHECK-LABEL: test1:
+; CHECK: #APP
+; CHECK: # rm input: no pressure
+; CHECK: # -{{[0-9]+}}(%rsp), -{{[0-9]+}}(%rsp), -{{[0-9]+}}(%rsp), -{{[0-9]+}}(%rsp), -{{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load i32, ptr %a, align 4
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load i32, ptr %b, align 4
+ %4 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 2
+ %5 = load i32, ptr %c, align 4
+ %6 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %6, i32 0, i32 3
+ %7 = load i32, ptr %d, align 4
+ %8 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %8, i32 0, i32 4
+ %9 = load i32, ptr %e, align 4
+ call void asm sideeffect "# rm input: no pressure\0A\09# $0, $1, $2, $3, $4", "rm,rm,rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %3, i32 %5, i32 %7, i32 %9)
+ %10 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %10, i32 0, i32 0
+ %11 = load i32, ptr %a1, align 4
+ ret i32 %11
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test2(ptr noundef %foo) {
+; CHECK-LABEL: test2:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm input: pressure
+; CHECK: # {{[0-9]+}}(%rsp), {{[0-9]+}}(%rsp), {{[0-9]+}}(%rsp), {{[0-9]+}}(%rsp), {{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load i32, ptr %a, align 4
+ %3 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 1
+ %4 = load i32, ptr %b, align 4
+ %5 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 2
+ %6 = load i32, ptr %c, align 4
+ %7 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %7, i32 0, i32 3
+ %8 = load i32, ptr %d, align 4
+ %9 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %9, i32 0, i32 4
+ %10 = load i32, ptr %e, align 4
+ call void asm sideeffect "# rm input: pressure\0A\09# $0, $1, $2, $3, $4", "rm,rm,rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(i32 %2, i32 %4, i32 %6, i32 %8, i32 %10)
+ %11 = load i64, ptr %rax, align 8
+ %12 = load i64, ptr %rcx, align 8
+ %13 = load i64, ptr %rdx, align 8
+ %14 = load i64, ptr %rsi, align 8
+ %15 = load i64, ptr %rdi, align 8
+ %16 = load i64, ptr %rbx, align 8
+ %17 = load i64, ptr %rbp, align 8
+ %18 = load i64, ptr %r8, align 8
+ %19 = load i64, ptr %r9, align 8
+ %20 = load i64, ptr %r10, align 8
+ %21 = load i64, ptr %r11, align 8
+ %22 = load i64, ptr %r12, align 8
+ %23 = load i64, ptr %r13, align 8
+ %24 = load i64, ptr %r14, align 8
+ %25 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18, i64 noundef %19, i64 noundef %20, i64 noundef %21, i64 noundef %22, i64 noundef %23, i64 noundef %24, i64 noundef %25)
+ %26 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %26, i32 0, i32 0
+ %27 = load i32, ptr %a15, align 4
+ ret i32 %27
+}
+
+declare void @g(i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef)
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test3(ptr noundef %foo) {
+; CHECK-LABEL: test3:
+; CHECK: #APP
+; CHECK: # rm output: no pressure
+; CHECK: # (%rax), (%rcx), (%rdx), (%rsi), (%rdi)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 1
+ %2 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 2
+ %3 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 3
+ %4 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 4
+ call void asm sideeffect "# rm output: no pressure\0A\09# $0, $1, $2, $3, $4", "=*rm,=*rm,=*rm,=*rm,=*rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, ptr elementtype(i32) %d, ptr elementtype(i32) %e)
+ %5 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 0
+ %6 = load i32, ptr %a1, align 4
+ ret i32 %6
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test4(ptr noundef %foo) {
+; CHECK-LABEL: test4:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm output: pressure
+; CHECK: # (%rax), (%rcx), (%rdx), (%rsi), (%rdi)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 2
+ %4 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 3
+ %5 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 4
+ call void asm sideeffect "# rm output: pressure\0A\09# $0, $1, $2, $3, $4", "=*rm,=*rm,=*rm,=*rm,=*rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, ptr elementtype(i32) %d, ptr elementtype(i32) %e)
+ %6 = load i64, ptr %rax, align 8
+ %7 = load i64, ptr %rcx, align 8
+ %8 = load i64, ptr %rdx, align 8
+ %9 = load i64, ptr %rsi, align 8
+ %10 = load i64, ptr %rdi, align 8
+ %11 = load i64, ptr %rbx, align 8
+ %12 = load i64, ptr %rbp, align 8
+ %13 = load i64, ptr %r8, align 8
+ %14 = load i64, ptr %r9, align 8
+ %15 = load i64, ptr %r10, align 8
+ %16 = load i64, ptr %r11, align 8
+ %17 = load i64, ptr %r12, align 8
+ %18 = load i64, ptr %r13, align 8
+ %19 = load i64, ptr %r14, align 8
+ %20 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %6, i64 noundef %7, i64 noundef %8, i64 noundef %9, i64 noundef %10, i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18, i64 noundef %19, i64 noundef %20)
+ %21 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %21, i32 0, i32 0
+ %22 = load i32, ptr %a15, align 4
+ ret i32 %22
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test5(ptr noundef %foo) {
+; CHECK-LABEL: test5:
+; CHECK: #APP
+; CHECK: # rm tied output: no pressure
+; CHECK: # %ecx, %esi, %r8d, %r10d, %ebx
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load i32, ptr %a, align 4
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load i32, ptr %b, align 4
+ %4 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 2
+ %5 = load i32, ptr %c, align 4
+ %6 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %6, i32 0, i32 3
+ %7 = load i32, ptr %d, align 4
+ %8 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %8, i32 0, i32 4
+ %9 = load i32, ptr %e, align 4
+ call void asm sideeffect "# rm tied output: no pressure\0A\09# $0, $1, $2, $3, $4", "=*rm,=*rm,=*rm,=*rm,=*rm,0,1,2,3,4,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, ptr elementtype(i32) %d, ptr elementtype(i32) %e, i32 %1, i32 %3, i32 %5, i32 %7, i32 %9)
+ %10 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %10, i32 0, i32 0
+ %11 = load i32, ptr %a1, align 4
+ ret i32 %11
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test6(ptr noundef %foo) {
+; CHECK-LABEL: test6:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm tied output: pressure
+; CHECK: # %ecx, %esi, %r8d, %r10d, %ebx
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load i32, ptr %a, align 4
+ %3 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 1
+ %4 = load i32, ptr %b, align 4
+ %5 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 2
+ %6 = load i32, ptr %c, align 4
+ %7 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %7, i32 0, i32 3
+ %8 = load i32, ptr %d, align 4
+ %9 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %9, i32 0, i32 4
+ %10 = load i32, ptr %e, align 4
+ call void asm sideeffect "# rm tied output: pressure\0A\09# $0, $1, $2, $3, $4", "=*rm,=*rm,=*rm,=*rm,=*rm,0,1,2,3,4,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, ptr elementtype(i32) %d, ptr elementtype(i32) %e, i32 %2, i32 %4, i32 %6, i32 %8, i32 %10)
+ %11 = load i64, ptr %rax, align 8
+ %12 = load i64, ptr %rcx, align 8
+ %13 = load i64, ptr %rdx, align 8
+ %14 = load i64, ptr %rsi, align 8
+ %15 = load i64, ptr %rdi, align 8
+ %16 = load i64, ptr %rbx, align 8
+ %17 = load i64, ptr %rbp, align 8
+ %18 = load i64, ptr %r8, align 8
+ %19 = load i64, ptr %r9, align 8
+ %20 = load i64, ptr %r10, align 8
+ %21 = load i64, ptr %r11, align 8
+ %22 = load i64, ptr %r12, align 8
+ %23 = load i64, ptr %r13, align 8
+ %24 = load i64, ptr %r14, align 8
+ %25 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18, i64 noundef %19, i64 noundef %20, i64 noundef %21, i64 noundef %22, i64 noundef %23, i64 noundef %24, i64 noundef %25)
+ %26 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %26, i32 0, i32 0
+ %27 = load i32, ptr %a15, align 4
+ ret i32 %27
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test7(ptr noundef %foo) {
+; CHECK-LABEL: test7:
+; CHECK: #APP
+; CHECK: # rm output, r input: no pressure
+; CHECK: # (%rax), %ecx
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 1
+ %2 = load i32, ptr %b, align 4
+ call void asm sideeffect "# rm output, r input: no pressure\0A\09# $0, $1", "=*rm,r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, i32 %2)
+ %3 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 0
+ %4 = load i32, ptr %a1, align 4
+ ret i32 %4
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test8(ptr noundef %foo) {
+; CHECK-LABEL: test8:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm output, r input: pressure
+; CHECK: # (%rax), %ecx
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load i32, ptr %b, align 4
+ call void asm sideeffect "# rm output, r input: pressure\0A\09# $0, $1", "=*rm,r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, i32 %3)
+ %4 = load i64, ptr %rax, align 8
+ %5 = load i64, ptr %rcx, align 8
+ %6 = load i64, ptr %rdx, align 8
+ %7 = load i64, ptr %rsi, align 8
+ %8 = load i64, ptr %rdi, align 8
+ %9 = load i64, ptr %rbx, align 8
+ %10 = load i64, ptr %rbp, align 8
+ %11 = load i64, ptr %r8, align 8
+ %12 = load i64, ptr %r9, align 8
+ %13 = load i64, ptr %r10, align 8
+ %14 = load i64, ptr %r11, align 8
+ %15 = load i64, ptr %r12, align 8
+ %16 = load i64, ptr %r13, align 8
+ %17 = load i64, ptr %r14, align 8
+ %18 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %4, i64 noundef %5, i64 noundef %6, i64 noundef %7, i64 noundef %8, i64 noundef %9, i64 noundef %10, i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18)
+ %19 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %19, i32 0, i32 0
+ %20 = load i32, ptr %a15, align 4
+ ret i32 %20
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test9(ptr noundef %foo) {
+; CHECK-LABEL: test9:
+; CHECK: #APP
+; CHECK: # m output, rm input: no pressure
+; CHECK: # (%rax), -{{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 1
+ %2 = load i32, ptr %b, align 4
+ call void asm sideeffect "# m output, rm input: no pressure\0A\09# $0, $1", "=*m,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, i32 %2)
+ %3 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 0
+ %4 = load i32, ptr %a1, align 4
+ ret i32 %4
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test10(ptr noundef %foo) {
+; CHECK-LABEL: test10:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # m output, rm input: pressure
+; CHECK: # (%rax), {{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load i32, ptr %b, align 4
+ call void asm sideeffect "# m output, rm input: pressure\0A\09# $0, $1", "=*m,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, i32 %3)
+ %4 = load i64, ptr %rax, align 8
+ %5 = load i64, ptr %rcx, align 8
+ %6 = load i64, ptr %rdx, align 8
+ %7 = load i64, ptr %rsi, align 8
+ %8 = load i64, ptr %rdi, align 8
+ %9 = load i64, ptr %rbx, align 8
+ %10 = load i64, ptr %rbp, align 8
+ %11 = load i64, ptr %r8, align 8
+ %12 = load i64, ptr %r9, align 8
+ %13 = load i64, ptr %r10, align 8
+ %14 = load i64, ptr %r11, align 8
+ %15 = load i64, ptr %r12, align 8
+ %16 = load i64, ptr %r13, align 8
+ %17 = load i64, ptr %r14, align 8
+ %18 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %4, i64 noundef %5, i64 noundef %6, i64 noundef %7, i64 noundef %8, i64 noundef %9, i64 noundef %10, i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18)
+ %19 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %19, i32 0, i32 0
+ %20 = load i32, ptr %a15, align 4
+ ret i32 %20
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test11(ptr noundef %foo) {
+; CHECK-LABEL: test11:
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: no pressure
+; CHECK: # (%rax), (%rcx), (%rdx), (%rsi), (%rdi), -{{[0-9]+}}(%rsp), -{{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 1
+ %2 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 2
+ %3 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 3
+ %4 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 4
+ %5 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 0
+ %6 = load i32, ptr %a1, align 4
+ %7 = load ptr, ptr %foo.addr, align 8
+ %b2 = getelementptr inbounds nuw %struct.foo, ptr %7, i32 0, i32 1
+ %8 = load i32, ptr %b2, align 4
+ call void asm sideeffect "# multiple m output, rm input: no pressure\0A\09# $0, $1, $2, $3, $4, $5, $6", "=*m,=*m,=*m,=*m,=*m,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, ptr elementtype(i32) %d, ptr elementtype(i32) %e, i32 %6, i32 %8)
+ %9 = load ptr, ptr %foo.addr, align 8
+ %a3 = getelementptr inbounds nuw %struct.foo, ptr %9, i32 0, i32 0
+ %10 = load i32, ptr %a3, align 4
+ ret i32 %10
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test12(ptr noundef %foo) {
+; CHECK-LABEL: test12:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: pressure
+; CHECK: # (%rax), (%rcx), (%rdx), (%rsi), (%rdi), {{[0-9]+}}(%rsp), {{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 2
+ %4 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 3
+ %5 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 4
+ %6 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %6, i32 0, i32 0
+ %7 = load i32, ptr %a15, align 4
+ %8 = load ptr, ptr %foo.addr, align 8
+ %b16 = getelementptr inbounds nuw %struct.foo, ptr %8, i32 0, i32 1
+ %9 = load i32, ptr %b16, align 4
+ call void asm sideeffect "# multiple m output, rm input: pressure\0A\09# $0, $1, $2, $3, $4, $5, $6", "=*m,=*m,=*m,=*m,=*m,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, ptr elementtype(i32) %d, ptr elementtype(i32) %e, i32 %7, i32 %9)
+ %10 = load i64, ptr %rax, align 8
+ %11 = load i64, ptr %rcx, align 8
+ %12 = load i64, ptr %rdx, align 8
+ %13 = load i64, ptr %rsi, align 8
+ %14 = load i64, ptr %rdi, align 8
+ %15 = load i64, ptr %rbx, align 8
+ %16 = load i64, ptr %rbp, align 8
+ %17 = load i64, ptr %r8, align 8
+ %18 = load i64, ptr %r9, align 8
+ %19 = load i64, ptr %r10, align 8
+ %20 = load i64, ptr %r11, align 8
+ %21 = load i64, ptr %r12, align 8
+ %22 = load i64, ptr %r13, align 8
+ %23 = load i64, ptr %r14, align 8
+ %24 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %10, i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18, i64 noundef %19, i64 noundef %20, i64 noundef %21, i64 noundef %22, i64 noundef %23, i64 noundef %24)
+ %25 = load ptr, ptr %foo.addr, align 8
+ %a17 = getelementptr inbounds nuw %struct.foo, ptr %25, i32 0, i32 0
+ %26 = load i32, ptr %a17, align 4
+ ret i32 %26
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test13(ptr noundef %foo) {
+; CHECK-LABEL: test13:
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: no pressure
+; CHECK: # (%rax), (%rcx), (%rdx), -{{[0-9]+}}(%rsp), -{{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %0, i32 0, i32 0
+ %1 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 1
+ %2 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 2
+ %3 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 3
+ %4 = load i32, ptr %d, align 4
+ %5 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %5, i32 0, i32 4
+ %6 = load i32, ptr %e, align 4
+ call void asm sideeffect "# multiple m output, rm input: no pressure\0A\09# $0, $1, $2, $3, $4", "=*&rm,=*&rm,=*&rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, i32 %4, i32 %6)
+ %7 = load ptr, ptr %foo.addr, align 8
+ %a1 = getelementptr inbounds nuw %struct.foo, ptr %7, i32 0, i32 0
+ %8 = load i32, ptr %a1, align 4
+ ret i32 %8
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @test14(ptr noundef %foo) {
+; CHECK-LABEL: test14:
+; CHECK: #APP
+; CHECK: # %rax,%rcx,%rdx,%rsi,%rdi,%rbx,%rbp,%r8,%r9,%r10,%r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: pressure
+; CHECK: # (%rax), (%rcx), (%rdx), {{[0-9]+}}(%rsp), {{[0-9]+}}(%rsp)
+; CHECK: #NO_APP
+entry:
+ %foo.addr = alloca ptr, align 8
+ %rax = alloca i64, align 8
+ %rcx = alloca i64, align 8
+ %rdx = alloca i64, align 8
+ %rsi = alloca i64, align 8
+ %rdi = alloca i64, align 8
+ %rbx = alloca i64, align 8
+ %rbp = alloca i64, align 8
+ %r8 = alloca i64, align 8
+ %r9 = alloca i64, align 8
+ %r10 = alloca i64, align 8
+ %r11 = alloca i64, align 8
+ %r12 = alloca i64, align 8
+ %r13 = alloca i64, align 8
+ %r14 = alloca i64, align 8
+ %r15 = alloca i64, align 8
+ store ptr %foo, ptr %foo.addr, align 8
+ %0 = call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ store i64 %asmresult, ptr %rax, align 8
+ store i64 %asmresult1, ptr %rcx, align 8
+ store i64 %asmresult2, ptr %rdx, align 8
+ store i64 %asmresult3, ptr %rsi, align 8
+ store i64 %asmresult4, ptr %rdi, align 8
+ store i64 %asmresult5, ptr %rbx, align 8
+ store i64 %asmresult6, ptr %rbp, align 8
+ store i64 %asmresult7, ptr %r8, align 8
+ store i64 %asmresult8, ptr %r9, align 8
+ store i64 %asmresult9, ptr %r10, align 8
+ store i64 %asmresult10, ptr %r11, align 8
+ store i64 %asmresult11, ptr %r12, align 8
+ store i64 %asmresult12, ptr %r13, align 8
+ store i64 %asmresult13, ptr %r14, align 8
+ store i64 %asmresult14, ptr %r15, align 8
+ %1 = load ptr, ptr %foo.addr, align 8
+ %a = getelementptr inbounds nuw %struct.foo, ptr %1, i32 0, i32 0
+ %2 = load ptr, ptr %foo.addr, align 8
+ %b = getelementptr inbounds nuw %struct.foo, ptr %2, i32 0, i32 1
+ %3 = load ptr, ptr %foo.addr, align 8
+ %c = getelementptr inbounds nuw %struct.foo, ptr %3, i32 0, i32 2
+ %4 = load ptr, ptr %foo.addr, align 8
+ %d = getelementptr inbounds nuw %struct.foo, ptr %4, i32 0, i32 3
+ %5 = load i32, ptr %d, align 4
+ %6 = load ptr, ptr %foo.addr, align 8
+ %e = getelementptr inbounds nuw %struct.foo, ptr %6, i32 0, i32 4
+ %7 = load i32, ptr %e, align 4
+ call void asm sideeffect "# multiple m output, rm input: pressure\0A\09# $0, $1, $2, $3, $4", "=*&rm,=*&rm,=*&rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %a, ptr elementtype(i32) %b, ptr elementtype(i32) %c, i32 %5, i32 %7)
+ %8 = load i64, ptr %rax, align 8
+ %9 = load i64, ptr %rcx, align 8
+ %10 = load i64, ptr %rdx, align 8
+ %11 = load i64, ptr %rsi, align 8
+ %12 = load i64, ptr %rdi, align 8
+ %13 = load i64, ptr %rbx, align 8
+ %14 = load i64, ptr %rbp, align 8
+ %15 = load i64, ptr %r8, align 8
+ %16 = load i64, ptr %r9, align 8
+ %17 = load i64, ptr %r10, align 8
+ %18 = load i64, ptr %r11, align 8
+ %19 = load i64, ptr %r12, align 8
+ %20 = load i64, ptr %r13, align 8
+ %21 = load i64, ptr %r14, align 8
+ %22 = load i64, ptr %r15, align 8
+ call void @g(i64 noundef %8, i64 noundef %9, i64 noundef %10, i64 noundef %11, i64 noundef %12, i64 noundef %13, i64 noundef %14, i64 noundef %15, i64 noundef %16, i64 noundef %17, i64 noundef %18, i64 noundef %19, i64 noundef %20, i64 noundef %21, i64 noundef %22)
+ %23 = load ptr, ptr %foo.addr, align 8
+ %a15 = getelementptr inbounds nuw %struct.foo, ptr %23, i32 0, i32 0
+ %24 = load i32, ptr %a15, align 4
+ ret i32 %24
+}
diff --git a/llvm/test/CodeGen/X86/asm-constraints-rm.ll b/llvm/test/CodeGen/X86/asm-constraints-rm.ll
new file mode 100644
index 0000000000000..74f59291c29f8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/asm-constraints-rm.ll
@@ -0,0 +1,430 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --filter "^\t#" --version 4
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+; The non-fast register allocators should use registers when there isn't
+; register pressure.
+
+define dso_local i32 @test1(ptr noundef readonly captures(none) %foo) local_unnamed_addr {
+; CHECK-LABEL: test1:
+; CHECK: #APP
+; CHECK: # rm input: no pressure
+; CHECK: # %eax, %ecx, %edx, %esi, %r8d
+; CHECK: #NO_APP
+entry:
+ %0 = load i32, ptr %foo, align 4
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %1 = load i32, ptr %b, align 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %2 = load i32, ptr %c, align 4
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %3 = load i32, ptr %d, align 4
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %4 = load i32, ptr %e, align 4
+ tail call void asm sideeffect "# rm input: no pressure\0A\09# $0, $1, $2, $3, $4", "rm,rm,rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4)
+ %5 = load i32, ptr %foo, align 4
+ ret i32 %5
+}
+
+define dso_local i32 @test2(ptr noundef readonly captures(none) %foo) local_unnamed_addr {
+; CHECK-LABEL: test2:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm input: pressure
+; CHECK: # %esi %edi %r8d %r9d %eax
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %1 = load i32, ptr %foo, align 4
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %2 = load i32, ptr %b, align 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %3 = load i32, ptr %c, align 4
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %4 = load i32, ptr %d, align 4
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %5 = load i32, ptr %e, align 4
+ tail call void asm sideeffect "# rm input: pressure\0A\09# $0 $1 $2 $3 $4", "rm,rm,rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5)
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %6 = load i32, ptr %foo, align 4
+ ret i32 %6
+}
+
+define dso_local i32 @test3(ptr noundef writeonly captures(none) initializes((0, 20)) %foo) local_unnamed_addr {
+; CHECK-LABEL: test3:
+; CHECK: #APP
+; CHECK: # rm output: no pressure
+; CHECK: # %eax, %ecx, %edx, %esi, %r8d
+; CHECK: #NO_APP
+entry:
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %0 = tail call { i32, i32, i32, i32, i32 } asm sideeffect "# rm output: no pressure\0A\09# $0, $1, $2, $3, $4", "=rm,=rm,=rm,=rm,=rm,~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i32, i32, i32, i32, i32 } %0, 0
+ %asmresult1 = extractvalue { i32, i32, i32, i32, i32 } %0, 1
+ %asmresult2 = extractvalue { i32, i32, i32, i32, i32 } %0, 2
+ %asmresult3 = extractvalue { i32, i32, i32, i32, i32 } %0, 3
+ %asmresult4 = extractvalue { i32, i32, i32, i32, i32 } %0, 4
+ store i32 %asmresult, ptr %foo, align 4
+ store i32 %asmresult1, ptr %b, align 4
+ store i32 %asmresult2, ptr %c, align 4
+ store i32 %asmresult3, ptr %d, align 4
+ store i32 %asmresult4, ptr %e, align 4
+ ret i32 %asmresult
+}
+
+define dso_local i32 @test4(ptr noundef writeonly captures(none) initializes((0, 20)) %foo) local_unnamed_addr {
+; CHECK-LABEL: test4:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm output: pressure
+; CHECK: # %esi, %edi, %r8d, %r9d, %eax
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %1 = tail call { i32, i32, i32, i32, i32 } asm sideeffect "# rm output: pressure\0A\09# $0, $1, $2, $3, $4", "=rm,=rm,=rm,=rm,=rm,~{dirflag},~{fpsr},~{flags}"()
+ %asmresult15 = extractvalue { i32, i32, i32, i32, i32 } %1, 0
+ %asmresult16 = extractvalue { i32, i32, i32, i32, i32 } %1, 1
+ %asmresult17 = extractvalue { i32, i32, i32, i32, i32 } %1, 2
+ %asmresult18 = extractvalue { i32, i32, i32, i32, i32 } %1, 3
+ %asmresult19 = extractvalue { i32, i32, i32, i32, i32 } %1, 4
+ store i32 %asmresult15, ptr %foo, align 4
+ store i32 %asmresult16, ptr %b, align 4
+ store i32 %asmresult17, ptr %c, align 4
+ store i32 %asmresult18, ptr %d, align 4
+ store i32 %asmresult19, ptr %e, align 4
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %2 = load i32, ptr %foo, align 4
+ ret i32 %2
+}
+
+define dso_local i32 @test5(ptr noundef captures(none) %foo) local_unnamed_addr {
+; CHECK-LABEL: test5:
+; CHECK: #APP
+; CHECK: # rm tied output: no pressure
+; CHECK: # %eax, %ecx, %edx, %esi, %r8d
+; CHECK: #NO_APP
+entry:
+ %0 = load i32, ptr %foo, align 4
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %1 = load i32, ptr %b, align 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %2 = load i32, ptr %c, align 4
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %3 = load i32, ptr %d, align 4
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %4 = load i32, ptr %e, align 4
+ %5 = tail call { i32, i32, i32, i32, i32 } asm sideeffect "# rm tied output: no pressure\0A\09# $0, $1, $2, $3, $4", "=rm,=rm,=rm,=rm,=rm,0,1,2,3,4,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4)
+ %asmresult = extractvalue { i32, i32, i32, i32, i32 } %5, 0
+ %asmresult1 = extractvalue { i32, i32, i32, i32, i32 } %5, 1
+ %asmresult2 = extractvalue { i32, i32, i32, i32, i32 } %5, 2
+ %asmresult3 = extractvalue { i32, i32, i32, i32, i32 } %5, 3
+ %asmresult4 = extractvalue { i32, i32, i32, i32, i32 } %5, 4
+ store i32 %asmresult, ptr %foo, align 4
+ store i32 %asmresult1, ptr %b, align 4
+ store i32 %asmresult2, ptr %c, align 4
+ store i32 %asmresult3, ptr %d, align 4
+ store i32 %asmresult4, ptr %e, align 4
+ ret i32 %asmresult
+}
+
+define dso_local i32 @test6(ptr noundef captures(none) %foo) local_unnamed_addr {
+; CHECK-LABEL: test6:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm tied output: pressure
+; CHECK: # %esi, %edi, %r8d, %r9d, %eax
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %1 = load i32, ptr %foo, align 4
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %2 = load i32, ptr %b, align 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %3 = load i32, ptr %c, align 4
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %4 = load i32, ptr %d, align 4
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %5 = load i32, ptr %e, align 4
+ %6 = tail call { i32, i32, i32, i32, i32 } asm sideeffect "# rm tied output: pressure\0A\09# $0, $1, $2, $3, $4", "=rm,=rm,=rm,=rm,=rm,0,1,2,3,4,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5)
+ %asmresult15 = extractvalue { i32, i32, i32, i32, i32 } %6, 0
+ %asmresult16 = extractvalue { i32, i32, i32, i32, i32 } %6, 1
+ %asmresult17 = extractvalue { i32, i32, i32, i32, i32 } %6, 2
+ %asmresult18 = extractvalue { i32, i32, i32, i32, i32 } %6, 3
+ %asmresult19 = extractvalue { i32, i32, i32, i32, i32 } %6, 4
+ store i32 %asmresult15, ptr %foo, align 4
+ store i32 %asmresult16, ptr %b, align 4
+ store i32 %asmresult17, ptr %c, align 4
+ store i32 %asmresult18, ptr %d, align 4
+ store i32 %asmresult19, ptr %e, align 4
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %7 = load i32, ptr %foo, align 4
+ ret i32 %7
+}
+
+define dso_local i32 @test7(ptr noundef captures(none) initializes((0, 4)) %foo) local_unnamed_addr {
+; CHECK-LABEL: test7:
+; CHECK: #APP
+; CHECK: # rm output, r input: no pressure
+; CHECK: # %eax, %eax
+; CHECK: #NO_APP
+entry:
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %0 = load i32, ptr %b, align 4
+ %1 = tail call i32 asm sideeffect "# rm output, r input: no pressure\0A\09# $0, $1", "=rm,r,~{dirflag},~{fpsr},~{flags}"(i32 %0)
+ store i32 %1, ptr %foo, align 4
+ ret i32 %1
+}
+
+define dso_local i32 @test8(ptr noundef captures(none) initializes((0, 4)) %foo) local_unnamed_addr {
+; CHECK-LABEL: test8:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # rm output, r input: pressure
+; CHECK: # %esi, %esi
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %1 = load i32, ptr %b, align 4
+ %2 = tail call i32 asm sideeffect "# rm output, r input: pressure\0A\09# $0, $1", "=rm,r,~{dirflag},~{fpsr},~{flags}"(i32 %1)
+ store i32 %2, ptr %foo, align 4
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %3 = load i32, ptr %foo, align 4
+ ret i32 %3
+}
+
+define dso_local i32 @test9(ptr noundef %foo) local_unnamed_addr {
+; CHECK-LABEL: test9:
+; CHECK: #APP
+; CHECK: # m output, rm input: no pressure
+; CHECK: # (%rdi), %eax
+; CHECK: #NO_APP
+entry:
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %0 = load i32, ptr %b, align 4
+ tail call void asm sideeffect "# m output, rm input: no pressure\0A\09# $0, $1", "=*m,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %foo, i32 %0)
+ %1 = load i32, ptr %foo, align 4
+ ret i32 %1
+}
+
+define dso_local i32 @test10(ptr noundef %foo) local_unnamed_addr {
+; CHECK-LABEL: test10:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # m output, rm input: pressure
+; CHECK: # (%rbp), %esi
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %1 = load i32, ptr %b, align 4
+ tail call void asm sideeffect "# m output, rm input: pressure\0A\09# $0, $1", "=*m,rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %foo, i32 %1)
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %2 = load i32, ptr %foo, align 4
+ ret i32 %2
+}
+
+define dso_local i32 @test11(ptr noundef %foo) local_unnamed_addr {
+; CHECK-LABEL: test11:
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: no pressure
+; CHECK: # (%rdi), 4(%rdi), 8(%rdi), 12(%rdi), 16(%rdi), %eax, %ecx
+; CHECK: #NO_APP
+entry:
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %0 = load i32, ptr %foo, align 4
+ %1 = load i32, ptr %b, align 4
+ tail call void asm sideeffect "# multiple m output, rm input: no pressure\0A\09# $0, $1, $2, $3, $4, $5, $6", "=*m,=*m,=*m,=*m,=*m,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr nonnull elementtype(i32) %foo, ptr nonnull elementtype(i32) %b, ptr nonnull elementtype(i32) %c, ptr nonnull elementtype(i32) %d, ptr nonnull elementtype(i32) %e, i32 %0, i32 %1)
+ %2 = load i32, ptr %foo, align 4
+ ret i32 %2
+}
+
+define dso_local i32 @test12(ptr noundef %foo) local_unnamed_addr {
+; CHECK-LABEL: test12:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: pressure
+; CHECK: # (%rbp), 4(%rbp), 8(%rbp), 12(%rbp), 16(%rbp), %esi, %edi
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %1 = load i32, ptr %foo, align 4
+ %2 = load i32, ptr %b, align 4
+ tail call void asm sideeffect "# multiple m output, rm input: pressure\0A\09# $0, $1, $2, $3, $4, $5, $6", "=*m,=*m,=*m,=*m,=*m,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr nonnull elementtype(i32) %foo, ptr nonnull elementtype(i32) %b, ptr nonnull elementtype(i32) %c, ptr nonnull elementtype(i32) %d, ptr nonnull elementtype(i32) %e, i32 %1, i32 %2)
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %3 = load i32, ptr %foo, align 4
+ ret i32 %3
+}
+
+define dso_local i32 @test13(ptr noundef %foo) local_unnamed_addr {
+; CHECK-LABEL: test13:
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: no pressure
+; CHECK: # %eax, %esi, %r8d, %r9d, %r10d, %ecx, %edx
+; CHECK: #NO_APP
+entry:
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %0 = load i32, ptr %foo, align 4
+ %1 = load i32, ptr %b, align 4
+ tail call void asm sideeffect "# multiple m output, rm input: no pressure\0A\09# $0, $1, $2, $3, $4, $5, $6", "=*&rm,=*&rm,=*&rm,=*&rm,=*&rm,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr nonnull elementtype(i32) %foo, ptr nonnull elementtype(i32) %b, ptr nonnull elementtype(i32) %c, ptr nonnull elementtype(i32) %d, ptr nonnull elementtype(i32) %e, i32 %0, i32 %1)
+ %2 = load i32, ptr %foo, align 4
+ ret i32 %2
+}
+
+define dso_local i32 @test14(ptr noundef %foo) local_unnamed_addr {
+; CHECK-LABEL: test14:
+; CHECK: #APP
+; CHECK: # %rax, %rcx, %rdx, %rsi, %rdi, %rbx, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
+; CHECK: #NO_APP
+; CHECK: #APP
+; CHECK: # multiple m output, rm input: pressure
+; CHECK: # (%rbp), 4(%rbp), 8(%rbp), 12(%rbp), 16(%rbp), %esi, %edi
+; CHECK: #NO_APP
+entry:
+ %0 = tail call { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } asm sideeffect "# $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14", "={rax},={rcx},={rdx},={rsi},={rdi},={rbx},={rbp},={r8},={r9},={r10},={r11},={r12},={r13},={r14},={r15},~{dirflag},~{fpsr},~{flags}"()
+ %asmresult = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 0
+ %asmresult1 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 1
+ %asmresult2 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 2
+ %asmresult3 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 3
+ %asmresult4 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 4
+ %asmresult5 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 5
+ %asmresult6 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 6
+ %asmresult7 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 7
+ %asmresult8 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 8
+ %asmresult9 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 9
+ %asmresult10 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 10
+ %asmresult11 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 11
+ %asmresult12 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 12
+ %asmresult13 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 13
+ %asmresult14 = extractvalue { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } %0, 14
+ %b = getelementptr inbounds nuw i8, ptr %foo, i64 4
+ %c = getelementptr inbounds nuw i8, ptr %foo, i64 8
+ %d = getelementptr inbounds nuw i8, ptr %foo, i64 12
+ %e = getelementptr inbounds nuw i8, ptr %foo, i64 16
+ %1 = load i32, ptr %foo, align 4
+ %2 = load i32, ptr %b, align 4
+ tail call void asm sideeffect "# multiple m output, rm input: pressure\0A\09# $0, $1, $2, $3, $4, $5, $6", "=*m,=*m,=*m,=*m,=*m,rm,rm,~{dirflag},~{fpsr},~{flags}"(ptr nonnull elementtype(i32) %foo, ptr nonnull elementtype(i32) %b, ptr nonnull elementtype(i32) %c, ptr nonnull elementtype(i32) %d, ptr nonnull elementtype(i32) %e, i32 %1, i32 %2)
+ tail call void @g(i64 noundef %asmresult, i64 noundef %asmresult1, i64 noundef %asmresult2, i64 noundef %asmresult3, i64 noundef %asmresult4, i64 noundef %asmresult5, i64 noundef %asmresult6, i64 noundef %asmresult7, i64 noundef %asmresult8, i64 noundef %asmresult9, i64 noundef %asmresult10, i64 noundef %asmresult11, i64 noundef %asmresult12, i64 noundef %asmresult13, i64 noundef %asmresult14)
+ %3 = load i32, ptr %foo, align 4
+ ret i32 %3
+}
+
+declare void @g(i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef, i64 noundef)
diff --git a/llvm/test/CodeGen/X86/asm-modifier.ll b/llvm/test/CodeGen/X86/asm-modifier.ll
index e1aac95a1ff6a..3b4d9136febc2 100644
--- a/llvm/test/CodeGen/X86/asm-modifier.ll
+++ b/llvm/test/CodeGen/X86/asm-modifier.ll
@@ -89,11 +89,11 @@ define void @test_q() {
; CHECK-LABEL: test_q:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: #APP
-; CHECK-NEXT: #TEST 0
+; CHECK-NEXT: #TEST %{{[re]}}ax
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: ret{{[l|q]}}
entry:
- call void asm sideeffect "#TEST ${0:q}", "=*imr"( ptr elementtype( i64) null )
+ %0 = call i64 asm sideeffect "#TEST ${0:q}", "=imr"()
ret void
}
diff --git a/llvm/test/CodeGen/X86/inline-asm-attributes.ll b/llvm/test/CodeGen/X86/inline-asm-attributes.ll
new file mode 100644
index 0000000000000..8f41dfbbb18a1
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-attributes.ll
@@ -0,0 +1,28 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+define void @test_ret_attr_drop(i32 %x, ptr %p) {
+; CHECK-LABEL: test_ret_attr_drop:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: movl %eax, (%rsi)
+; CHECK-NEXT: retq
+entry:
+ %0 = call range(i32 0, 10) i32 asm sideeffect "# $0, $1", "=rm,rm"(i32 %x)
+ store i32 %0, ptr %p
+ ret void
+}
+
+define i32 @test_ret_attr_keep(i32 %x) {
+; CHECK-LABEL: test_ret_attr_keep:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: retq
+entry:
+ %0 = call range(i32 0, 10) i32 asm "# $0, $1", "=r,rm"(i32 %x)
+ ret i32 %0
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-bundles.ll b/llvm/test/CodeGen/X86/inline-asm-bundles.ll
new file mode 100644
index 0000000000000..55389ff8e53c3
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-bundles.ll
@@ -0,0 +1,14 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+define i32 @test_bundle(i32 %x) {
+; CHECK-LABEL: test_bundle:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: retq
+entry:
+ %0 = call i32 asm sideeffect "# $0, $1", "=r,rm"(i32 %x) [ "bundle"(i32 42) ]
+ ret i32 %0
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-callbase.ll b/llvm/test/CodeGen/X86/inline-asm-callbase.ll
new file mode 100644
index 0000000000000..94a9563d4de3b
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-callbase.ll
@@ -0,0 +1,75 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+declare i32 @__gxx_personality_v0(...)
+
+define i32 @test_invoke_rm(i32 %x) personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: test_invoke_rm:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: .Ltmp0: # EH_LABEL
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: .Ltmp1: # EH_LABEL
+; CHECK-NEXT: # %bb.1: # %normal
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB0_2: # %unwind
+; CHECK-NEXT: pushq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: .Ltmp2: # EH_LABEL
+; CHECK-NEXT: movq %rax, %rdi
+; CHECK-NEXT: callq _Unwind_Resume at PLT
+entry:
+ %0 = invoke i32 asm "# $0, $1", "=r,rm"(i32 %x)
+ to label %normal unwind label %unwind
+
+normal:
+ ret i32 %0
+
+unwind:
+ %1 = landingpad { ptr, i32 }
+ cleanup
+ resume { ptr, i32 } %1
+}
+
+define i32 @test_callbr_rm(i32 %x) {
+; CHECK-LABEL: test_callbr_rm:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: .LBB1_1: # Inline asm indirect target
+; CHECK-NEXT: # %indirect
+; CHECK-NEXT: # Label of block must be emitted
+; CHECK-NEXT: retq
+entry:
+ %0 = callbr i32 asm "# $0, $1", "=r,rm,!i"(i32 %x)
+ to label %normal [label %indirect]
+
+normal:
+ ret i32 %0
+
+indirect:
+ ret i32 %0
+}
+
+define i32 @test_callbr_convert(i32 %x) {
+; CHECK-LABEL: test_callbr_convert:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: .LBB2_1: # Inline asm indirect target
+; CHECK-NEXT: # %indirect
+; CHECK-NEXT: # Label of block must be emitted
+; CHECK-NEXT: retq
+entry:
+ %0 = callbr i32 asm "# $0, $1", "=rm,rm,!i"(i32 %x)
+ to label %normal [label %indirect]
+
+normal:
+ ret i32 %0
+
+indirect:
+ ret i32 %0
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-memory.ll b/llvm/test/CodeGen/X86/inline-asm-memory.ll
new file mode 100644
index 0000000000000..d1313a9834251
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-memory.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 < %s | FileCheck %s
+
+define void @test1(i32 %x) {
+; CHECK-LABEL: test1:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %edi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: retq
+entry:
+ %0 = call i32 asm sideeffect "# $0, $1", "=r,rm,~{dirflag},~{fpsr},~{flags}"(i32 %x)
+ ret void
+}
+
+define void @test2(ptr %p) {
+; CHECK-LABEL: test2:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: movl %eax, (%rdi)
+; CHECK-NEXT: retq
+entry:
+ %0 = call i32 asm sideeffect "# $0", "=rm,~{dirflag},~{fpsr},~{flags}"()
+ store i32 %0, ptr %p
+ ret void
+}
+
+define void @test3(ptr %x_ptr) {
+; CHECK-LABEL: test3:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: movl (%rdi), %eax
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: movl %eax, (%rdi)
+; CHECK-NEXT: retq
+entry:
+ %x = load i32, ptr %x_ptr
+ %0 = call i32 asm sideeffect "# $0", "=rm,0,~{dirflag},~{fpsr},~{flags}"(i32 %x)
+ store i32 %0, ptr %x_ptr
+ ret void
+}
+
+define dso_local i64 @test4() local_unnamed_addr #0 {
+; CHECK-LABEL: test4:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %rax, $256
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: retq
+entry:
+ %0 = tail call i64 asm "# $0, $1", "=r,irm,~{dirflag},~{fpsr},~{flags}"(i32 256)
+ ret i64 %0
+}
+
+; Tied output ("=rm,0", i.e. "+rm") must NOT be converted to indirect ("=*rm").
+; The non-tied input ("rm") in the same asm IS converted to "*rm".
+define void @test5(ptr %x_ptr, i32 %b) {
+; CHECK-LABEL: test5:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: movl (%rdi), %eax
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # %eax, %esi, %eax
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: movl %eax, (%rdi)
+; CHECK-NEXT: retq
+entry:
+ %x = load i32, ptr %x_ptr
+ %0 = call i32 asm sideeffect "# $0, $1, $2", "=rm,rm,0,~{dirflag},~{fpsr},~{flags}"(i32 %b, i32 %x)
+ store i32 %0, ptr %x_ptr
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-rm-no-opt.ll b/llvm/test/CodeGen/X86/inline-asm-rm-no-opt.ll
new file mode 100644
index 0000000000000..55e334888278a
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-rm-no-opt.ll
@@ -0,0 +1,55 @@
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -O0 < %s | FileCheck %s
+
+; CHECK-LABEL: test_1:
+; CHECK: #APP
+; CHECK-NEXT: pushq -16(%{{.*}})
+; CHECK-NEXT: popfq
+; CHECK-NEXT: #NO_APP
+define dso_local void @test_1(i64 noundef %flags) {
+entry:
+ %flags.addr = alloca i64, align 8
+ store i64 %flags, ptr %flags.addr, align 8
+ %0 = load i64, ptr %flags.addr, align 8
+ call void asm sideeffect "push $0 ; popf", "rm,~{dirflag},~{fpsr},~{flags}"(i64 %0)
+ ret void
+}
+
+; CHECK-LABEL: test_2:
+; CHECK: #APP
+; CHECK-NEXT: pushfq
+; CHECK-NEXT: popq -8(%{{.*}})
+; CHECK-NEXT: #NO_APP
+define dso_local i64 @test_2() {
+entry:
+ %out = alloca i64, align 8
+ call void asm sideeffect "pushf ; pop $0", "=*rm,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i64) %out)
+ %0 = load i64, ptr %out, align 8
+ ret i64 %0
+}
+
+; CHECK-LABEL: test_3:
+; CHECK: #APP
+; CHECK-NEXT: pushq -16(%{{.*}})
+; CHECK-NEXT: popfq
+; CHECK-NEXT: #NO_APP
+define dso_local void @test_3(i64 noundef %flags) {
+entry:
+ %flags.addr = alloca i64, align 8
+ store i64 %flags, ptr %flags.addr, align 8
+ %0 = load i64, ptr %flags.addr, align 8
+ call void asm sideeffect "push $0 ; popf", "imr,~{dirflag},~{fpsr},~{flags}"(i64 %0)
+ ret void
+}
+
+; CHECK-LABEL: test_4:
+; CHECK: #APP
+; CHECK-NEXT: pushfq
+; CHECK-NEXT: popq -8(%{{.*}})
+; CHECK-NEXT: #NO_APP
+define dso_local i64 @test_4() {
+entry:
+ %out = alloca i64, align 8
+ call void asm sideeffect "pushf ; pop $0", "=*imr,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i64) %out)
+ %0 = load i64, ptr %out, align 8
+ ret i64 %0
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-rm-opt.ll b/llvm/test/CodeGen/X86/inline-asm-rm-opt.ll
new file mode 100644
index 0000000000000..3a4eb2a5fc22d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-rm-opt.ll
@@ -0,0 +1,45 @@
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+; CHECK-LABEL: test_1:
+; CHECK: #APP
+; CHECK-NEXT: pushq %rdi
+; CHECK-NEXT: popfq
+; CHECK-NEXT: #NO_APP
+define dso_local void @test_1(i64 noundef %flags) local_unnamed_addr {
+entry:
+ tail call void asm sideeffect "push $0 ; popf", "rm,~{dirflag},~{fpsr},~{flags}"(i64 %flags)
+ ret void
+}
+
+; CHECK-LABEL: test_2:
+; CHECK: #APP
+; CHECK-NEXT: pushfq
+; CHECK-NEXT: popq %rax
+; CHECK-NEXT: #NO_APP
+define dso_local i64 @test_2() local_unnamed_addr {
+entry:
+ %0 = tail call i64 asm sideeffect "pushf ; pop $0", "=rm,~{dirflag},~{fpsr},~{flags}"()
+ ret i64 %0
+}
+
+; CHECK-LABEL: test_3:
+; CHECK: #APP
+; CHECK-NEXT: pushq %rdi
+; CHECK-NEXT: popfq
+; CHECK-NEXT: #NO_APP
+define dso_local void @test_3(i64 noundef %flags) local_unnamed_addr {
+entry:
+ tail call void asm sideeffect "push $0 ; popf", "imr,~{dirflag},~{fpsr},~{flags}"(i64 %flags)
+ ret void
+}
+
+; CHECK-LABEL: test_4:
+; CHECK: #APP
+; CHECK-NEXT: pushfq
+; CHECK-NEXT: popq %rax
+; CHECK-NEXT: #NO_APP
+define dso_local i64 @test_4() local_unnamed_addr {
+entry:
+ %0 = tail call i64 asm sideeffect "pushf ; pop $0", "=imr,~{dirflag},~{fpsr},~{flags}"()
+ ret i64 %0
+}
diff --git a/llvm/test/CodeGen/X86/inlineasm-sched-bug.ll b/llvm/test/CodeGen/X86/inlineasm-sched-bug.ll
index be4d1c29332f7..a322bd3003a58 100644
--- a/llvm/test/CodeGen/X86/inlineasm-sched-bug.ll
+++ b/llvm/test/CodeGen/X86/inlineasm-sched-bug.ll
@@ -6,16 +6,13 @@
define i32 @foo(i32 %treemap) nounwind {
; CHECK-LABEL: foo:
; CHECK: # %bb.0: # %entry
-; CHECK-NEXT: pushl %eax
; CHECK-NEXT: movl {{[0-9]+}}(%esp), %eax
; CHECK-NEXT: movl %eax, %ecx
; CHECK-NEXT: negl %ecx
; CHECK-NEXT: andl %eax, %ecx
-; CHECK-NEXT: movl %ecx, (%esp)
; CHECK-NEXT: #APP
-; CHECK-NEXT: bsfl (%esp), %eax
+; CHECK-NEXT: bsfl %ecx, %eax
; CHECK-NEXT: #NO_APP
-; CHECK-NEXT: popl %ecx
; CHECK-NEXT: retl
entry:
%sub = sub i32 0, %treemap
>From 36276d1f946d0863995d449c3c6ed28ad8421254 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Mon, 4 May 2026 08:30:23 -0700
Subject: [PATCH 2/2] Use "!empty()" instead of "size() != 0"
---
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 62b1ecb3b92f1..c8307a6297f23 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10696,7 +10696,7 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
ConstraintDecisionInfo Info;
while (determineConstraints(Info, TargetConstraints, Call, *this, TLI, TM,
DAG, EHPadBB)) {
- if (Info.ErrorMsg.buffer().size() != 0)
+ if (!Info.ErrorMsg.buffer().empty())
return emitInlineAsmError(Call, Info.ErrorMsg.str());
Info.reset();
}
More information about the cfe-commits
mailing list