[llvm] [SPARC] Implement TargetInstrInfo::insertIndirectBranch() (PR #226678)

via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 11 08:03:37 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/6] [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 3f6023b8c0706..9edb248986e6d 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 a719391559eba..a6b32df23062f 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 334a7ebfb6739..8d8d30da952bd 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/6] 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 9edb248986e6d..021d91d959343 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 37898a3435b2594cbd2687dd8bcf80962a8254fb 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/6] Add 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 8d8d30da952bd..9b0b0aeff9652 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 6d97b0cbf5a55976eac225ce7ee839487633331c 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/6] 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 021d91d959343..7d38d47ce6a6c 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 99cb89ea342e8..8aec6884a8ec5 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 9b0b0aeff9652..9fa33b24e3e25 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.

>From 105f944e1d38f5038f81fb8167cb392b0961dfb3 Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Fri, 2 Oct 2026 20:34:37 +0700
Subject: [PATCH 5/6] Remove scheduling class from CALL_LONGJUMP

---
 llvm/lib/Target/Sparc/SparcInstrInfo.td | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td
index 8aec6884a8ec5..fabd220197649 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.td
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td
@@ -1131,9 +1131,7 @@ let Uses = [O6], Defs = [O7],
   // 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> {
+                    "call $disp", []> {
       bits<30> disp;
       let op = 1;
       let Inst{29-0} = disp;

>From 186491d9361aa04f12a7ccc0fd9176779af7abfb Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Sun, 11 Oct 2026 22:03:11 +0700
Subject: [PATCH 6/6] Refactor and add tests for %o7 used case

---
 llvm/lib/Target/Sparc/SparcInstrInfo.cpp  |   8 +-
 llvm/lib/Target/Sparc/SparcInstrInfo.td   |  21 +--
 llvm/test/CodeGen/SPARC/branches-relax.ll | 177 +++++++++++++++++++++-
 3 files changed, 193 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
index 7d38d47ce6a6c..3af592f1ec9e0 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp
@@ -273,7 +273,8 @@ SparcInstrInfo::getBranchDestBlock(const MachineInstr &MI) const {
   case SP::BPRA:
   case SP::BPRNT:
   case SP::BPRANT:
-      return MI.getOperand(0).getMBB();
+  case SP::CALL_LONGJUMP:
+    return MI.getOperand(0).getMBB();
   }
 }
 
@@ -408,7 +409,7 @@ void SparcInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
   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(MBB.pred_size() == 1 && "should only have a single predecessor");
   assert(RestoreBB.empty() &&
          "restore block should be inserted for restoring clobbered registers");
 
