[llvm] 2523d72 - [ARM] Change register class of tTAILJUMPr's destination (#206763)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 2 02:29:09 PDT 2026
Author: Victor Campos
Date: 2026-07-02T10:29:05+01:00
New Revision: 2523d725d156f50973c4192137ffdd308913c267
URL: https://github.com/llvm/llvm-project/commit/2523d725d156f50973c4192137ffdd308913c267
DIFF: https://github.com/llvm/llvm-project/commit/2523d725d156f50973c4192137ffdd308913c267.diff
LOG: [ARM] Change register class of tTAILJUMPr's destination (#206763)
This patch changes the register class of tTAILJUMPr's destination from
tcGPR to GPR.
tTAILJUMPr is lowered into a BX (branch-and-exchange) with a register
operand. This instruction has no restrictions about its register
operand, therefore the previous limitation to tcGPR was too tight.
The important consequence of this change is in the machine outliner for
ARM. With this change, code sequences that terminate with a tBLXr can be
outlined regardless of the register used as operand. Before, if the
register used was callee saved, that is, not in tcGPR, the outliner gave
up. The outlining process replaces the call (BLX) by a tail call (BX).
This replacement can be problematic if the callee saved registers are
not properly restored before the tail call. However, one important
detail to mention is that tTAILJUMPr is created during pseudo
instruction expansion, which in turn takes place after register
allocation and prologue/epilogue insertion. By this point, the
replacement is safe because the epilogue will already have been properly
laid out.
Assisted-by: codex. It was used to help in the creation of tests.
Added:
Modified:
llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
llvm/lib/Target/ARM/ARMInstrThumb.td
llvm/test/CodeGen/ARM/machine-outliner-thunk-tcgpr.mir
Removed:
################################################################################
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
index 01a1b4288d569..a922024b032db 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
@@ -5694,6 +5694,15 @@ static bool isLRAvailable(const TargetRegisterInfo &TRI,
return !Live;
}
+/// Return true if \p MI is a call instruction that the outliner can rewrite as
+/// a tail call.
+static bool CanTransformInstrIntoTailCall(const MachineInstr &MI) {
+ auto Opcode = MI.getOpcode();
+ return (Opcode == ARM::BL || Opcode == ARM::BLX || Opcode == ARM::BLX_noip ||
+ Opcode == ARM::tBL || Opcode == ARM::tBLXi || Opcode == ARM::tBLXr ||
+ Opcode == ARM::tBLXr_noip);
+}
+
std::optional<std::unique_ptr<outliner::OutlinedFunction>>
ARMBaseInstrInfo::getOutliningCandidateInfo(
const MachineModuleInfo &MMI,
@@ -5788,8 +5797,6 @@ ARMBaseInstrInfo::getOutliningCandidateInfo(
// At this point, we have only "safe" candidates to outline. Figure out
// frame + call instruction information.
- unsigned LastInstrOpcode = RepeatedSequenceLocs[0].back().getOpcode();
-
// Helper lambda which sets call information for every candidate.
auto SetCandidateCallInfo =
[&RepeatedSequenceLocs](unsigned CallID, unsigned NumBytesForCall) {
@@ -5825,13 +5832,7 @@ ARMBaseInstrInfo::getOutliningCandidateInfo(
FrameID = MachineOutlinerTailCall;
NumBytesToCreateFrame = Costs.FrameTailCall;
SetCandidateCallInfo(MachineOutlinerTailCall, Costs.CallTailCall);
- } else if (LastInstrOpcode == ARM::BL || LastInstrOpcode == ARM::BLX ||
- LastInstrOpcode == ARM::BLX_noip || LastInstrOpcode == ARM::tBL ||
- LastInstrOpcode == ARM::tBLXi ||
- ((LastInstrOpcode == ARM::tBLXr ||
- LastInstrOpcode == ARM::tBLXr_noip) &&
- ARM::tcGPRRegClass.contains(
- RepeatedSequenceLocs[0].back().getOperand(2).getReg()))) {
+ } else if (CanTransformInstrIntoTailCall(RepeatedSequenceLocs[0].back())) {
FrameID = MachineOutlinerThunk;
NumBytesToCreateFrame = Costs.FrameThunk;
SetCandidateCallInfo(MachineOutlinerThunk, Costs.CallThunk);
@@ -6171,9 +6172,7 @@ ARMBaseInstrInfo::getOutliningTypeImpl(const MachineModuleInfo &MMI,
// as a tail-call. Explicitly list the call instructions we know about so
// we don't get unexpected results with call pseudo-instructions.
auto UnknownCallOutlineType = outliner::InstrType::Illegal;
- if (Opc == ARM::BL || Opc == ARM::tBL || Opc == ARM::BLX ||
- Opc == ARM::BLX_noip || Opc == ARM::tBLXr || Opc == ARM::tBLXr_noip ||
- Opc == ARM::tBLXi)
+ if (CanTransformInstrIntoTailCall(MI))
UnknownCallOutlineType = outliner::InstrType::LegalTerminator;
if (!Callee)
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb.td b/llvm/lib/Target/ARM/ARMInstrThumb.td
index 1e2e3c30a78f7..c87bdb030f0f1 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb.td
@@ -652,7 +652,7 @@ let isBranch = 1, isTerminator = 1, hasSideEffects = 0 in
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in {
// IOS versions.
let Uses = [SP] in {
- def tTAILJMPr : tPseudoExpand<(outs), (ins tcGPR:$dst),
+ def tTAILJMPr : tPseudoExpand<(outs), (ins GPR:$dst),
4, IIC_Br, [],
(tBX GPR:$dst, (ops 14, zero_reg))>,
Requires<[IsThumb]>, Sched<[WriteBr]>;
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-thunk-tcgpr.mir b/llvm/test/CodeGen/ARM/machine-outliner-thunk-tcgpr.mir
index b2638a7ff300d..a64946b12357a 100644
--- a/llvm/test/CodeGen/ARM/machine-outliner-thunk-tcgpr.mir
+++ b/llvm/test/CodeGen/ARM/machine-outliner-thunk-tcgpr.mir
@@ -1,40 +1,61 @@
# RUN: llc -run-pass=machine-outliner -verify-machineinstrs %s -o - | FileCheck %s
-# CHECK-NOT: tTAILJMPr {{.*}}$r4
--- |
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-none-eabihf"
- define void @test1() #0 { ret void }
- define void @test2() #0 { ret void }
+ define void @callee_saved() #0 { ret void }
+ define void @caller_saved() #0 { ret void }
attributes #0 = { minsize }
...
---
-name: test1
+name: callee_saved
tracksRegLiveness: true
body: |
+ ; CHECK-LABEL: name: callee_saved
+ ; CHECK: tBL 14 /* CC::al */, $noreg, @[[CALLEE_SAVED_OUTLINED:OUTLINED_FUNCTION_[0-9]+]]
+ ; CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @[[CALLEE_SAVED_OUTLINED]]
+ ; CHECK-NEXT: tBX_RET
bb.0:
liveins: $r4, $lr
- $r0, dead $cpsr = tMOVi8 0, 14, $noreg
- $r1, dead $cpsr = tMOVi8 0, 14, $noreg
+
+ $r1 = t2MOVi 0, 14, $noreg, $noreg
+ $r2 = t2MVNi 99, 14, $noreg, $noreg
tBLXr 14, $noreg, $r4, implicit-def dead $lr, implicit $sp
- $r0, dead $cpsr = tMOVi8 0, 14, $noreg
- $r1, dead $cpsr = tMOVi8 0, 14, $noreg
+ $r1 = t2MOVi 0, 14, $noreg, $noreg
+ $r2 = t2MVNi 99, 14, $noreg, $noreg
tBLXr 14, $noreg, $r4, implicit-def dead $lr, implicit $sp
tBX_RET 14, $noreg
...
---
-name: test2
+name: caller_saved
tracksRegLiveness: true
body: |
+ ; CHECK-LABEL: name: caller_saved
+ ; CHECK: tBL 14 /* CC::al */, $noreg, @[[CALLER_SAVED_OUTLINED:OUTLINED_FUNCTION_[0-9]+]]
+ ; CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @[[CALLER_SAVED_OUTLINED]]
+ ; CHECK-NEXT: tBX_RET
bb.0:
- liveins: $r4, $lr
- $r0, dead $cpsr = tMOVi8 0, 14, $noreg
- $r1, dead $cpsr = tMOVi8 0, 14, $noreg
- tBLXr 14, $noreg, $r4, implicit-def dead $lr, implicit $sp
- $r0, dead $cpsr = tMOVi8 0, 14, $noreg
- $r1, dead $cpsr = tMOVi8 0, 14, $noreg
- tBLXr 14, $noreg, $r4, implicit-def dead $lr, implicit $sp
+ liveins: $r0, $lr
+
+ $r1 = t2MOVi 13, 14, $noreg, $noreg
+ $r2 = t2MVNi 88, 14, $noreg, $noreg
+ tBLXr 14, $noreg, $r0, implicit-def dead $lr, implicit $sp
+ $r1 = t2MOVi 13, 14, $noreg, $noreg
+ $r2 = t2MVNi 88, 14, $noreg, $noreg
+ tBLXr 14, $noreg, $r0, implicit-def dead $lr, implicit $sp
tBX_RET 14, $noreg
-...
\ No newline at end of file
+...
+
+# CHECK: name: [[CALLER_SAVED_OUTLINED]]
+# CHECK: $r1 = t2MOVi 13
+# CHECK-NEXT: $r2 = t2MVNi 88
+# CHECK-NOT: tBLXr
+# CHECK: tTAILJMPr $r0
+
+# CHECK: name: [[CALLEE_SAVED_OUTLINED]]
+# CHECK: $r1 = t2MOVi 0
+# CHECK-NEXT: $r2 = t2MVNi 99
+# CHECK-NOT: tBLXr
+# CHECK: tTAILJMPr $r4
More information about the llvm-commits
mailing list