[llvm-branch-commits] [llvm] release/23.x: [MIPS] Don't mark SLL64_64 as isMoveReg (#214060) (PR #217798)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Aug 20 18:46:33 PDT 2026


https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/217798

Backport c3a7864be42e5d0aeb042b27a108e635c89cc8bf

Requested by: @brad0

>From 2bf7b794db078681dffd3fc0d85548b1db68cfe8 Mon Sep 17 00:00:00 2001
From: Daniel Lehmann <59584561+danlehmann at users.noreply.github.com>
Date: Thu, 20 Aug 2026 17:29:20 -0700
Subject: [PATCH] [MIPS] Don't mark SLL64_64 as isMoveReg (#214060)

I was running into a Rust miscompilation using pretty standard code (see
bugreport). I had Claude rootcause this to an LLVM bug, where SLL (on 64
bit) is marked as "move", even though it isn't - for full 64-bit values
(with arbitrary upper 32 bits), SLL is not a move as the upper bits get
just sign extended.

The fix itself is tiny and makes sense to me (I know MIPS very well, but
have little LLVM expertise myself). The test for this however is pretty
convoluted as it is quite hard to trigger this bug reliably - it needs
quite some register pressure to actually happen.

On the llvm/test/CodeGen/Mips/madd-msub.ll change: Register indices
changed here as the is the register allocator is now picking a new
independent register instead of reusing an existing one (`sll $4, $4, 0`
changes to `sll $1, $4, 0`). In this particular example that causes
instruction counts to be the same, though in other examples a small
increase is possible (cost of correctness).

Fixes #213419

Assisted-by: Claude Code (Opus 5)
(cherry picked from commit c3a7864be42e5d0aeb042b27a108e635c89cc8bf)
---
 llvm/lib/Target/Mips/Mips64InstrInfo.td       |  13 +-
 llvm/test/CodeGen/Mips/madd-msub.ll           |  84 +++++------
 .../CodeGen/Mips/sll64_64-is-not-a-copy.mir   | 134 ++++++++++++++++++
 3 files changed, 183 insertions(+), 48 deletions(-)
 create mode 100644 llvm/test/CodeGen/Mips/sll64_64-is-not-a-copy.mir

diff --git a/llvm/lib/Target/Mips/Mips64InstrInfo.td b/llvm/lib/Target/Mips/Mips64InstrInfo.td
index 1321dd3bde211..b6d820bbc1025 100644
--- a/llvm/lib/Target/Mips/Mips64InstrInfo.td
+++ b/llvm/lib/Target/Mips/Mips64InstrInfo.td
@@ -426,12 +426,13 @@ let isCodeGenOnly = 1, AdditionalPredicates = [NotInMicroMips] in {
 let isCodeGenOnly = 1, rs = 0, shamt = 0 in {
   def DSLL64_32 : FR<0x00, 0x3c, (outs GPR64:$rd), (ins GPR32:$rt),
                      "dsll\t$rd, $rt, 32", []>, GPR_64;
-  let isMoveReg = 1 in {
-    def SLL64_32 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR32:$rt),
-                      "sll\t$rd, $rt, 0", []>, GPR_64;
-    def SLL64_64 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR64:$rt),
-                      "sll\t$rd, $rt, 0", []>, GPR_64;
-  }
+  let isMoveReg = 1 in
+  def SLL64_32 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR32:$rt),
+                    "sll\t$rd, $rt, 0", []>, GPR_64;
+  // Not isMoveReg: with a 64-bit source this truncates to 32 bits and
+  // sign-extends, which is not value-preserving.
+  def SLL64_64 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR64:$rt),
+                    "sll\t$rd, $rt, 0", []>, GPR_64;
 }
 
 // We need the following pseudo instruction to avoid offset calculation for
