[llvm] 5c170b0 - Mips: Mark the $gp setup copy dead when erasing the call's $gp use (#227216)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 03:03:49 PDT 2026


Author: Matt Arsenault
Date: 2026-09-29T12:03:43+02:00
New Revision: 5c170b01350198b7f1f830fb53253652015077b3

URL: https://github.com/llvm/llvm-project/commit/5c170b01350198b7f1f830fb53253652015077b3
DIFF: https://github.com/llvm/llvm-project/commit/5c170b01350198b7f1f830fb53253652015077b3.diff

LOG: Mips: Mark the $gp setup copy dead when erasing the call's $gp use (#227216)

MipsOptimizePICCall drops the implicit $gp operand from a call when the
lazy binding stub for the callee has already run. The copy that set $gp
up for that call then has no reader left, but nothing flagged it, so the
MIR carried a live def until a later liveness recomputation cleaned it
up.

The pass already walks each block in order, so track the reaching
definition of $gp as it goes and mark it dead when the use is erased.

Co-authored-by: Claude Opus 5 <noreply at anthropic.com>

Added: 
    llvm/test/CodeGen/Mips/optimize-pic-call-dead-gp.ll

Modified: 
    llvm/lib/Target/Mips/MipsOptimizePICCall.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/Mips/MipsOptimizePICCall.cpp b/llvm/lib/Target/Mips/MipsOptimizePICCall.cpp
index 901b675e27aa8..9d44aaa338d4d 100644
--- a/llvm/lib/Target/Mips/MipsOptimizePICCall.cpp
+++ b/llvm/lib/Target/Mips/MipsOptimizePICCall.cpp
@@ -159,9 +159,9 @@ static void setCallTargetReg(MachineBasicBlock *MBB,
 }
 
 /// Search MI's operands for register GP and erase it.