@@ -508,6 +509,9 @@ bool SparcInstrInfo::isBranchOffsetInRange(unsigned BranchOpc,
                                            int64_t Offset) const {
   assert((Offset & 0b11) == 0 && "Malformed branch offset");
   switch (BranchOpc) {
+  case SP::CALL_LONGJUMP:
+    return isIntN(30, Offset >> 2);
+
   case SP::BA:
   case SP::BCOND:
   case SP::BCONDA:
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td
index fabd220197649..40518a0cab15c 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.td
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td
@@ -1019,6 +1019,17 @@ multiclass IPredBranch<string regstr, list<dag> CCPattern> {
 } // let isBranch = 1, isTerminator = 1, hasDelaySlot = 1
 
 
+// This is for using the CALL instruction as a longer unconditional branch.
+let Defs = [O7], isBranch = 1, isCall = 0, isCodeGenOnly = 1,
+  isTerminator = 1, hasDelaySlot = 1, isBarrier = 1 in {
+  def CALL_LONGJUMP : InstSP<(outs), (ins calltarget:$disp, variable_ops),
+                  "call $disp", []> {
+    bits<30> disp;
+    let op = 1;
+    let Inst{29-0} = disp;
+  }
+}
+
 // Indirect branch instructions.
 let isTerminator = 1, isBarrier = 1,  hasDelaySlot = 1, isBranch =1,
      isIndirectBranch = 1, rd = 0, isCodeGenOnly = 1 in {
@@ -1128,16 +1139,6 @@ 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", []> {
-      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 9fa33b24e3e25..aaa0aedeb5f45 100644
--- a/llvm/test/CodeGen/SPARC/branches-relax.ll
+++ b/llvm/test/CodeGen/SPARC/branches-relax.ll
@@ -3,7 +3,7 @@
 ; 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.
+;; so for it only @branch_relax_large* test is really relevant.
 
 define i32 @branch_relax_int(i32 %in) {
 ; SPARC-LABEL: branch_relax_int:
@@ -353,3 +353,178 @@ false:
   call void asm sideeffect ".space 268435456", ""()
   ret i32 0
 }
+
+define i32 @branch_relax_large_o7_used(i32 %in) {
+; SPARC-LABEL: branch_relax_large_o7_used:
+; 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:    !APP
+; SPARC-NEXT:  .Ltmp0:
+; SPARC-NEXT:    call .Ltmp0+8
+; SPARC-NEXT:    nop
+; SPARC-NEXT:    !NO_APP
+; SPARC-NEXT:    bne .LBB4_1
+; SPARC-NEXT:    nop
+; SPARC-NEXT:  ! %bb.3:
+; SPARC-NEXT:    mov %o7, %i0
+; SPARC-NEXT:    call .LBB4_4
+; SPARC-NEXT:    nop
+; SPARC-NEXT:  .LBB4_1: ! %false
+; SPARC-NEXT:    !APP
+; SPARC-NEXT:    .skip 268435456
+; SPARC-NEXT:    !NO_APP
+; SPARC-NEXT:    ret
+; SPARC-NEXT:    restore %g0, %g0, %o0
+; SPARC-NEXT:  .LBB4_4: ! %true
+; SPARC-NEXT:    mov %i0, %o7
+; SPARC-NEXT:  ! %bb.2: ! %true
+; SPARC-NEXT:    !APP
+; SPARC-NEXT:    .skip 268435456
+; SPARC-NEXT:    !NO_APP
+; SPARC-NEXT:    ret
+; SPARC-NEXT:    restore %g0, %o7, %o0
+;
+; SPARC64-LABEL: branch_relax_large_o7_used:
+; 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:    !APP
+; SPARC64-NEXT:  .Ltmp0:
+; SPARC64-NEXT:    call .Ltmp0+8
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:    !NO_APP
+; SPARC64-NEXT:    bne %icc, .LBB4_1
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:  ! %bb.3:
+; SPARC64-NEXT:    mov %o7, %i0
+; SPARC64-NEXT:    call .LBB4_4
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:  .LBB4_1: ! %false
+; SPARC64-NEXT:    !APP
+; SPARC64-NEXT:    .skip 268435456
+; SPARC64-NEXT:    !NO_APP
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore %g0, %g0, %o0
+; SPARC64-NEXT:  .LBB4_4: ! %true
+; SPARC64-NEXT:    mov %i0, %o7
+; SPARC64-NEXT:  ! %bb.2: ! %true
+; SPARC64-NEXT:    !APP
+; SPARC64-NEXT:    .skip 268435456
+; SPARC64-NEXT:    !NO_APP
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore %g0, %o7, %o0
+  %tst = icmp eq i32 %in, 0
+  %ret = call i32 asm sideeffect "call .+8\0Anop", "={o7}"()
+  br i1 %tst, label %true, label %false
+
+true:
+  call void asm sideeffect ".space 268435456", ""()
+  ret i32 %ret
+
+false:
+  call void asm sideeffect ".space 268435456", ""()
+  ret i32 0
+}
+
+;; For these we reserve everything except %o7 to make sure it is spilled.
+; RUN: llc < %s -mtriple=sparc -mattr=+reserve-g1 -mattr=+reserve-g2 -mattr=+reserve-g3 -mattr=+reserve-g4 -mattr=+reserve-g5 -mattr=+reserve-g6 -mattr=+reserve-g7 -mattr=+reserve-o0 -mattr=+reserve-o1 -mattr=+reserve-o2 -mattr=+reserve-o3 -mattr=+reserve-o4 -mattr=+reserve-o5 -mattr=+reserve-l1 -mattr=+reserve-l2 -mattr=+reserve-l3 -mattr=+reserve-l4 -mattr=+reserve-l5 -mattr=+reserve-l6 -mattr=+reserve-l7 -mattr=+reserve-l0 -mattr=+reserve-i0 -mattr=+reserve-i1 -mattr=+reserve-i2 -mattr=+reserve-i3 -mattr=+reserve-i4 -mattr=+reserve-i5 -verify-machineinstrs | FileCheck --check-prefix=SPARC-SPILL-O7 %s
+; RUN: llc < %s -mtriple=sparc64 -mattr=+reserve-g1 -mattr=+reserve-g2 -mattr=+reserve-g3 -mattr=+reserve-g4 -mattr=+reserve-g5 -mattr=+reserve-g6 -mattr=+reserve-g7 -mattr=+reserve-o0 -mattr=+reserve-o1 -mattr=+reserve-o2 -mattr=+reserve-o3 -mattr=+reserve-o4 -mattr=+reserve-o5 -mattr=+reserve-l1 -mattr=+reserve-l2 -mattr=+reserve-l3 -mattr=+reserve-l4 -mattr=+reserve-l5 -mattr=+reserve-l6 -mattr=+reserve-l7 -mattr=+reserve-l0 -mattr=+reserve-i0 -mattr=+reserve-i1 -mattr=+reserve-i2 -mattr=+reserve-i3 -mattr=+reserve-i4 -mattr=+reserve-i5 -verify-machineinstrs -sparc-bpcc-offset-bits=4 -sparc-bpr-offset-bits=4 | FileCheck --check-prefix=SPARC64-SPILL-O7 %s
+
+define i32 @branch_relax_large_o7_spilled(i32 %in) {
+; SPARC-SPILL-O7-LABEL: branch_relax_large_o7_spilled:
+; SPARC-SPILL-O7:         .cfi_startproc
+; SPARC-SPILL-O7-NEXT:  ! %bb.0:
+; SPARC-SPILL-O7-NEXT:    save %sp, -96, %sp
+; SPARC-SPILL-O7-NEXT:    .cfi_def_cfa_register %fp
+; SPARC-SPILL-O7-NEXT:    .cfi_window_save
+; SPARC-SPILL-O7-NEXT:    .cfi_register %o7, %i7
+; SPARC-SPILL-O7-NEXT:    mov %i0, %o7
+; SPARC-SPILL-O7-NEXT:    cmp %o7, 0
+; SPARC-SPILL-O7-NEXT:    !APP
+; SPARC-SPILL-O7-NEXT:  .Ltmp1:
+; SPARC-SPILL-O7-NEXT:    call .Ltmp1+8
+; SPARC-SPILL-O7-NEXT:    nop
+; SPARC-SPILL-O7-NEXT:    !NO_APP
+; SPARC-SPILL-O7-NEXT:    !APP
+; SPARC-SPILL-O7-NEXT:    mov %g0, %i0
+; SPARC-SPILL-O7-NEXT:    !NO_APP
+; SPARC-SPILL-O7-NEXT:    bne .LBB5_1
+; SPARC-SPILL-O7-NEXT:    nop
+; SPARC-SPILL-O7-NEXT:  ! %bb.3:
+; SPARC-SPILL-O7-NEXT:    st %o7, [%fp] ! 4-byte Folded Spill
+; SPARC-SPILL-O7-NEXT:    call .LBB5_4
+; SPARC-SPILL-O7-NEXT:    nop
+; SPARC-SPILL-O7-NEXT:  .LBB5_1: ! %false
+; SPARC-SPILL-O7-NEXT:    !APP
+; SPARC-SPILL-O7-NEXT:    .skip 268435456
+; SPARC-SPILL-O7-NEXT:    !NO_APP
+; SPARC-SPILL-O7-NEXT:    ret
+; SPARC-SPILL-O7-NEXT:    restore
+; SPARC-SPILL-O7-NEXT:  .LBB5_4: ! %true
+; SPARC-SPILL-O7-NEXT:    ld [%fp], %o7 ! 4-byte Folded Reload
+; SPARC-SPILL-O7-NEXT:  ! %bb.2: ! %true
+; SPARC-SPILL-O7-NEXT:    !APP
+; SPARC-SPILL-O7-NEXT:    .skip 268435456
+; SPARC-SPILL-O7-NEXT:    !NO_APP
+; SPARC-SPILL-O7-NEXT:    ret
+; SPARC-SPILL-O7-NEXT:    restore %g0, %o7, %o0
+;
+; SPARC64-SPILL-O7-LABEL: branch_relax_large_o7_spilled:
+; SPARC64-SPILL-O7:         .cfi_startproc
+; SPARC64-SPILL-O7-NEXT:  ! %bb.0:
+; SPARC64-SPILL-O7-NEXT:    save %sp, -128, %sp
+; SPARC64-SPILL-O7-NEXT:    .cfi_def_cfa_register %fp
+; SPARC64-SPILL-O7-NEXT:    .cfi_window_save
+; SPARC64-SPILL-O7-NEXT:    .cfi_register %o7, %i7
+; SPARC64-SPILL-O7-NEXT:    mov %i0, %o7
+; SPARC64-SPILL-O7-NEXT:    cmp %o7, 0
+; SPARC64-SPILL-O7-NEXT:    !APP
+; SPARC64-SPILL-O7-NEXT:  .Ltmp1:
+; SPARC64-SPILL-O7-NEXT:    call .Ltmp1+8
+; SPARC64-SPILL-O7-NEXT:    nop
+; SPARC64-SPILL-O7-NEXT:    !NO_APP
+; SPARC64-SPILL-O7-NEXT:    !APP
+; SPARC64-SPILL-O7-NEXT:    mov %g0, %i0
+; SPARC64-SPILL-O7-NEXT:    !NO_APP
+; SPARC64-SPILL-O7-NEXT:    bne %icc, .LBB5_1
+; SPARC64-SPILL-O7-NEXT:    nop
+; SPARC64-SPILL-O7-NEXT:  ! %bb.3:
+; SPARC64-SPILL-O7-NEXT:    stx %o7, [%fp+2047] ! 8-byte Folded Spill
+; SPARC64-SPILL-O7-NEXT:    call .LBB5_4
+; SPARC64-SPILL-O7-NEXT:    nop
+; SPARC64-SPILL-O7-NEXT:  .LBB5_1: ! %false
+; SPARC64-SPILL-O7-NEXT:    !APP
+; SPARC64-SPILL-O7-NEXT:    .skip 268435456
+; SPARC64-SPILL-O7-NEXT:    !NO_APP
+; SPARC64-SPILL-O7-NEXT:    ret
+; SPARC64-SPILL-O7-NEXT:    restore
+; SPARC64-SPILL-O7-NEXT:  .LBB5_4: ! %true
+; SPARC64-SPILL-O7-NEXT:    ldx [%fp+2047], %o7 ! 8-byte Folded Reload
+; SPARC64-SPILL-O7-NEXT:  ! %bb.2: ! %true
+; SPARC64-SPILL-O7-NEXT:    !APP
+; SPARC64-SPILL-O7-NEXT:    .skip 268435456
+; SPARC64-SPILL-O7-NEXT:    !NO_APP
+; SPARC64-SPILL-O7-NEXT:    ret
+; SPARC64-SPILL-O7-NEXT:    restore %g0, %o7, %o0
+  %tst = icmp eq i32 %in, 0
+  %rett = call i32 asm sideeffect "call .+8\0Anop", "={o7}"()
+  %retf = call i32 asm sideeffect "clr %i0", "={i0}"()
+  br i1 %tst, label %true, label %false
+
+true:
+  call void asm sideeffect ".space 268435456", ""()
+  ret i32 %rett
+
+false:
+  call void asm sideeffect ".space 268435456", ""()
+  ret i32 %retf
+}



More information about the llvm-commits mailing list