diff --git a/llvm/test/CodeGen/Mips/madd-msub.ll b/llvm/test/CodeGen/Mips/madd-msub.ll
index 6bb1ea48184f9..f0d6649e767a0 100644
--- a/llvm/test/CodeGen/Mips/madd-msub.ll
+++ b/llvm/test/CodeGen/Mips/madd-msub.ll
@@ -42,22 +42,22 @@ define i64 @madd1(i32 %a, i32 %b, i32 %c) nounwind readnone {
 ;
 ; 64-LABEL: madd1:
 ; 64:       # %bb.0: # %entry
-; 64-NEXT:    sll $4, $4, 0
-; 64-NEXT:    sll $5, $5, 0
-; 64-NEXT:    dmult $5, $4
+; 64-NEXT:    sll $1, $4, 0
+; 64-NEXT:    sll $2, $5, 0
+; 64-NEXT:    dmult $2, $1
 ; 64-NEXT:    mflo $1
-; 64-NEXT:    sll $6, $6, 0
+; 64-NEXT:    sll $2, $6, 0
 ; 64-NEXT:    jr $ra
-; 64-NEXT:    daddu $2, $1, $6
+; 64-NEXT:    daddu $2, $1, $2
 ;
 ; 64R6-LABEL: madd1:
 ; 64R6:       # %bb.0: # %entry
-; 64R6-NEXT:    sll $4, $4, 0
-; 64R6-NEXT:    sll $5, $5, 0
-; 64R6-NEXT:    dmul $1, $5, $4
-; 64R6-NEXT:    sll $6, $6, 0
+; 64R6-NEXT:    sll $1, $4, 0
+; 64R6-NEXT:    sll $2, $5, 0
+; 64R6-NEXT:    dmul $1, $2, $1
+; 64R6-NEXT:    sll $2, $6, 0
 ; 64R6-NEXT:    jr $ra
-; 64R6-NEXT:    daddu $2, $1, $6
+; 64R6-NEXT:    daddu $2, $1, $2
 ;
 ; 16-LABEL: madd1:
 ; 16:       # %bb.0: # %entry
@@ -173,18 +173,18 @@ define i64 @madd3(i32 %a, i32 %b, i64 %c) nounwind readnone {
 ;
 ; 64-LABEL: madd3:
 ; 64:       # %bb.0: # %entry
-; 64-NEXT:    sll $4, $4, 0
-; 64-NEXT:    sll $5, $5, 0
-; 64-NEXT:    dmult $5, $4
+; 64-NEXT:    sll $1, $4, 0
+; 64-NEXT:    sll $2, $5, 0
+; 64-NEXT:    dmult $2, $1
 ; 64-NEXT:    mflo $1
 ; 64-NEXT:    jr $ra
 ; 64-NEXT:    daddu $2, $1, $6
 ;
 ; 64R6-LABEL: madd3:
 ; 64R6:       # %bb.0: # %entry
-; 64R6-NEXT:    sll $4, $4, 0
-; 64R6-NEXT:    sll $5, $5, 0
-; 64R6-NEXT:    dmul $1, $5, $4
+; 64R6-NEXT:    sll $1, $4, 0
+; 64R6-NEXT:    sll $2, $5, 0
+; 64R6-NEXT:    dmul $1, $2, $1
 ; 64R6-NEXT:    jr $ra
 ; 64R6-NEXT:    daddu $2, $1, $6
 ;
@@ -291,22 +291,22 @@ define i64 @msub1(i32 %a, i32 %b, i32 %c) nounwind readnone {
 ;
 ; 64-LABEL: msub1:
 ; 64:       # %bb.0: # %entry
-; 64-NEXT:    sll $4, $4, 0
-; 64-NEXT:    sll $5, $5, 0
-; 64-NEXT:    dmult $5, $4
+; 64-NEXT:    sll $1, $4, 0
+; 64-NEXT:    sll $2, $5, 0
+; 64-NEXT:    dmult $2, $1
 ; 64-NEXT:    mflo $1
-; 64-NEXT:    sll $6, $6, 0
+; 64-NEXT:    sll $2, $6, 0
 ; 64-NEXT:    jr $ra
-; 64-NEXT:    dsubu $2, $6, $1
+; 64-NEXT:    dsubu $2, $2, $1
 ;
 ; 64R6-LABEL: msub1:
 ; 64R6:       # %bb.0: # %entry
-; 64R6-NEXT:    sll $4, $4, 0
-; 64R6-NEXT:    sll $5, $5, 0
-; 64R6-NEXT:    dmul $1, $5, $4
-; 64R6-NEXT:    sll $6, $6, 0
+; 64R6-NEXT:    sll $1, $4, 0
+; 64R6-NEXT:    sll $2, $5, 0
+; 64R6-NEXT:    dmul $1, $2, $1
+; 64R6-NEXT:    sll $2, $6, 0
 ; 64R6-NEXT:    jr $ra
-; 64R6-NEXT:    dsubu $2, $6, $1
+; 64R6-NEXT:    dsubu $2, $2, $1
 ;
 ; 16-LABEL: msub1:
 ; 16:       # %bb.0: # %entry
@@ -424,18 +424,18 @@ define i64 @msub3(i32 %a, i32 %b, i64 %c) nounwind readnone {
 ;
 ; 64-LABEL: msub3:
 ; 64:       # %bb.0: # %entry
-; 64-NEXT:    sll $4, $4, 0
-; 64-NEXT:    sll $5, $5, 0
-; 64-NEXT:    dmult $5, $4
+; 64-NEXT:    sll $1, $4, 0
+; 64-NEXT:    sll $2, $5, 0
+; 64-NEXT:    dmult $2, $1
 ; 64-NEXT:    mflo $1
 ; 64-NEXT:    jr $ra
 ; 64-NEXT:    dsubu $2, $6, $1
 ;
 ; 64R6-LABEL: msub3:
 ; 64R6:       # %bb.0: # %entry
-; 64R6-NEXT:    sll $4, $4, 0
-; 64R6-NEXT:    sll $5, $5, 0
-; 64R6-NEXT:    dmul $1, $5, $4
+; 64R6-NEXT:    sll $1, $4, 0
+; 64R6-NEXT:    sll $2, $5, 0
+; 64R6-NEXT:    dmul $1, $2, $1
 ; 64R6-NEXT:    jr $ra
 ; 64R6-NEXT:    dsubu $2, $6, $1
 ;
@@ -546,22 +546,22 @@ define i64 @msub5(i32 %a, i32 %b, i32 %c) {
 ;
 ; 64-LABEL: msub5:
 ; 64:       # %bb.0: # %entry
-; 64-NEXT:    sll $4, $4, 0
-; 64-NEXT:    sll $5, $5, 0
-; 64-NEXT:    dmult $5, $4
+; 64-NEXT:    sll $1, $4, 0
+; 64-NEXT:    sll $2, $5, 0
+; 64-NEXT:    dmult $2, $1
 ; 64-NEXT:    mflo $1
-; 64-NEXT:    sll $6, $6, 0
+; 64-NEXT:    sll $2, $6, 0
 ; 64-NEXT:    jr $ra
-; 64-NEXT:    dsubu $2, $1, $6
+; 64-NEXT:    dsubu $2, $1, $2
 ;
 ; 64R6-LABEL: msub5:
 ; 64R6:       # %bb.0: # %entry
-; 64R6-NEXT:    sll $4, $4, 0
-; 64R6-NEXT:    sll $5, $5, 0
-; 64R6-NEXT:    dmul $1, $5, $4
-; 64R6-NEXT:    sll $6, $6, 0
+; 64R6-NEXT:    sll $1, $4, 0
+; 64R6-NEXT:    sll $2, $5, 0
+; 64R6-NEXT:    dmul $1, $2, $1
+; 64R6-NEXT:    sll $2, $6, 0
 ; 64R6-NEXT:    jr $ra
-; 64R6-NEXT:    dsubu $2, $1, $6
+; 64R6-NEXT:    dsubu $2, $1, $2
 ;
 ; 16-LABEL: msub5:
 ; 16:       # %bb.0: # %entry
diff --git a/llvm/test/CodeGen/Mips/sll64_64-is-not-a-copy.mir b/llvm/test/CodeGen/Mips/sll64_64-is-not-a-copy.mir
new file mode 100644
index 0000000000000..aa7ac7fc7b441
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/sll64_64-is-not-a-copy.mir
@@ -0,0 +1,134 @@
+# NOTE: assertions are hand-written, not autogenerated.
+# RUN: llc -mtriple=mips64 -mcpu=mips3 -run-pass=greedy -o - %s | FileCheck %s
+
+# SLL64_64 is `sll $rd, $rt, 0` with a GPR64 source, so it truncates its operand to 32 bits and
+# sign-extends the result. It is therefore not a register move, and must survive register
+# allocation: deleting it, or replacing its uses with its source, silently drops the truncation.
+#
+# This function reaches register allocation with four live SLL64_64 instructions. Marking the
+# instruction isMoveReg used to make the allocator drop one of them.
+
+# CHECK-COUNT-4: = SLL64_64
+
+--- |
+  target datalayout = "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+  target triple = "mips64-unknown-unknown"
+
+  define void @test() { ret void }
+...
+---
+name:            test
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $a0_64, $a1_64, $a2_64, $a3_64
+    %109:gpr64 = COPY $a3_64
+    %108:gpr64 = COPY $a2_64
+    %107:gpr64 = COPY $a1_64
+    %106:gpr64 = COPY $a0_64
+    %111:gpr32 = LUi 1023
+    %112:gpr32 = ORi %111, 65535
+    %3:gpr64 = LD %106, 16
+    %114:gpr32 = SLL %3.sub_32, 0
+    %115:gpr32 = AND %114, %112
+    %116:gpr64 = DSRL %3, 32
+    %118:gpr32 = SLL %116.sub_32, 0
+    %119:gpr32 = ANDi %118, 1023
+    %120:gpr32 = LW %109, 32
+    %7:gpr32 = SUBu %115, %120
+    %2:gpr64 = LD %106, 8
+    %0:gpr64 = LD %106, 0
+    %121:gpr32 = SLL %119, 1
+    %271:gpr64 = DADDiu $zero_64, 0
+    %6:gpr32 = ADDiu %121, 2
+    %136:gpr64 = DADDiu $zero_64, 3
+    %137:gpr64 = DSLL %136, 52
+    %138:gpr64 = AND64 %0, %137
+    %139:gpr64 = DADDiu $zero_64, 13107
+    %140:gpr64 = DSLL %139, 18
+    %141:gpr64 = DADDiu %140, 768
+    %142:gpr64 = AND64 %0, %141
+    %143:gpr64 = DADDiu $zero_64, 8211
+    %144:gpr64 = DSLL %143, 18
+    %145:gpr64 = DADDiu %144, 512
+    %146:gpr64 = DSRL %3, 51
+    %148:gpr32 = nuw nsw SLL %146.sub_32, 0
+    %153:gpr64 = DADDiu $zero_64, 1
+    %154:gpr64 = DSLL %153, 55
+    %155:gpr64 = DADDiu $zero_64, -4
+    %215:gpr64 = LUi64 3
+    %216:gpr64 = ORi64 %215, 65535
+    J %bb.2, implicit-def dead $at
+  bb.1:
+    RetRA
+  bb.2:
+    %124:gpr64 = nuw nsw DSLL %271, 3
+    %11:gpr64 = nuw DADDu %107, %124
+    %12:gpr64 = LD %11, 0
+    BNE64 %138, $zero_64, %bb.97, implicit-def dead $at
+    BNE64 %142, %145, %bb.96, implicit-def dead $at
+  bb.11:
+    %149:gpr32 = ANDi %148, 2
+    %151:gpr32 = SLL %12.sub_32, 0
+    %25:gpr64 = LD %11, 16
+    %23:gpr64 = LD %11, 8
+    %152:gpr64 = DSRA %23, 30
+    %19:gpr64 = AND64 %12, %154
+    %24:gpr64 = AND64 %152, %155
+    %26:gpr64 = SLL64_64 %25
+    %21:gpr32 = SLL %151, 2
+    %156:gpr32 = SLL %151, 18
+    %157:gpr64 = DSRL %12, 14
+    %20:gpr32 = SLL %157.sub_32, 0
+    %56:gpr32 = SRA %156, 18
+    %159:gpr64 = LD %11, 24
+    %162:gpr64 = DSRL %2, 12
+    %165:gpr64 = ANDi64 %162, 4095
+    %167:gpr64 = DSRA %159, 30
+    %168:gpr64 = DSRA %25, 30
+    %57:gpr64 = AND64 %168, %155
+    %58:gpr64 = AND64 %167, %155
+    %34:gpr32 = SRA %21, 18
+    %33:gpr64 = SLL64_64 %159
+    %32:gpr64 = nsw DADDiu %165, -1
+    %172:gpr64 = DSRL %2, 44
+    %173:gpr64 = ANDi64 %172, 4095
+    %174:gpr64 = nuw nsw DADDiu %173, 3
+    %30:gpr64 = DSRL %174, 2
+    %35:gpr32 = COPY %56
+    %38:gpr64 = SLL64_64 %23
+    %283:gpr64 = COPY %30
+    %44:gpr32 = exact SRA %56, 2
+    %254:acc64 = PseudoMULT %6, %44
+    %255:gpr32 = PseudoMFLO %254
+    %256:gpr32 = ADDu %7, %255
+    %258:gpr32 = SLL %283.sub_32, 1
+    %24:gpr64 = DADDu %24, %38
+    BNE %56, %35, %bb.40, implicit-def $at
+  bb.40:
+    %59:gpr32 = exact SRA %56, 2
+    %290:gpr64 = COPY %58
+    %217:gpr64 = DADDu %290, %216
+    %218:gpr64 = DSRA %217, 18
+    %293:gpr64 = COPY %32
+    %225:acc64 = PseudoMULT %6, %59
+    %226:gpr32 = PseudoMFLO %225
+  bb.58:
+    %79:gpr32 = SRA %20, 18
+    %78:gpr64 = SLL64_64 %23
+    %24:gpr64 = DADDu %24, %78
+    %311:gpr64 = COPY %58
+    %312:gpr64 = COPY %57
+    %180:gpr32 = SLT64 %312, %311
+    BEQ %180, $zero, %bb.87, implicit-def dead $at
+  bb.86:
+    %58:gpr64 = DADDu %58, %33
+    %57:gpr64 = DADDu %57, %26
+    BNE %56, %34, %bb.58, implicit-def $at
+  bb.87:
+    %313:gpr64 = COPY %30
+  bb.96:
+  bb.97:
+    %267:gpr32 = samesign SLTu64 %271, %108
+    BNE %267, $zero, %bb.2, implicit-def dead $at
+    J %bb.1, implicit-def dead $at
\ No newline at end of file



More information about the llvm-branch-commits mailing list