[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