[llvm] [SPARC] Implement TargetInstrInfo::insertIndirectBranch() (PR #226678)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 08:54:43 PDT 2026
https://github.com/koachan updated https://github.com/llvm/llvm-project/pull/226678
>From a8cc65dc20f46ab6f0c4791dedd81231691ddede Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Sat, 26 Sep 2026 18:18:16 +0700
Subject: [PATCH 1/4] [SPARC] Implement TargetInstrInfo::insertIndirectBranch()
Implement `insertIndirectBranch` so that branch relaxation pass can insert
unconditional long-range branching.
Follow an approach similar to the one used in AVR and use the CALL instruction
to get a reach of 32-bit signed offset instead of emitting actual indirect
branches.
---
llvm/lib/Target/Sparc/SparcInstrInfo.cpp | 74 +++++++++++++++++++++++
llvm/lib/Target/Sparc/SparcInstrInfo.h | 5 ++
llvm/test/CodeGen/SPARC/branches-relax.ll | 39 ++++++++++++
3 files changed, 118 insertions(+)
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
index 3f6023b8c0706b..9edb248986e6db 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
@@ -11,14 +11,19 @@
//===----------------------------------------------------------------------===//
#include "SparcInstrInfo.h"
+#include "MCTargetDesc/SparcMCTargetDesc.h"
#include "Sparc.h"
#include "SparcMachineFunctionInfo.h"
+#include "SparcRegisterInfo.h"
#include "SparcSubtarget.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/RegisterScavenging.h"
+#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
@@ -399,6 +404,75 @@ unsigned SparcInstrInfo::insertBranch(MachineBasicBlock &MBB,
return 2;
}
+void SparcInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
+ MachineBasicBlock &NewDestBB,
+ MachineBasicBlock &RestoreBB,
+ const DebugLoc &DL, int64_t BrOffset,
+ RegScavenger *RS) const {
+ assert(RS && "RegScavenger required for long branching");
+ assert(MBB.empty() &&
+ "new block should be inserted for expanding unconditional branch");
+ assert(MBB.pred_size() == 1);
+ assert(RestoreBB.empty() &&
+ "restore block should be inserted for restoring clobbered registers");
+
+ MachineFunction *MF = MBB.getParent();
+ MachineFrameInfo &MFI = MF->getFrameInfo();
+
+ if (!isInt<32>(BrOffset))
+ report_fatal_error(
+ "Branch offsets outside of the signed 32-bit range not supported");
+ RS->enterBasicBlockEnd(MBB);
+
+ // A SPARC CALL instruction already has signed 32-bit range, so we'll use it
+ // instead of actually emitting an indirect branch.
+ //
+ // Note that using CALLs to do unconditional long-range branch in this manner
+ // should only be done if the target is out of range of a regular BA
+ // instruction, given that CALLS will always clobber %o7 and interfere with
+ // the processor's predictor machinery.
+ bool IsO7Used = RS->isRegUsed(SP::O7);
+ Register ScratchReg = RS->FindUnusedReg(&SP::IntRegsRegClass);
+ int FI = 0;
+ if (IsO7Used) {
+ if (ScratchReg.isValid()) {
+ BuildMI(&MBB, DL, get(SP::ORrr), ScratchReg)
+ .addReg(SP::G0)
+ .addReg(SP::O7, RegState::Kill);
+ RS->setRegUsed(ScratchReg);
+ } else {
+ // In case we run out of usable registers we'll spill it into the stack.
+ const MCRegisterClass &RC =
+ Subtarget.is64Bit() ? SP::I64RegsRegClass : SP::IntRegsRegClass;
+ unsigned SpillSize = TRI.getSpillSize(RC);
+ Align SpillAlign = TRI.getSpillAlign(RC);
+
+ FI = MFI.CreateStackObject(SpillSize, SpillAlign, true);
+
+ MachineBasicBlock::iterator I = MBB.end();
+ storeRegToStackSlot(MBB, I, SP::O7, true, FI, &RC, Register());
+ TRI.eliminateFrameIndex(std::prev(I), 0, 0, RS);
+ }
+ }
+
+ BuildMI(&MBB, DL, get(SP::CALL)).addMBB(IsO7Used ? &RestoreBB : &NewDestBB);
+
+ if (IsO7Used) {
+ if (ScratchReg.isValid()) {
+ BuildMI(&RestoreBB, DL, get(SP::ORrr), SP::O7)
+ .addReg(SP::G0)
+ .addReg(ScratchReg, RegState::Kill);
+ } else {
+ const MCRegisterClass &RC =
+ Subtarget.is64Bit() ? SP::I64RegsRegClass : SP::IntRegsRegClass;
+
+ MachineBasicBlock::iterator I = RestoreBB.begin();
+ loadRegFromStackSlot(RestoreBB, I, SP::O7, FI, &RC, Register());
+ TRI.eliminateFrameIndex(std::prev(I), 0, 1, RS);
+ }
+ }
+}
+
unsigned SparcInstrInfo::removeBranch(MachineBasicBlock &MBB,
int *BytesRemoved) const {
MachineBasicBlock::iterator I = MBB.end();
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.h b/llvm/lib/Target/Sparc/SparcInstrInfo.h
index a719391559ebaf..a6b32df23062f6 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.h
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.h
@@ -82,6 +82,11 @@ class SparcInstrInfo : public SparcGenInstrInfo {
const DebugLoc &DL,
int *BytesAdded = nullptr) const override;
+ void insertIndirectBranch(MachineBasicBlock &MBB,
+ MachineBasicBlock &NewDestBB,
+ MachineBasicBlock &RestoreBB, const DebugLoc &DL,
+ int64_t BrOffset, RegScavenger *RS) const override;
+
bool
reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override;
diff --git a/llvm/test/CodeGen/SPARC/branches-relax.ll b/llvm/test/CodeGen/SPARC/branches-relax.ll
index 334a7ebfb67394..8d8d30da952bd5 100644
--- a/llvm/test/CodeGen/SPARC/branches-relax.ll
+++ b/llvm/test/CodeGen/SPARC/branches-relax.ll
@@ -162,3 +162,42 @@ false:
call void asm sideeffect "nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop", ""()
ret float 0.0
}
+
+define i32 @branch_relax_large(i32 %in) {
+; SPARC64-LABEL: branch_relax_large:
+; SPARC64: .cfi_startproc
+; SPARC64-NEXT: ! %bb.0:
+; SPARC64-NEXT: save %sp, -128, %sp
+; SPARC64-NEXT: .cfi_def_cfa_register %fp
+; SPARC64-NEXT: .cfi_window_save
+; SPARC64-NEXT: .cfi_register %o7, %i7
+; SPARC64-NEXT: cmp %i0, 0
+; SPARC64-NEXT: bne %icc, .LBB3_1
+; SPARC64-NEXT: nop
+; SPARC64-NEXT: ! %bb.3:
+; SPARC64-NEXT: call .LBB3_2
+; SPARC64-NEXT: nop
+; SPARC64-NEXT: .LBB3_1: ! %false
+; SPARC64-NEXT: !APP
+; SPARC64-NEXT: .skip 268435456
+; SPARC64-NEXT: !NO_APP
+; SPARC64-NEXT: ret
+; SPARC64-NEXT: restore %g0, %g0, %o0
+; SPARC64-NEXT: .LBB3_2: ! %true
+; SPARC64-NEXT: mov 4, %i0
+; SPARC64-NEXT: !APP
+; SPARC64-NEXT: .skip 268435456
+; SPARC64-NEXT: !NO_APP
+; SPARC64-NEXT: ret
+; SPARC64-NEXT: restore
+ %tst = icmp eq i32 %in, 0
+ br i1 %tst, label %true, label %false
+
+true:
+ call void asm sideeffect ".space 268435456", ""()
+ ret i32 4
+
+false:
+ call void asm sideeffect ".space 268435456", ""()
+ ret i32 0
+}
>From 3a9eb70fa37f00723829622f9b8eae22665d4236 Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Sat, 26 Sep 2026 18:25:37 +0700
Subject: [PATCH 2/4] Remove stray includes
---
llvm/lib/Target/Sparc/SparcInstrInfo.cpp | 4 ----
1 file changed, 4 deletions(-)
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
index 9edb248986e6db..021d91d9593436 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
@@ -11,19 +11,15 @@
//===----------------------------------------------------------------------===//
#include "SparcInstrInfo.h"
-#include "MCTargetDesc/SparcMCTargetDesc.h"
#include "Sparc.h"
#include "SparcMachineFunctionInfo.h"
-#include "SparcRegisterInfo.h"
#include "SparcSubtarget.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
-#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
>From 2d55fde910f83c900785541238f7cf43afd4899b Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Sat, 26 Sep 2026 20:25:23 +0700
Subject: [PATCH 3/4] wqAdd 32-bit test run
---
llvm/test/CodeGen/SPARC/branches-relax.ll | 152 ++++++++++++++++++++++
1 file changed, 152 insertions(+)
diff --git a/llvm/test/CodeGen/SPARC/branches-relax.ll b/llvm/test/CodeGen/SPARC/branches-relax.ll
index 8d8d30da952bd5..9b0b0aeff96528 100644
--- a/llvm/test/CodeGen/SPARC/branches-relax.ll
+++ b/llvm/test/CodeGen/SPARC/branches-relax.ll
@@ -1,7 +1,49 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=sparc | FileCheck --check-prefix=SPARC %s
; RUN: llc < %s -mtriple=sparc64 -sparc-bpcc-offset-bits=4 -sparc-bpr-offset-bits=4 | FileCheck --check-prefix=SPARC64 %s
+;; Note that 32-bit SPARC branches all have +- 8 MiB range (i.e. same with `ba`)
+;; so for it only @branch_relax_large test is really relevant.
+
define i32 @branch_relax_int(i32 %in) {
+; SPARC-LABEL: branch_relax_int:
+; SPARC: .cfi_startproc
+; SPARC-NEXT: ! %bb.0:
+; SPARC-NEXT: save %sp, -96, %sp
+; SPARC-NEXT: .cfi_def_cfa_register %fp
+; SPARC-NEXT: .cfi_window_save
+; SPARC-NEXT: .cfi_register %o7, %i7
+; SPARC-NEXT: cmp %i0, 0
+; SPARC-NEXT: be .LBB0_2
+; SPARC-NEXT: nop
+; SPARC-NEXT: ! %bb.1: ! %false
+; SPARC-NEXT: !APP
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore %g0, %g0, %o0
+; SPARC-NEXT: .LBB0_2: ! %true
+; SPARC-NEXT: mov 4, %i0
+; SPARC-NEXT: !APP
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore
+;
; SPARC64-LABEL: branch_relax_int:
; SPARC64: .cfi_startproc
; SPARC64-NEXT: ! %bb.0:
@@ -54,6 +96,45 @@ false:
}
define i64 @branch_relax_reg(i64 %in) {
+; SPARC-LABEL: branch_relax_reg:
+; SPARC: .cfi_startproc
+; SPARC-NEXT: ! %bb.0:
+; SPARC-NEXT: save %sp, -96, %sp
+; SPARC-NEXT: .cfi_def_cfa_register %fp
+; SPARC-NEXT: .cfi_window_save
+; SPARC-NEXT: .cfi_register %o7, %i7
+; SPARC-NEXT: orcc %i1, %i0, %g0
+; SPARC-NEXT: be .LBB1_2
+; SPARC-NEXT: nop
+; SPARC-NEXT: ! %bb.1: ! %false
+; SPARC-NEXT: !APP
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: mov %g0, %i0
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore %g0, %g0, %o1
+; SPARC-NEXT: .LBB1_2: ! %true
+; SPARC-NEXT: mov 4, %i1
+; SPARC-NEXT: !APP
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore %g0, %g0, %o0
+;
; SPARC64-LABEL: branch_relax_reg:
; SPARC64: .cfi_startproc
; SPARC64-NEXT: ! %bb.0:
@@ -105,6 +186,50 @@ false:
}
define float @branch_relax_float(float %in) {
+; SPARC-LABEL: branch_relax_float:
+; SPARC: .cfi_startproc
+; SPARC-NEXT: ! %bb.0:
+; SPARC-NEXT: save %sp, -96, %sp
+; SPARC-NEXT: .cfi_def_cfa_register %fp
+; SPARC-NEXT: .cfi_window_save
+; SPARC-NEXT: .cfi_register %o7, %i7
+; SPARC-NEXT: st %i0, [%fp+-4]
+; SPARC-NEXT: ld [%fp+-4], %f1
+; SPARC-NEXT: sethi %hi(.LCPI2_0), %i0
+; SPARC-NEXT: ld [%i0+%lo(.LCPI2_0)], %f0
+; SPARC-NEXT: fcmps %f1, %f0
+; SPARC-NEXT: nop
+; SPARC-NEXT: fbne .LBB2_2
+; SPARC-NEXT: nop
+; SPARC-NEXT: ! %bb.1: ! %true
+; SPARC-NEXT: sethi %hi(.LCPI2_1), %i0
+; SPARC-NEXT: ld [%i0+%lo(.LCPI2_1)], %f0
+; SPARC-NEXT: !APP
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore
+; SPARC-NEXT: .LBB2_2: ! %false
+; SPARC-NEXT: !APP
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: nop
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore
+;
; SPARC64-LABEL: branch_relax_float:
; SPARC64: .cfi_startproc
; SPARC64-NEXT: ! %bb.0:
@@ -164,6 +289,33 @@ false:
}
define i32 @branch_relax_large(i32 %in) {
+; SPARC-LABEL: branch_relax_large:
+; SPARC: .cfi_startproc
+; SPARC-NEXT: ! %bb.0:
+; SPARC-NEXT: save %sp, -96, %sp
+; SPARC-NEXT: .cfi_def_cfa_register %fp
+; SPARC-NEXT: .cfi_window_save
+; SPARC-NEXT: .cfi_register %o7, %i7
+; SPARC-NEXT: cmp %i0, 0
+; SPARC-NEXT: bne .LBB3_1
+; SPARC-NEXT: nop
+; SPARC-NEXT: ! %bb.3:
+; SPARC-NEXT: call .LBB3_2
+; SPARC-NEXT: nop
+; SPARC-NEXT: .LBB3_1: ! %false
+; SPARC-NEXT: !APP
+; SPARC-NEXT: .skip 268435456
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore %g0, %g0, %o0
+; SPARC-NEXT: .LBB3_2: ! %true
+; SPARC-NEXT: mov 4, %i0
+; SPARC-NEXT: !APP
+; SPARC-NEXT: .skip 268435456
+; SPARC-NEXT: !NO_APP
+; SPARC-NEXT: ret
+; SPARC-NEXT: restore
+;
; SPARC64-LABEL: branch_relax_large:
; SPARC64: .cfi_startproc
; SPARC64-NEXT: ! %bb.0:
>From 936e3a42db9c88a8fd5e4580cf2bc9875687561c Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Wed, 30 Sep 2026 22:27:28 +0700
Subject: [PATCH 4/4] Add special form of CALL for use as longer BA
---
llvm/lib/Target/Sparc/SparcInstrInfo.cpp | 3 ++-
llvm/lib/Target/Sparc/SparcInstrInfo.td | 12 ++++++++++++
llvm/test/CodeGen/SPARC/branches-relax.ll | 4 ++--
3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
index 021d91d9593436..7d38d47ce6a6ca 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
@@ -451,7 +451,8 @@ void SparcInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
}
}
- BuildMI(&MBB, DL, get(SP::CALL)).addMBB(IsO7Used ? &RestoreBB : &NewDestBB);
+ BuildMI(&MBB, DL, get(SP::CALL_LONGJUMP))
+ .addMBB(IsO7Used ? &RestoreBB : &NewDestBB);
if (IsO7Used) {
if (ScratchReg.isValid()) {
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td
index 99cb89ea342e8c..8aec6884a8ec5b 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.td
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td
@@ -1128,6 +1128,18 @@ let Uses = [O6], Defs = [O7],
let Inst{29-0} = disp;
}
+ // This is for using the CALL instruction as a longer unconditional branch.
+ let isCall = 0, isCodeGenOnly = 1, isTerminator = 1 in {
+ def CALL_LONGJUMP : InstSP<(outs), (ins calltarget:$disp, variable_ops),
+ "call $disp",
+ [],
+ IIC_jmp_or_call> {
+ bits<30> disp;
+ let op = 1;
+ let Inst{29-0} = disp;
+ }
+ }
+
// call with trailing imm argument.
// The imm argument is discarded.
let isAsmParserOnly = 1 in {
diff --git a/llvm/test/CodeGen/SPARC/branches-relax.ll b/llvm/test/CodeGen/SPARC/branches-relax.ll
index 9b0b0aeff96528..9fa33b24e3e257 100644
--- a/llvm/test/CodeGen/SPARC/branches-relax.ll
+++ b/llvm/test/CodeGen/SPARC/branches-relax.ll
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=sparc | FileCheck --check-prefix=SPARC %s
-; RUN: llc < %s -mtriple=sparc64 -sparc-bpcc-offset-bits=4 -sparc-bpr-offset-bits=4 | FileCheck --check-prefix=SPARC64 %s
+; RUN: llc < %s -mtriple=sparc -verify-machineinstrs | FileCheck --check-prefix=SPARC %s
+; RUN: llc < %s -mtriple=sparc64 -verify-machineinstrs -sparc-bpcc-offset-bits=4 -sparc-bpr-offset-bits=4 | FileCheck --check-prefix=SPARC64 %s
;; Note that 32-bit SPARC branches all have +- 8 MiB range (i.e. same with `ba`)
;; so for it only @branch_relax_large test is really relevant.
More information about the llvm-commits
mailing list