[llvm] [ARM] Take advantage of built-in mod of shift amount in variable-shift rotations (PR #157208)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 20 09:17:13 PDT 2025


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/157208

>From 342abb66e43ff6ca8452d64f0cd650fd23f6d805 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 5 Sep 2025 12:03:17 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)

---
 llvm/test/CodeGen/ARM/shift-mod.ll | 1068 ++++++++++++++++++++++++++++
 1 file changed, 1068 insertions(+)
 create mode 100644 llvm/test/CodeGen/ARM/shift-mod.ll

diff --git a/llvm/test/CodeGen/ARM/shift-mod.ll b/llvm/test/CodeGen/ARM/shift-mod.ll
new file mode 100644
index 0000000000000..3ba591e11acd1
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/shift-mod.ll
@@ -0,0 +1,1068 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=armv7-linux-gnueabihf %s -o - | FileCheck %s --check-prefixes=CHECK-COMMON,CHECK-ARM
+; RUN: llc -mtriple=armv7eb-linux-gnueabihf %s -o - | FileCheck %s --check-prefixes=CHECK-BE
+; RUN: llc -mtriple=thumbv7-linux-gnueabihf %s -o - | FileCheck %s --check-prefixes=CHECK-COMMON,CHECK-THUMB
+; RUN: llc -mtriple=thumbv7m %s -o - | FileCheck %s --check-prefixes=CHECK-COMMON,CHECK-THUMB
+; RUN: llc -mtriple=thumbv7m -mattr=+strict-align %s -o - | FileCheck %s --check-prefixes=CHECK-COMMON,CHECK-ALIGN
+; RUN: llc -mtriple=thumbv6m %s -o - | FileCheck %s --check-prefix=CHECK-V6M
+
+; -----------------------------------------------------------------
+; LSL (shl) group
+; -----------------------------------------------------------------
+
+; (amount = amt + 32) => should remove the ADD and emit LSL directly.
+define i32 @lsl_add_mod(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsl_add_mod:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    add r1, r1, #32
+; CHECK-NEXT:    lsl r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsl_add_mod:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    add r1, r1, #32
+; CHECK-ARM-NEXT:    lsl r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsl_add_mod:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    add r1, r1, #32
+; CHECK-BE-NEXT:    lsl r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsl_add_mod:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    adds r1, #32
+; CHECK-THUMB-NEXT:    lsls r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsl_add_mod:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    adds r1, #32
+; CHECK-ALIGN-NEXT:    lsls r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsl_add_mod:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    adds r1, #32
+; CHECK-V6M-NEXT:    lsls r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = add i32 %amt, 32
+  %r  = shl i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = 32 - amt) => should become RSB/NEG then used by LSL.
+define i32 @lsl_sub_rsb(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsl_sub_rsb:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #32
+; CHECK-NEXT:    lsl r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsl_sub_rsb:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #32
+; CHECK-ARM-NEXT:    lsl r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsl_sub_rsb:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #32
+; CHECK-BE-NEXT:    lsl r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsl_sub_rsb:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
+; CHECK-THUMB-NEXT:    lsls r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsl_sub_rsb:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
+; CHECK-ALIGN-NEXT:    lsls r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsl_sub_rsb:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #32
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    lsls r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 32, %amt
+  %r  = shl i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = 31 - amt) => should become MVN(amt) then used by LSL.
+define i32 @lsl_sub_mvn(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsl_sub_mvn:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #31
+; CHECK-NEXT:    lsl r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsl_sub_mvn:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #31
+; CHECK-ARM-NEXT:    lsl r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsl_sub_mvn:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #31
+; CHECK-BE-NEXT:    lsl r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsl_sub_mvn:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
+; CHECK-THUMB-NEXT:    lsls r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsl_sub_mvn:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
+; CHECK-ALIGN-NEXT:    lsls r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsl_sub_mvn:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    lsls r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 31, %amt
+  %r  = shl i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = amt & 31) => AND is redundant; should be removed and emit LSL only.
+define i32 @lsl_and_mask(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsl_and_mask:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    and r1, r1, #31
+; CHECK-NEXT:    lsl r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsl_and_mask:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    and r1, r1, #31
+; CHECK-ARM-NEXT:    lsl r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsl_and_mask:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    and r1, r1, #31
+; CHECK-BE-NEXT:    lsl r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsl_and_mask:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    and r1, r1, #31
+; CHECK-THUMB-NEXT:    lsls r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsl_and_mask:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    and r1, r1, #31
+; CHECK-ALIGN-NEXT:    lsls r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsl_and_mask:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    ands r2, r1
+; CHECK-V6M-NEXT:    lsls r0, r2
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = and i32 %amt, 31
+  %r  = shl i32 %val, %sa
+  ret i32 %r
+}
+
+; -----------------------------------------------------------------
+; LSR (logical right) group
+; -----------------------------------------------------------------
+
+; (amount = amt + 32) => should remove the ADD and emit LSR directly.
+define i32 @lsr_add_mod(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsr_add_mod:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    add r1, r1, #32
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsr_add_mod:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    add r1, r1, #32
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsr_add_mod:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    add r1, r1, #32
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsr_add_mod:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    adds r1, #32
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsr_add_mod:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    adds r1, #32
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsr_add_mod:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    adds r1, #32
+; CHECK-V6M-NEXT:    lsrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = add i32 %amt, 32
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = 32 - amt) => should become RSB/NEG then used by LSR (and in some targets may be lowered to ROR form).
+define i32 @lsr_sub_rsb(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsr_sub_rsb:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #32
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsr_sub_rsb:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #32
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsr_sub_rsb:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #32
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsr_sub_rsb:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsr_sub_rsb:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsr_sub_rsb:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #32
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    lsrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 32, %amt
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = 31 - amt) => should become MVN(amt) then used by LSR.
+define i32 @lsr_sub_mvn(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsr_sub_mvn:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #31
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsr_sub_mvn:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #31
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsr_sub_mvn:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #31
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsr_sub_mvn:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsr_sub_mvn:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsr_sub_mvn:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    lsrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 31, %amt
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = amt & 31) => AND is redundant; should be removed and emit LSR only.
+define i32 @lsr_and_mask(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: lsr_and_mask:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    and r1, r1, #31
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: lsr_and_mask:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    and r1, r1, #31
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: lsr_and_mask:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    and r1, r1, #31
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: lsr_and_mask:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    and r1, r1, #31
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: lsr_and_mask:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    and r1, r1, #31
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: lsr_and_mask:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    ands r2, r1
+; CHECK-V6M-NEXT:    lsrs r0, r2
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = and i32 %amt, 31
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+; -----------------------------------------------------------------
+; ASR (arithmetic right) group
+; -----------------------------------------------------------------
+
+; (amount = amt + 32) => should remove the ADD and emit ASR directly.
+; CHECK-LABEL: asr_add_mod:
+; CHECK-NOT: add
+; CHECK: asr
+; THUMB-LABEL: asr_add_mod:
+; THUMB-NOT: add
+; THUMB: asr
+define i32 @asr_add_mod(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: asr_add_mod:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    add r1, r1, #32
+; CHECK-NEXT:    asr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: asr_add_mod:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    add r1, r1, #32
+; CHECK-ARM-NEXT:    asr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: asr_add_mod:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    add r1, r1, #32
+; CHECK-BE-NEXT:    asr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: asr_add_mod:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    adds r1, #32
+; CHECK-THUMB-NEXT:    asrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: asr_add_mod:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    adds r1, #32
+; CHECK-ALIGN-NEXT:    asrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: asr_add_mod:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    adds r1, #32
+; CHECK-V6M-NEXT:    asrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = add i32 %amt, 32
+  %r  = ashr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = 32 - amt) => should become RSB/NEG then used by ASR.
+; CHECK-LABEL: asr_sub_rsb:
+; CHECK: rsb
+; CHECK: asr
+; THUMB-LABEL: asr_sub_rsb:
+; THUMB: rsb
+; THUMB: asr
+define i32 @asr_sub_rsb(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: asr_sub_rsb:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #32
+; CHECK-NEXT:    asr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: asr_sub_rsb:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #32
+; CHECK-ARM-NEXT:    asr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: asr_sub_rsb:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #32
+; CHECK-BE-NEXT:    asr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: asr_sub_rsb:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
+; CHECK-THUMB-NEXT:    asrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: asr_sub_rsb:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
+; CHECK-ALIGN-NEXT:    asrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: asr_sub_rsb:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #32
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    asrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 32, %amt
+  %r  = ashr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = 31 - amt) => should become MVN(amt) then used by ASR.
+; CHECK-LABEL: asr_sub_mvn:
+; CHECK: mvn
+; CHECK: asr
+; THUMB-LABEL: asr_sub_mvn:
+; THUMB: mvn
+; THUMB: asr
+define i32 @asr_sub_mvn(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: asr_sub_mvn:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #31
+; CHECK-NEXT:    asr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: asr_sub_mvn:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #31
+; CHECK-ARM-NEXT:    asr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: asr_sub_mvn:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #31
+; CHECK-BE-NEXT:    asr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: asr_sub_mvn:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
+; CHECK-THUMB-NEXT:    asrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: asr_sub_mvn:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
+; CHECK-ALIGN-NEXT:    asrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: asr_sub_mvn:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    asrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 31, %amt
+  %r  = ashr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = amt & 31) => AND is redundant; should be removed and emit ASR only.
+; CHECK-LABEL: asr_and_mask:
+; CHECK-NOT: and
+; CHECK: asr
+; THUMB-LABEL: asr_and_mask:
+; THUMB-NOT: and
+; THUMB: asr
+define i32 @asr_and_mask(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: asr_and_mask:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    and r1, r1, #31
+; CHECK-NEXT:    asr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: asr_and_mask:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    and r1, r1, #31
+; CHECK-ARM-NEXT:    asr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: asr_and_mask:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    and r1, r1, #31
+; CHECK-BE-NEXT:    asr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: asr_and_mask:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    and r1, r1, #31
+; CHECK-THUMB-NEXT:    asrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: asr_and_mask:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    and r1, r1, #31
+; CHECK-ALIGN-NEXT:    asrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: asr_and_mask:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    ands r2, r1
+; CHECK-V6M-NEXT:    asrs r0, r2
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = and i32 %amt, 31
+  %r  = ashr i32 %val, %sa
+  ret i32 %r
+}
+
+; (amount = amt + 32) => remove ADD and emit ROR/LSR as appropriate (we check for absence of ADD and presence of LSR/ROR)
+define i32 @ror_add_mod(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: ror_add_mod:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    add r1, r1, #32
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: ror_add_mod:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    add r1, r1, #32
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: ror_add_mod:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    add r1, r1, #32
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: ror_add_mod:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    adds r1, #32
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: ror_add_mod:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    adds r1, #32
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: ror_add_mod:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    adds r1, #32
+; CHECK-V6M-NEXT:    lsrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = add i32 %amt, 32
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+define i32 @ror_sub_rsb(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: ror_sub_rsb:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #32
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: ror_sub_rsb:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #32
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: ror_sub_rsb:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #32
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: ror_sub_rsb:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: ror_sub_rsb:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: ror_sub_rsb:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #32
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    lsrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 32, %amt
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+define i32 @ror_sub_mvn(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: ror_sub_mvn:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    rsb r1, r1, #31
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: ror_sub_mvn:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #31
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: ror_sub_mvn:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #31
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: ror_sub_mvn:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: ror_sub_mvn:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: ror_sub_mvn:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    lsrs r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 31, %amt
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+define i32 @ror_and_mask(i32 %val, i32 %amt) #0 {
+; CHECK-LABEL: ror_and_mask:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    and r1, r1, #31
+; CHECK-NEXT:    lsr r0, r0, r1
+; CHECK-NEXT:    bx lr
+;
+; CHECK-ARM-LABEL: ror_and_mask:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    and r1, r1, #31
+; CHECK-ARM-NEXT:    lsr r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: ror_and_mask:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    and r1, r1, #31
+; CHECK-BE-NEXT:    lsr r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: ror_and_mask:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    and r1, r1, #31
+; CHECK-THUMB-NEXT:    lsrs r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: ror_and_mask:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    and r1, r1, #31
+; CHECK-ALIGN-NEXT:    lsrs r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: ror_and_mask:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    ands r2, r1
+; CHECK-V6M-NEXT:    lsrs r0, r2
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = and i32 %amt, 31
+  %r  = lshr i32 %val, %sa
+  ret i32 %r
+}
+
+
+declare i32 @llvm.fshl.i32(i32, i32, i32)
+declare i32 @llvm.fshr.i32(i32, i32, i32)
+
+; -----------------------
+; fshl (funnel-shift left) group
+; fshl(x,x, s) == rotate-left(x, s)
+; -----------------------
+
+; amt + 32 -> REMOVE ADD (mod 32), expect rotate lowering (ror/lsl/lsr depending on selector)
+define i32 @fshl_add_mod(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshl_add_mod:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    mvn r2, #31
+; CHECK-ARM-NEXT:    sub r1, r2, r1
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshl_add_mod:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    mvn r2, #31
+; CHECK-BE-NEXT:    sub r1, r2, r1
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshl_add_mod:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    mvn r2, #31
+; CHECK-THUMB-NEXT:    subs r1, r2, r1
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshl_add_mod:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    mvn r2, #31
+; CHECK-ALIGN-NEXT:    subs r1, r2, r1
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshl_add_mod:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    mvns r2, r2
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = add i32 %amt, 32
+  %r  = call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; 32 - amt -> expect RSB/NEG materialization then rotate lowering
+; CHECK-LABEL: fshl_sub_rsb:
+; CHECK: rsb
+; CHECK: ror
+; THUMB-LABEL: fshl_sub_rsb:
+; THUMB: rsb
+; THUMB: ror
+define i32 @fshl_sub_rsb(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshl_sub_rsb:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    sub r1, r1, #32
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshl_sub_rsb:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    sub r1, r1, #32
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshl_sub_rsb:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    subs r1, #32
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshl_sub_rsb:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    subs r1, #32
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshl_sub_rsb:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    subs r1, #32
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 32, %amt
+  %r  = call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; 31 - amt -> expect MVN(amt) (i.e. NOT) then rotate lowering (selector may use NOT+rotate form)
+; CHECK-LABEL: fshl_sub_mvn:
+; CHECK: mvn
+; CHECK: ror
+; THUMB-LABEL: fshl_sub_mvn:
+; THUMB: mvn
+; THUMB: ror
+define i32 @fshl_sub_mvn(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshl_sub_mvn:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    sub r1, r1, #31
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshl_sub_mvn:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    sub r1, r1, #31
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshl_sub_mvn:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    subs r1, #31
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshl_sub_mvn:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    subs r1, #31
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshl_sub_mvn:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    subs r1, #31
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 31, %amt
+  %r  = call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; amt & 31 -> AND redundant (hardware masks low 5 bits) -> expect rotate lowering with no AND
+define i32 @fshl_and_mask(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshl_and_mask:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #0
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshl_and_mask:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #0
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshl_and_mask:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsbs r1, r1, #0
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshl_and_mask:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsbs r1, r1, #0
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshl_and_mask:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    rsbs r1, r1, #0
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = and i32 %amt, 31
+  %r  = call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; -----------------------
+; fshr (funnel-shift right) group
+; fshr(x,x, s) == rotate-right(x, s)
+; -----------------------
+
+; amt + 32 -> REMOVE ADD (mod 32) -> expect rotate lowering
+define i32 @fshr_add_mod(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshr_add_mod:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    add r1, r1, #32
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshr_add_mod:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    add r1, r1, #32
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshr_add_mod:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    adds r1, #32
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshr_add_mod:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    adds r1, #32
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshr_add_mod:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    adds r1, #32
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = add i32 %amt, 32
+  %r  = call i32 @llvm.fshr.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; 32 - amt -> expect RSB then rotate lowering
+define i32 @fshr_sub_rsb(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshr_sub_rsb:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #32
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshr_sub_rsb:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #32
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshr_sub_rsb:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshr_sub_rsb:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshr_sub_rsb:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #32
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 32, %amt
+  %r  = call i32 @llvm.fshr.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; 31 - amt -> expect MVN then rotate lowering
+define i32 @fshr_sub_mvn(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshr_sub_mvn:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    rsb r1, r1, #31
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshr_sub_mvn:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    rsb r1, r1, #31
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshr_sub_mvn:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshr_sub_mvn:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshr_sub_mvn:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    movs r2, #31
+; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = sub i32 31, %amt
+  %r  = call i32 @llvm.fshr.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+; amt & 31 -> AND redundant -> expect rotate lowering with no AND
+define i32 @fshr_and_mask(i32 %x, i32 %amt) #0 {
+; CHECK-ARM-LABEL: fshr_and_mask:
+; CHECK-ARM:       @ %bb.0: @ %entry
+; CHECK-ARM-NEXT:    ror r0, r0, r1
+; CHECK-ARM-NEXT:    bx lr
+;
+; CHECK-BE-LABEL: fshr_and_mask:
+; CHECK-BE:       @ %bb.0: @ %entry
+; CHECK-BE-NEXT:    ror r0, r0, r1
+; CHECK-BE-NEXT:    bx lr
+;
+; CHECK-THUMB-LABEL: fshr_and_mask:
+; CHECK-THUMB:       @ %bb.0: @ %entry
+; CHECK-THUMB-NEXT:    rors r0, r1
+; CHECK-THUMB-NEXT:    bx lr
+;
+; CHECK-ALIGN-LABEL: fshr_and_mask:
+; CHECK-ALIGN:       @ %bb.0: @ %entry
+; CHECK-ALIGN-NEXT:    rors r0, r1
+; CHECK-ALIGN-NEXT:    bx lr
+;
+; CHECK-V6M-LABEL: fshr_and_mask:
+; CHECK-V6M:       @ %bb.0: @ %entry
+; CHECK-V6M-NEXT:    rors r0, r1
+; CHECK-V6M-NEXT:    bx lr
+entry:
+  %sa = and i32 %amt, 31
+  %r  = call i32 @llvm.fshr.i32(i32 %x, i32 %x, i32 %sa)
+  ret i32 %r
+}
+
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-COMMON: {{.*}}

>From 997a93358b8f05871333abdf72e3c1e05e09f891 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 5 Sep 2025 12:19:34 -0400
Subject: [PATCH 2/2] [ARM] Take advantage of built-in mod of shift amount in
 variable-shift rotations

Just like in AArch64, but only for rotates of course.
---
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    |   2 +-
 llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp       | 125 +++
 llvm/test/CodeGen/ARM/rotate-add.ll           |   2 +-
 llvm/test/CodeGen/ARM/shift-mod.ll            | 780 +-----------------
 4 files changed, 142 insertions(+), 767 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 54bdb8750f709..ea2d322c6d23b 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -530,7 +530,7 @@ char AArch64DAGToDAGISelLegacy::ID = 0;
 INITIALIZE_PASS(AArch64DAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
 
 /// isIntImmediate - This method tests to see if the node is a constant
-/// operand. If so Imm will receive the 32-bit value.
+/// operand. If so Imm will receive the 64-bit value.
 static bool isIntImmediate(const SDNode *N, uint64_t &Imm) {
   if (const ConstantSDNode *C = dyn_cast<const ConstantSDNode>(N)) {
     Imm = C->getZExtValue();
diff --git a/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp b/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
index 847b7af5a9b11..d9120221ac36a 100644
--- a/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
+++ b/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
@@ -306,6 +306,8 @@ class ARMDAGToDAGISel : public SelectionDAGISel {
 
   bool tryInsertVectorElt(SDNode *N);
 
+  bool tryShiftAmountMod(SDNode *N);
+
   bool tryReadRegister(SDNode *N);
   bool tryWriteRegister(SDNode *N);
 
@@ -3155,6 +3157,125 @@ bool ARMDAGToDAGISel::tryInsertVectorElt(SDNode *N) {
   return false;
 }
 
+/// tryShiftAmountMod - Take advantage of built-in mod of shift amount in
+/// variable shift/rotate instructions.
+bool ARMDAGToDAGISel::tryShiftAmountMod(SDNode *N) {
+  EVT VT = N->getValueType(0);
+
+  if (VT != MVT::i32)
+    return false;
+
+  // Select ROR by register; in ARM state this is modeled as MOVsr with a ROR
+  // shifter operand, while in Thumb we use tROR/t2RORrr directly.
+
+  SDValue ShiftAmt = N->getOperand(1);
+  SDLoc DL(N);
+  SDValue NewShiftAmt;
+
+  if (ShiftAmt->getOpcode() == ISD::ADD || ShiftAmt->getOpcode() == ISD::SUB) {
+    SDValue Add0 = ShiftAmt->getOperand(0);
+    SDValue Add1 = ShiftAmt->getOperand(1);
+    unsigned Add0Imm;
+    unsigned Add1Imm;
+
+    if (isInt32Immediate(Add1, Add1Imm) && ((Add1Imm & 31) == 0)) {
+      NewShiftAmt = Add0;
+    } else if (ShiftAmt->getOpcode() == ISD::SUB &&
+               isInt32Immediate(Add0, Add0Imm) && Add0Imm != 0 &&
+               ((Add0Imm & 31) == 0)) {
+
+      unsigned NegOpc =
+          Subtarget->isThumb()
+              ? (Subtarget->hasThumb2() ? ARM::t2RSBri : ARM::tRSB)
+              : ARM::RSBri;
+
+      SDValue ZeroImm = CurDAG->getTargetConstant(0, DL, MVT::i32);
+
+      if (Subtarget->isThumb2()) {
+        SDValue Ops[] = {Add1, ZeroImm, getAL(CurDAG, DL),
+                         CurDAG->getRegister(0, MVT::i32),
+                         CurDAG->getRegister(0, MVT::i32)};
+        MachineSDNode *Neg = CurDAG->getMachineNode(NegOpc, DL, MVT::i32, Ops);
+        NewShiftAmt = SDValue(Neg, 0);
+      } else if (Subtarget->isThumb1Only()) {
+        SDValue Ops[] = {CurDAG->getRegister(ARM::CPSR, MVT::i32), Add1,
+                         getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32)};
+        MachineSDNode *Neg = CurDAG->getMachineNode(NegOpc, DL, MVT::i32, Ops);
+        NewShiftAmt = SDValue(Neg, 0);
+      } else {
+        SDValue Ops[] = {Add1, ZeroImm, getAL(CurDAG, DL),
+                         CurDAG->getRegister(0, MVT::i32),
+                         CurDAG->getRegister(0, MVT::i32)};
+        MachineSDNode *Neg = CurDAG->getMachineNode(NegOpc, DL, MVT::i32, Ops);
+        NewShiftAmt = SDValue(Neg, 0);
+      }
+    } else if (ShiftAmt->getOpcode() == ISD::SUB &&
+               isInt32Immediate(Add0, Add0Imm) && ((Add0Imm & 31) == 31)) {
+      unsigned NotOpc = Subtarget->isThumb()
+                            ? (Subtarget->isThumb2() ? ARM::t2MVNr : ARM::tMVN)
+                            : ARM::MVNr;
+
+      if (Subtarget->isThumb2()) {
+        SDValue Ops[] = {Add1, getAL(CurDAG, DL),
+                         CurDAG->getRegister(0, MVT::i32),
+                         CurDAG->getRegister(0, MVT::i32)};
+        MachineSDNode *Not = CurDAG->getMachineNode(NotOpc, DL, MVT::i32, Ops);
+        NewShiftAmt = SDValue(Not, 0);
+      } else if (Subtarget->isThumb1Only()) {
+        SDValue Ops[] = {CurDAG->getRegister(ARM::CPSR, MVT::i32), Add1,
+                         getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32)};
+        MachineSDNode *Not = CurDAG->getMachineNode(NotOpc, DL, MVT::i32, Ops);
+        NewShiftAmt = SDValue(Not, 0);
+      } else {
+        SDValue Ops[] = {Add1, getAL(CurDAG, DL),
+                         CurDAG->getRegister(0, MVT::i32),
+                         CurDAG->getRegister(0, MVT::i32)};
+        MachineSDNode *Not = CurDAG->getMachineNode(NotOpc, DL, MVT::i32, Ops);
+        NewShiftAmt = SDValue(Not, 0);
+      }
+    } else {
+      return false;
+    }
+  } else {
+    // Check if shift amount is masked with AND covering low 5 bits
+    unsigned MaskImm;
+    if (!isOpcWithIntImmediate(ShiftAmt.getNode(), ISD::AND, MaskImm))
+      return false;
+
+    if ((unsigned)llvm::countr_one(MaskImm) < 5)
+      return false;
+
+    NewShiftAmt = ShiftAmt->getOperand(0);
+  }
+
+  if (Subtarget->isThumb()) {
+    if (Subtarget->isThumb1Only()) {
+      SDValue Ops[] = {CurDAG->getRegister(ARM::CPSR, MVT::i32),
+                       N->getOperand(0), NewShiftAmt, getAL(CurDAG, DL),
+                       CurDAG->getRegister(0, MVT::i32)};
+      CurDAG->SelectNodeTo(N, ARM::tROR, VT, Ops);
+    } else {
+      SDValue Ops[] = {N->getOperand(0), NewShiftAmt, getAL(CurDAG, DL),
+                       CurDAG->getRegister(0, MVT::i32),
+                       CurDAG->getRegister(0, MVT::i32)};
+      CurDAG->SelectNodeTo(N, ARM::t2RORrr, VT, Ops);
+    }
+  } else {
+    SDValue BaseReg = N->getOperand(0);
+    SDValue ShReg = NewShiftAmt;
+    SDValue OpcEnc = CurDAG->getTargetConstant(
+        ARM_AM::getSORegOpc(ARM_AM::ror, 0), DL, MVT::i32);
+    SDValue Ops[] = {BaseReg,
+                     ShReg,
+                     OpcEnc,
+                     getAL(CurDAG, DL),
+                     CurDAG->getRegister(0, MVT::i32),
+                     CurDAG->getRegister(0, MVT::i32)};
+    CurDAG->SelectNodeTo(N, ARM::MOVsr, VT, Ops);
+  }
+  return true;
+}
+
 bool ARMDAGToDAGISel::transformFixedFloatingPointConversion(SDNode *N,
                                                             SDNode *FMul,
                                                             bool IsUnsigned,
@@ -3728,6 +3849,10 @@ void ARMDAGToDAGISel::Select(SDNode *N) {
     if (tryV6T2BitfieldExtractOp(N, true))
       return;
     break;
+  case ISD::ROTR:
+    if (tryShiftAmountMod(N))
+      return;
+    break;
   case ISD::FP_TO_UINT:
   case ISD::FP_TO_SINT:
   case ISD::FP_TO_UINT_SAT:
diff --git a/llvm/test/CodeGen/ARM/rotate-add.ll b/llvm/test/CodeGen/ARM/rotate-add.ll
index fd3055e5e2725..0a2e9a37dbdb8 100644
--- a/llvm/test/CodeGen/ARM/rotate-add.ll
+++ b/llvm/test/CodeGen/ARM/rotate-add.ll
@@ -29,7 +29,7 @@ define i32 @test_simple_rotr(i32 %x) {
 define i32 @test_rotl_var(i32 %x, i32 %y) {
 ; CHECK-LABEL: test_rotl_var:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    rsb r1, r1, #32
+; CHECK-NEXT:    rsb r1, r1, #0
 ; CHECK-NEXT:    ror r0, r0, r1
 ; CHECK-NEXT:    bx lr
   %shl = shl i32 %x, %y
diff --git a/llvm/test/CodeGen/ARM/shift-mod.ll b/llvm/test/CodeGen/ARM/shift-mod.ll
index 3ba591e11acd1..8e01dcb3c3aee 100644
--- a/llvm/test/CodeGen/ARM/shift-mod.ll
+++ b/llvm/test/CodeGen/ARM/shift-mod.ll
@@ -6,738 +6,6 @@
 ; RUN: llc -mtriple=thumbv7m -mattr=+strict-align %s -o - | FileCheck %s --check-prefixes=CHECK-COMMON,CHECK-ALIGN
 ; RUN: llc -mtriple=thumbv6m %s -o - | FileCheck %s --check-prefix=CHECK-V6M
 
-; -----------------------------------------------------------------
-; LSL (shl) group
-; -----------------------------------------------------------------
-
-; (amount = amt + 32) => should remove the ADD and emit LSL directly.
-define i32 @lsl_add_mod(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsl_add_mod:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    add r1, r1, #32
-; CHECK-NEXT:    lsl r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsl_add_mod:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    add r1, r1, #32
-; CHECK-ARM-NEXT:    lsl r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsl_add_mod:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    add r1, r1, #32
-; CHECK-BE-NEXT:    lsl r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsl_add_mod:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    adds r1, #32
-; CHECK-THUMB-NEXT:    lsls r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsl_add_mod:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    adds r1, #32
-; CHECK-ALIGN-NEXT:    lsls r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsl_add_mod:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    adds r1, #32
-; CHECK-V6M-NEXT:    lsls r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = add i32 %amt, 32
-  %r  = shl i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = 32 - amt) => should become RSB/NEG then used by LSL.
-define i32 @lsl_sub_rsb(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsl_sub_rsb:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #32
-; CHECK-NEXT:    lsl r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsl_sub_rsb:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #32
-; CHECK-ARM-NEXT:    lsl r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsl_sub_rsb:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #32
-; CHECK-BE-NEXT:    lsl r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsl_sub_rsb:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
-; CHECK-THUMB-NEXT:    lsls r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsl_sub_rsb:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
-; CHECK-ALIGN-NEXT:    lsls r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsl_sub_rsb:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #32
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    lsls r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 32, %amt
-  %r  = shl i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = 31 - amt) => should become MVN(amt) then used by LSL.
-define i32 @lsl_sub_mvn(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsl_sub_mvn:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #31
-; CHECK-NEXT:    lsl r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsl_sub_mvn:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #31
-; CHECK-ARM-NEXT:    lsl r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsl_sub_mvn:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #31
-; CHECK-BE-NEXT:    lsl r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsl_sub_mvn:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
-; CHECK-THUMB-NEXT:    lsls r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsl_sub_mvn:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
-; CHECK-ALIGN-NEXT:    lsls r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsl_sub_mvn:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    lsls r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 31, %amt
-  %r  = shl i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = amt & 31) => AND is redundant; should be removed and emit LSL only.
-define i32 @lsl_and_mask(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsl_and_mask:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    and r1, r1, #31
-; CHECK-NEXT:    lsl r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsl_and_mask:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    and r1, r1, #31
-; CHECK-ARM-NEXT:    lsl r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsl_and_mask:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    and r1, r1, #31
-; CHECK-BE-NEXT:    lsl r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsl_and_mask:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    and r1, r1, #31
-; CHECK-THUMB-NEXT:    lsls r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsl_and_mask:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    and r1, r1, #31
-; CHECK-ALIGN-NEXT:    lsls r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsl_and_mask:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    ands r2, r1
-; CHECK-V6M-NEXT:    lsls r0, r2
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = and i32 %amt, 31
-  %r  = shl i32 %val, %sa
-  ret i32 %r
-}
-
-; -----------------------------------------------------------------
-; LSR (logical right) group
-; -----------------------------------------------------------------
-
-; (amount = amt + 32) => should remove the ADD and emit LSR directly.
-define i32 @lsr_add_mod(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsr_add_mod:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    add r1, r1, #32
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsr_add_mod:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    add r1, r1, #32
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsr_add_mod:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    add r1, r1, #32
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsr_add_mod:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    adds r1, #32
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsr_add_mod:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    adds r1, #32
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsr_add_mod:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    adds r1, #32
-; CHECK-V6M-NEXT:    lsrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = add i32 %amt, 32
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = 32 - amt) => should become RSB/NEG then used by LSR (and in some targets may be lowered to ROR form).
-define i32 @lsr_sub_rsb(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsr_sub_rsb:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #32
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsr_sub_rsb:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #32
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsr_sub_rsb:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #32
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsr_sub_rsb:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsr_sub_rsb:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsr_sub_rsb:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #32
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    lsrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 32, %amt
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = 31 - amt) => should become MVN(amt) then used by LSR.
-define i32 @lsr_sub_mvn(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsr_sub_mvn:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #31
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsr_sub_mvn:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #31
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsr_sub_mvn:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #31
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsr_sub_mvn:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsr_sub_mvn:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsr_sub_mvn:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    lsrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 31, %amt
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = amt & 31) => AND is redundant; should be removed and emit LSR only.
-define i32 @lsr_and_mask(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: lsr_and_mask:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    and r1, r1, #31
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: lsr_and_mask:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    and r1, r1, #31
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: lsr_and_mask:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    and r1, r1, #31
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: lsr_and_mask:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    and r1, r1, #31
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: lsr_and_mask:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    and r1, r1, #31
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: lsr_and_mask:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    ands r2, r1
-; CHECK-V6M-NEXT:    lsrs r0, r2
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = and i32 %amt, 31
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-; -----------------------------------------------------------------
-; ASR (arithmetic right) group
-; -----------------------------------------------------------------
-
-; (amount = amt + 32) => should remove the ADD and emit ASR directly.
-; CHECK-LABEL: asr_add_mod:
-; CHECK-NOT: add
-; CHECK: asr
-; THUMB-LABEL: asr_add_mod:
-; THUMB-NOT: add
-; THUMB: asr
-define i32 @asr_add_mod(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: asr_add_mod:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    add r1, r1, #32
-; CHECK-NEXT:    asr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: asr_add_mod:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    add r1, r1, #32
-; CHECK-ARM-NEXT:    asr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: asr_add_mod:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    add r1, r1, #32
-; CHECK-BE-NEXT:    asr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: asr_add_mod:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    adds r1, #32
-; CHECK-THUMB-NEXT:    asrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: asr_add_mod:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    adds r1, #32
-; CHECK-ALIGN-NEXT:    asrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: asr_add_mod:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    adds r1, #32
-; CHECK-V6M-NEXT:    asrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = add i32 %amt, 32
-  %r  = ashr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = 32 - amt) => should become RSB/NEG then used by ASR.
-; CHECK-LABEL: asr_sub_rsb:
-; CHECK: rsb
-; CHECK: asr
-; THUMB-LABEL: asr_sub_rsb:
-; THUMB: rsb
-; THUMB: asr
-define i32 @asr_sub_rsb(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: asr_sub_rsb:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #32
-; CHECK-NEXT:    asr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: asr_sub_rsb:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #32
-; CHECK-ARM-NEXT:    asr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: asr_sub_rsb:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #32
-; CHECK-BE-NEXT:    asr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: asr_sub_rsb:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
-; CHECK-THUMB-NEXT:    asrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: asr_sub_rsb:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
-; CHECK-ALIGN-NEXT:    asrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: asr_sub_rsb:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #32
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    asrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 32, %amt
-  %r  = ashr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = 31 - amt) => should become MVN(amt) then used by ASR.
-; CHECK-LABEL: asr_sub_mvn:
-; CHECK: mvn
-; CHECK: asr
-; THUMB-LABEL: asr_sub_mvn:
-; THUMB: mvn
-; THUMB: asr
-define i32 @asr_sub_mvn(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: asr_sub_mvn:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #31
-; CHECK-NEXT:    asr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: asr_sub_mvn:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #31
-; CHECK-ARM-NEXT:    asr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: asr_sub_mvn:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #31
-; CHECK-BE-NEXT:    asr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: asr_sub_mvn:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
-; CHECK-THUMB-NEXT:    asrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: asr_sub_mvn:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
-; CHECK-ALIGN-NEXT:    asrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: asr_sub_mvn:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    asrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 31, %amt
-  %r  = ashr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = amt & 31) => AND is redundant; should be removed and emit ASR only.
-; CHECK-LABEL: asr_and_mask:
-; CHECK-NOT: and
-; CHECK: asr
-; THUMB-LABEL: asr_and_mask:
-; THUMB-NOT: and
-; THUMB: asr
-define i32 @asr_and_mask(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: asr_and_mask:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    and r1, r1, #31
-; CHECK-NEXT:    asr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: asr_and_mask:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    and r1, r1, #31
-; CHECK-ARM-NEXT:    asr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: asr_and_mask:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    and r1, r1, #31
-; CHECK-BE-NEXT:    asr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: asr_and_mask:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    and r1, r1, #31
-; CHECK-THUMB-NEXT:    asrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: asr_and_mask:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    and r1, r1, #31
-; CHECK-ALIGN-NEXT:    asrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: asr_and_mask:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    ands r2, r1
-; CHECK-V6M-NEXT:    asrs r0, r2
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = and i32 %amt, 31
-  %r  = ashr i32 %val, %sa
-  ret i32 %r
-}
-
-; (amount = amt + 32) => remove ADD and emit ROR/LSR as appropriate (we check for absence of ADD and presence of LSR/ROR)
-define i32 @ror_add_mod(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: ror_add_mod:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    add r1, r1, #32
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: ror_add_mod:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    add r1, r1, #32
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: ror_add_mod:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    add r1, r1, #32
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: ror_add_mod:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    adds r1, #32
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: ror_add_mod:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    adds r1, #32
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: ror_add_mod:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    adds r1, #32
-; CHECK-V6M-NEXT:    lsrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = add i32 %amt, 32
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-define i32 @ror_sub_rsb(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: ror_sub_rsb:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #32
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: ror_sub_rsb:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #32
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: ror_sub_rsb:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #32
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: ror_sub_rsb:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: ror_sub_rsb:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: ror_sub_rsb:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #32
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    lsrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 32, %amt
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-define i32 @ror_sub_mvn(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: ror_sub_mvn:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    rsb r1, r1, #31
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: ror_sub_mvn:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #31
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: ror_sub_mvn:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #31
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: ror_sub_mvn:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: ror_sub_mvn:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: ror_sub_mvn:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    subs r1, r2, r1
-; CHECK-V6M-NEXT:    lsrs r0, r1
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = sub i32 31, %amt
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
-
-define i32 @ror_and_mask(i32 %val, i32 %amt) #0 {
-; CHECK-LABEL: ror_and_mask:
-; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    and r1, r1, #31
-; CHECK-NEXT:    lsr r0, r0, r1
-; CHECK-NEXT:    bx lr
-;
-; CHECK-ARM-LABEL: ror_and_mask:
-; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    and r1, r1, #31
-; CHECK-ARM-NEXT:    lsr r0, r0, r1
-; CHECK-ARM-NEXT:    bx lr
-;
-; CHECK-BE-LABEL: ror_and_mask:
-; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    and r1, r1, #31
-; CHECK-BE-NEXT:    lsr r0, r0, r1
-; CHECK-BE-NEXT:    bx lr
-;
-; CHECK-THUMB-LABEL: ror_and_mask:
-; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    and r1, r1, #31
-; CHECK-THUMB-NEXT:    lsrs r0, r1
-; CHECK-THUMB-NEXT:    bx lr
-;
-; CHECK-ALIGN-LABEL: ror_and_mask:
-; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    and r1, r1, #31
-; CHECK-ALIGN-NEXT:    lsrs r0, r1
-; CHECK-ALIGN-NEXT:    bx lr
-;
-; CHECK-V6M-LABEL: ror_and_mask:
-; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    ands r2, r1
-; CHECK-V6M-NEXT:    lsrs r0, r2
-; CHECK-V6M-NEXT:    bx lr
-entry:
-  %sa = and i32 %amt, 31
-  %r  = lshr i32 %val, %sa
-  ret i32 %r
-}
 
 
 declare i32 @llvm.fshl.i32(i32, i32, i32)
@@ -752,37 +20,31 @@ declare i32 @llvm.fshr.i32(i32, i32, i32)
 define i32 @fshl_add_mod(i32 %x, i32 %amt) #0 {
 ; CHECK-ARM-LABEL: fshl_add_mod:
 ; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    mvn r2, #31
-; CHECK-ARM-NEXT:    sub r1, r2, r1
+; CHECK-ARM-NEXT:    rsb r1, r1, #0
 ; CHECK-ARM-NEXT:    ror r0, r0, r1
 ; CHECK-ARM-NEXT:    bx lr
 ;
 ; CHECK-BE-LABEL: fshl_add_mod:
 ; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    mvn r2, #31
-; CHECK-BE-NEXT:    sub r1, r2, r1
+; CHECK-BE-NEXT:    rsb r1, r1, #0
 ; CHECK-BE-NEXT:    ror r0, r0, r1
 ; CHECK-BE-NEXT:    bx lr
 ;
 ; CHECK-THUMB-LABEL: fshl_add_mod:
 ; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    mvn r2, #31
-; CHECK-THUMB-NEXT:    subs r1, r2, r1
+; CHECK-THUMB-NEXT:    rsbs r1, r1, #0
 ; CHECK-THUMB-NEXT:    rors r0, r1
 ; CHECK-THUMB-NEXT:    bx lr
 ;
 ; CHECK-ALIGN-LABEL: fshl_add_mod:
 ; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    mvn r2, #31
-; CHECK-ALIGN-NEXT:    subs r1, r2, r1
+; CHECK-ALIGN-NEXT:    rsbs r1, r1, #0
 ; CHECK-ALIGN-NEXT:    rors r0, r1
 ; CHECK-ALIGN-NEXT:    bx lr
 ;
 ; CHECK-V6M-LABEL: fshl_add_mod:
 ; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    mvns r2, r2
-; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    rsbs r1, r1, #0
 ; CHECK-V6M-NEXT:    rors r0, r1
 ; CHECK-V6M-NEXT:    bx lr
 entry:
@@ -801,31 +63,26 @@ entry:
 define i32 @fshl_sub_rsb(i32 %x, i32 %amt) #0 {
 ; CHECK-ARM-LABEL: fshl_sub_rsb:
 ; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    sub r1, r1, #32
 ; CHECK-ARM-NEXT:    ror r0, r0, r1
 ; CHECK-ARM-NEXT:    bx lr
 ;
 ; CHECK-BE-LABEL: fshl_sub_rsb:
 ; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    sub r1, r1, #32
 ; CHECK-BE-NEXT:    ror r0, r0, r1
 ; CHECK-BE-NEXT:    bx lr
 ;
 ; CHECK-THUMB-LABEL: fshl_sub_rsb:
 ; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    subs r1, #32
 ; CHECK-THUMB-NEXT:    rors r0, r1
 ; CHECK-THUMB-NEXT:    bx lr
 ;
 ; CHECK-ALIGN-LABEL: fshl_sub_rsb:
 ; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    subs r1, #32
 ; CHECK-ALIGN-NEXT:    rors r0, r1
 ; CHECK-ALIGN-NEXT:    bx lr
 ;
 ; CHECK-V6M-LABEL: fshl_sub_rsb:
 ; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    subs r1, #32
 ; CHECK-V6M-NEXT:    rors r0, r1
 ; CHECK-V6M-NEXT:    bx lr
 entry:
@@ -923,31 +180,26 @@ entry:
 define i32 @fshr_add_mod(i32 %x, i32 %amt) #0 {
 ; CHECK-ARM-LABEL: fshr_add_mod:
 ; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    add r1, r1, #32
 ; CHECK-ARM-NEXT:    ror r0, r0, r1
 ; CHECK-ARM-NEXT:    bx lr
 ;
 ; CHECK-BE-LABEL: fshr_add_mod:
 ; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    add r1, r1, #32
 ; CHECK-BE-NEXT:    ror r0, r0, r1
 ; CHECK-BE-NEXT:    bx lr
 ;
 ; CHECK-THUMB-LABEL: fshr_add_mod:
 ; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    adds r1, #32
 ; CHECK-THUMB-NEXT:    rors r0, r1
 ; CHECK-THUMB-NEXT:    bx lr
 ;
 ; CHECK-ALIGN-LABEL: fshr_add_mod:
 ; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    adds r1, #32
 ; CHECK-ALIGN-NEXT:    rors r0, r1
 ; CHECK-ALIGN-NEXT:    bx lr
 ;
 ; CHECK-V6M-LABEL: fshr_add_mod:
 ; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    adds r1, #32
 ; CHECK-V6M-NEXT:    rors r0, r1
 ; CHECK-V6M-NEXT:    bx lr
 entry:
@@ -960,32 +212,31 @@ entry:
 define i32 @fshr_sub_rsb(i32 %x, i32 %amt) #0 {
 ; CHECK-ARM-LABEL: fshr_sub_rsb:
 ; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #32
+; CHECK-ARM-NEXT:    rsb r1, r1, #0
 ; CHECK-ARM-NEXT:    ror r0, r0, r1
 ; CHECK-ARM-NEXT:    bx lr
 ;
 ; CHECK-BE-LABEL: fshr_sub_rsb:
 ; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #32
+; CHECK-BE-NEXT:    rsb r1, r1, #0
 ; CHECK-BE-NEXT:    ror r0, r0, r1
 ; CHECK-BE-NEXT:    bx lr
 ;
 ; CHECK-THUMB-LABEL: fshr_sub_rsb:
 ; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #32
+; CHECK-THUMB-NEXT:    rsbs r1, r1, #0
 ; CHECK-THUMB-NEXT:    rors r0, r1
 ; CHECK-THUMB-NEXT:    bx lr
 ;
 ; CHECK-ALIGN-LABEL: fshr_sub_rsb:
 ; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #32
+; CHECK-ALIGN-NEXT:    rsbs r1, r1, #0
 ; CHECK-ALIGN-NEXT:    rors r0, r1
 ; CHECK-ALIGN-NEXT:    bx lr
 ;
 ; CHECK-V6M-LABEL: fshr_sub_rsb:
 ; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #32
-; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    rsbs r1, r1, #0
 ; CHECK-V6M-NEXT:    rors r0, r1
 ; CHECK-V6M-NEXT:    bx lr
 entry:
@@ -998,32 +249,31 @@ entry:
 define i32 @fshr_sub_mvn(i32 %x, i32 %amt) #0 {
 ; CHECK-ARM-LABEL: fshr_sub_mvn:
 ; CHECK-ARM:       @ %bb.0: @ %entry
-; CHECK-ARM-NEXT:    rsb r1, r1, #31
+; CHECK-ARM-NEXT:    mvn r1, r1
 ; CHECK-ARM-NEXT:    ror r0, r0, r1
 ; CHECK-ARM-NEXT:    bx lr
 ;
 ; CHECK-BE-LABEL: fshr_sub_mvn:
 ; CHECK-BE:       @ %bb.0: @ %entry
-; CHECK-BE-NEXT:    rsb r1, r1, #31
+; CHECK-BE-NEXT:    mvn r1, r1
 ; CHECK-BE-NEXT:    ror r0, r0, r1
 ; CHECK-BE-NEXT:    bx lr
 ;
 ; CHECK-THUMB-LABEL: fshr_sub_mvn:
 ; CHECK-THUMB:       @ %bb.0: @ %entry
-; CHECK-THUMB-NEXT:    rsb.w r1, r1, #31
+; CHECK-THUMB-NEXT:    mvns r1, r1
 ; CHECK-THUMB-NEXT:    rors r0, r1
 ; CHECK-THUMB-NEXT:    bx lr
 ;
 ; CHECK-ALIGN-LABEL: fshr_sub_mvn:
 ; CHECK-ALIGN:       @ %bb.0: @ %entry
-; CHECK-ALIGN-NEXT:    rsb.w r1, r1, #31
+; CHECK-ALIGN-NEXT:    mvns r1, r1
 ; CHECK-ALIGN-NEXT:    rors r0, r1
 ; CHECK-ALIGN-NEXT:    bx lr
 ;
 ; CHECK-V6M-LABEL: fshr_sub_mvn:
 ; CHECK-V6M:       @ %bb.0: @ %entry
-; CHECK-V6M-NEXT:    movs r2, #31
-; CHECK-V6M-NEXT:    subs r1, r2, r1
+; CHECK-V6M-NEXT:    mvns r1, r1
 ; CHECK-V6M-NEXT:    rors r0, r1
 ; CHECK-V6M-NEXT:    bx lr
 entry:



More information about the llvm-commits mailing list