-static void eraseGPOpnd(MachineInstr &MI) {
+static bool eraseGPOpnd(MachineInstr &MI) {
   if (!EraseGPOpnd)
-    return;
+    return false;
 
   MachineFunction &MF = *MI.getParent()->getParent();
   MVT::SimpleValueType Ty = getRegTy(MI.getOperand(0).getReg(), MF);
@@ -171,7 +171,7 @@ static void eraseGPOpnd(MachineInstr &MI) {
     MachineOperand &MO = MI.getOperand(I);
     if (MO.isReg() && MO.getReg() == Reg) {
       MI.removeOperand(I);
-      return;
+      return true;
     }
   }
 
@@ -228,36 +228,46 @@ bool OptimizePICCall::runOnMachineFunction(MachineFunction &F) {
 bool OptimizePICCall::visitNode(MBBInfo &MBBI) {
   bool Changed = false;
   MachineBasicBlock *MBB = MBBI.getNode()->getBlock();
+  const MipsRegisterInfo &TRI =
+      *MBB->getParent()->getSubtarget<MipsSubtarget>().getRegisterInfo();
+
+  // Definition of $gp reaching the current instruction.
+  MachineOperand *GPDef = nullptr;
 
   for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end(); I != E;
        ++I) {
     unsigned Reg;
     ValueType Entry;
 
-    // Skip instructions that are not call instructions via registers.
-    if (!isCallViaRegister(*I, Reg, Entry))
-      continue;
-
-    Changed = true;
-    unsigned N = getCount(Entry);
-
-    if (N != 0) {
-      // If a function has been called more than twice, we do not have to emit a
-      // load instruction to get the function address from the GOT, but can
-      // instead reuse the address that has been loaded before.
-      if (N >= 2 && !LoadTargetFromGOT)
-        getCallTargetRegOpnd(*I)->setReg(getReg(Entry));
-
-      // Erase the $gp operand if this isn't the first time a function has
-      // been called. $gp needs to be set up only if the function call can go
-      // through a lazy binding stub.
-      eraseGPOpnd(*I);
+    // Handle call instructions via registers.
+    if (isCallViaRegister(*I, Reg, Entry)) {
+      Changed = true;
+      unsigned N = getCount(Entry);
+
+      if (N != 0) {
+        // If a function has been called more than twice, we do not have to emit
+        // a load instruction to get the function address from the GOT, but can
+        // instead reuse the address that has been loaded before.
+        if (N >= 2 && !LoadTargetFromGOT)
+          getCallTargetRegOpnd(*I)->setReg(getReg(Entry));
+
+        // Erase the $gp operand if this isn't the first time a function has
+        // been called. $gp needs to be set up only if the function call can go
+        // through a lazy binding stub.
+        if (eraseGPOpnd(*I) && GPDef)
+          GPDef->setIsDead();
+      }
+
+      if (Entry)
+        incCntAndSetReg(Entry, Reg);
+
+      setCallTargetReg(MBB, I);
     }
 
-    if (Entry)
-      incCntAndSetReg(Entry, Reg);
-
-    setCallTargetReg(MBB, I);
+    if (MachineOperand *Def = I->findRegisterDefOperand(Mips::GP, &TRI))
+      GPDef = Def;
+    else if (I->readsRegister(Mips::GP, &TRI))
+      GPDef = nullptr;
   }
 
   return Changed;

diff  --git a/llvm/test/CodeGen/Mips/optimize-pic-call-dead-gp.ll b/llvm/test/CodeGen/Mips/optimize-pic-call-dead-gp.ll
new file mode 100644
index 0000000000000..c0657ed96e4ff
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/optimize-pic-call-dead-gp.ll
@@ -0,0 +1,78 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=mips-linux-gnu -relocation-model=pic -O0 -stop-before=regallocfast < %s | FileCheck -check-prefix=MIPS32 %s
+; RUN: llc -mtriple=mips64-linux-gnu -relocation-model=pic -O0 -stop-before=regallocfast < %s | FileCheck -check-prefix=MIPS64 %s
+
+; The second call does not need $gp, so the copy setting it up is dead.
+
+ at tls_gd = external thread_local global i32
+ at tls_ld = external thread_local(localdynamic) global i32
+
+define i32 @two_tls_calls() {
+  ; MIPS32-LABEL: name: two_tls_calls
+  ; MIPS32: bb.0 (%ir-block.0):
+  ; MIPS32-NEXT:   liveins: $t9, $v0
+  ; MIPS32-NEXT: {{  $}}
+  ; MIPS32-NEXT:   [[ADDu:%[0-9]+]]:gpr32 = ADDu $v0, $t9
+  ; MIPS32-NEXT:   ADJCALLSTACKDOWN 16, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS32-NEXT:   [[ADDiu:%[0-9]+]]:gpr32 = ADDiu [[ADDu]], target-flags(mips-tlsgd) @tls_gd
+  ; MIPS32-NEXT:   [[LW:%[0-9]+]]:gpr32 = LW [[ADDu]], target-flags(mips-got-call) &__tls_get_addr :: (load (s32) from call-entry &__tls_get_addr)
+  ; MIPS32-NEXT:   $a0 = COPY [[ADDiu]]
+  ; MIPS32-NEXT:   $gp = COPY [[ADDu]]
+  ; MIPS32-NEXT:   $t9 = COPY [[LW]]
+  ; MIPS32-NEXT:   JALRPseudo killed $t9, csr_o32, target-flags(mips-jalr) <mcsymbol __tls_get_addr>, implicit-def dead $ra, implicit $a0, implicit $gp, implicit-def $sp, implicit-def $v0
+  ; MIPS32-NEXT:   ADJCALLSTACKUP 16, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS32-NEXT:   [[COPY:%[0-9]+]]:gpr32 = COPY $v0
+  ; MIPS32-NEXT:   [[LW1:%[0-9]+]]:gpr32 = LW [[COPY]], 0 :: (dereferenceable load (s32) from @tls_gd)
+  ; MIPS32-NEXT:   ADJCALLSTACKDOWN 16, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS32-NEXT:   [[ADDiu1:%[0-9]+]]:gpr32 = ADDiu [[ADDu]], target-flags(mips-tlsldm) @tls_ld
+  ; MIPS32-NEXT:   [[LW2:%[0-9]+]]:gpr32 = LW [[ADDu]], target-flags(mips-got-call) &__tls_get_addr :: (load (s32) from call-entry &__tls_get_addr)
+  ; MIPS32-NEXT:   $a0 = COPY [[ADDiu1]]
+  ; MIPS32-NEXT:   dead $gp = COPY [[ADDu]]
+  ; MIPS32-NEXT:   $t9 = COPY [[LW2]]
+  ; MIPS32-NEXT:   JALRPseudo killed $t9, csr_o32, target-flags(mips-jalr) <mcsymbol __tls_get_addr>, implicit-def dead $ra, implicit $a0, implicit-def $sp, implicit-def $v0
+  ; MIPS32-NEXT:   ADJCALLSTACKUP 16, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS32-NEXT:   [[COPY1:%[0-9]+]]:gpr32 = COPY $v0
+  ; MIPS32-NEXT:   [[LUi:%[0-9]+]]:gpr32 = LUi target-flags(mips-dtprel-hi) @tls_ld
+  ; MIPS32-NEXT:   [[ADDu1:%[0-9]+]]:gpr32 = ADDu killed [[LUi]], [[COPY1]]
+  ; MIPS32-NEXT:   [[LW3:%[0-9]+]]:gpr32 = LW killed [[ADDu1]], target-flags(mips-dtprel-lo) @tls_ld :: (dereferenceable load (s32) from @tls_ld)
+  ; MIPS32-NEXT:   [[ADDu2:%[0-9]+]]:gpr32 = ADDu [[LW1]], [[LW3]]
+  ; MIPS32-NEXT:   $v0 = COPY [[ADDu2]]
+  ; MIPS32-NEXT:   RetRA implicit $v0
+  ;
+  ; MIPS64-LABEL: name: two_tls_calls
+  ; MIPS64: bb.0 (%ir-block.0):
+  ; MIPS64-NEXT:   liveins: $t9_64
+  ; MIPS64-NEXT: {{  $}}
+  ; MIPS64-NEXT:   [[LUi64_:%[0-9]+]]:gpr64 = LUi64 target-flags(mips-gpoff-hi) @two_tls_calls
+  ; MIPS64-NEXT:   [[DADDu:%[0-9]+]]:gpr64 = DADDu [[LUi64_]], $t9_64
+  ; MIPS64-NEXT:   [[DADDiu:%[0-9]+]]:gpr64 = DADDiu [[DADDu]], target-flags(mips-gpoff-lo) @two_tls_calls
+  ; MIPS64-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS64-NEXT:   [[DADDiu1:%[0-9]+]]:gpr64 = DADDiu [[DADDiu]], target-flags(mips-tlsgd) @tls_gd
+  ; MIPS64-NEXT:   [[LD:%[0-9]+]]:gpr64 = LD [[DADDiu]], target-flags(mips-got-call) &__tls_get_addr :: (load (s64) from call-entry &__tls_get_addr)
+  ; MIPS64-NEXT:   $a0_64 = COPY [[DADDiu1]]
+  ; MIPS64-NEXT:   $gp_64 = COPY [[DADDiu]]
+  ; MIPS64-NEXT:   $t9_64 = COPY [[LD]]
+  ; MIPS64-NEXT:   JALR64Pseudo killed $t9_64, csr_n64, target-flags(mips-jalr) <mcsymbol __tls_get_addr>, implicit-def dead $ra, implicit $a0_64, implicit $gp_64, implicit-def $sp, implicit-def $v0_64
+  ; MIPS64-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS64-NEXT:   [[COPY:%[0-9]+]]:gpr64 = COPY $v0_64
+  ; MIPS64-NEXT:   [[LW:%[0-9]+]]:gpr32 = LW [[COPY]], 0 :: (dereferenceable load (s32) from @tls_gd)
+  ; MIPS64-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS64-NEXT:   [[DADDiu2:%[0-9]+]]:gpr64 = DADDiu [[DADDiu]], target-flags(mips-tlsldm) @tls_ld
+  ; MIPS64-NEXT:   [[LD1:%[0-9]+]]:gpr64 = LD [[DADDiu]], target-flags(mips-got-call) &__tls_get_addr :: (load (s64) from call-entry &__tls_get_addr)
+  ; MIPS64-NEXT:   $a0_64 = COPY [[DADDiu2]]
+  ; MIPS64-NEXT:   dead $gp_64 = COPY [[DADDiu]]
+  ; MIPS64-NEXT:   $t9_64 = COPY [[LD1]]
+  ; MIPS64-NEXT:   JALR64Pseudo killed $t9_64, csr_n64, target-flags(mips-jalr) <mcsymbol __tls_get_addr>, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0_64
+  ; MIPS64-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; MIPS64-NEXT:   [[COPY1:%[0-9]+]]:gpr64 = COPY $v0_64
+  ; MIPS64-NEXT:   [[LUi64_1:%[0-9]+]]:gpr64 = LUi64 target-flags(mips-dtprel-hi) @tls_ld
+  ; MIPS64-NEXT:   [[DADDu1:%[0-9]+]]:gpr64 = DADDu killed [[LUi64_1]], [[COPY1]]
+  ; MIPS64-NEXT:   [[LW1:%[0-9]+]]:gpr32 = LW killed [[DADDu1]], target-flags(mips-dtprel-lo) @tls_ld :: (dereferenceable load (s32) from @tls_ld)
+  ; MIPS64-NEXT:   [[ADDu:%[0-9]+]]:gpr32 = ADDu killed [[LW]], killed [[LW1]]
+  ; MIPS64-NEXT:   $v0 = COPY [[ADDu]]
+  ; MIPS64-NEXT:   RetRA implicit $v0
+  %a = load i32, ptr @tls_gd
+  %b = load i32, ptr @tls_ld
+  %c = add i32 %a, %b
+  ret i32 %c
+}


        


More information about the llvm-commits mailing list