[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