[llvm] [Scalar] Dedicated pass for identifying redundant operations on packed bytes (PR #146364)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 9 07:19:43 PDT 2025


================
@@ -0,0 +1,302 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=packedintcombine %s | FileCheck %s --check-prefix=LAZY
+; RUN: opt -S -passes=packedintcombine -packedint-aggressive-rewriter %s | FileCheck %s --check-prefix=AGGRESSIVE
+
+define i16 @top_bytes(i32 %a, i32 %b) {
+; LAZY-LABEL: define i16 @top_bytes(
+; LAZY-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; LAZY-NEXT:    [[A_MASK:%.*]] = and i32 [[A]], -16777216
+; LAZY-NEXT:    [[A_LSHR:%.*]] = lshr i32 [[A_MASK]], 16
+; LAZY-NEXT:    [[B_MASK:%.*]] = and i32 [[B]], -16777216
+; LAZY-NEXT:    [[B_LSHR:%.*]] = lshr i32 [[B_MASK]], 24
+; LAZY-NEXT:    [[RES:%.*]] = or i32 [[A_LSHR]], [[B_LSHR]]
+; LAZY-NEXT:    [[TRUNC:%.*]] = trunc i32 [[RES]] to i16
+; LAZY-NEXT:    ret i16 [[TRUNC]]
+;
+; AGGRESSIVE-LABEL: define i16 @top_bytes(
+; AGGRESSIVE-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; AGGRESSIVE-NEXT:    [[B_SHIFT:%.*]] = lshr i32 [[B]], 24
+; AGGRESSIVE-NEXT:    [[B_SHIFT_CAST:%.*]] = trunc i32 [[B_SHIFT]] to i16
+; AGGRESSIVE-NEXT:    [[A_SHIFT:%.*]] = lshr i32 [[A]], 16
+; AGGRESSIVE-NEXT:    [[A_SHIFT_CAST:%.*]] = trunc i32 [[A_SHIFT]] to i16
+; AGGRESSIVE-NEXT:    [[A_MASK2:%.*]] = and i16 [[A_SHIFT_CAST]], -256
+; AGGRESSIVE-NEXT:    [[TRUNC_MERGE:%.*]] = or disjoint i16 [[B_SHIFT_CAST]], [[A_MASK2]]
+; AGGRESSIVE-NEXT:    ret i16 [[TRUNC_MERGE]]
+;
+  %a.mask = and i32 %a, u0xff000000
+  %a.lshr = lshr i32 %a.mask, 16
+  %b.mask = and i32 %b, u0xff000000
+  %b.lshr = lshr i32 %b.mask, 24
+  %res = or i32 %a.lshr, %b.lshr
+  %trunc = trunc i32 %res to i16
+  ret i16 %trunc
+}
+
+define i32 @bottom_bytes(i16 %a, i16 %b) {
+; LAZY-LABEL: define i32 @bottom_bytes(
+; LAZY-SAME: i16 [[A:%.*]], i16 [[B:%.*]]) {
+; LAZY-NEXT:    [[A_MASK:%.*]] = and i16 [[A]], 255
+; LAZY-NEXT:    [[A_SHL:%.*]] = shl i16 [[A_MASK]], 8
+; LAZY-NEXT:    [[B_MASK:%.*]] = and i16 [[B]], 255
+; LAZY-NEXT:    [[RES:%.*]] = or i16 [[A_SHL]], [[B_MASK]]
+; LAZY-NEXT:    [[ZEXT:%.*]] = zext i16 [[RES]] to i32
+; LAZY-NEXT:    [[SHL:%.*]] = shl i32 [[ZEXT]], 16
+; LAZY-NEXT:    ret i32 [[SHL]]
+;
+; AGGRESSIVE-LABEL: define i32 @bottom_bytes(
+; AGGRESSIVE-SAME: i16 [[A:%.*]], i16 [[B:%.*]]) {
+; AGGRESSIVE-NEXT:    [[A_CAST:%.*]] = zext i16 [[A]] to i32
+; AGGRESSIVE-NEXT:    [[B_MASK2:%.*]] = and i16 [[B]], 255
+; AGGRESSIVE-NEXT:    [[B_MASK_CAST:%.*]] = zext i16 [[B_MASK2]] to i32
+; AGGRESSIVE-NEXT:    [[B_SHIFT:%.*]] = shl i32 [[B_MASK_CAST]], 16
+; AGGRESSIVE-NEXT:    [[A_SHIFT:%.*]] = shl i32 [[A_CAST]], 24
+; AGGRESSIVE-NEXT:    [[SHL_MERGE:%.*]] = or disjoint i32 [[B_SHIFT]], [[A_SHIFT]]
+; AGGRESSIVE-NEXT:    ret i32 [[SHL_MERGE]]
+;
+  %a.mask = and i16 %a, u0x00ff
+  %a.shl = shl i16 %a.mask, 8
+  %b.mask = and i16 %b, u0x00ff
+  %res = or i16 %a.shl, %b.mask
+  %zext = zext i16 %res to i32
+  %shl = shl i32 %zext, 16
+  ret i32 %shl
+}
+
+define i32 @obtain_i32(i32 %from) {
+; LAZY-LABEL: define i32 @obtain_i32(
+; LAZY-SAME: i32 [[FROM:%.*]]) {
+; LAZY-NEXT:    ret i32 [[FROM]]
+;
+; AGGRESSIVE-LABEL: define i32 @obtain_i32(
+; AGGRESSIVE-SAME: i32 [[FROM:%.*]]) {
+; AGGRESSIVE-NEXT:    ret i32 [[FROM]]
+;
+  %get.0 = and i32 %from, 255
+  %shr.1 = lshr i32 %from, 8
+  %mask.1 = and i32 %shr.1, 255
+  %get.1 = shl i32 %mask.1, 8
+  %out.1 = or i32 %get.0, %get.1
+
+  %shr.2 = lshr i32 %from, 16
+  %mask.2 = and i32 %shr.2, 255
+  %get.2 = shl i32 %mask.2, 16
+  %shr.3 = lshr i32 %from, 24
+  %mask.3 = and i32 %shr.3, 255
+  %get.3 = shl i32 %mask.3, 24
+  %out.2 = or i32 %get.2, %get.3
+
+  %out = or i32 %out.1, %out.2
+  ret i32 %out
+}
+
+;; u0xff00ffff = -16711681
+define i32 @obtain_i32_masked(i32 %from) {
+; LAZY-LABEL: define i32 @obtain_i32_masked(
+; LAZY-SAME: i32 [[FROM:%.*]]) {
+; LAZY-NEXT:    [[FROM_MASK:%.*]] = and i32 [[FROM]], -16711681
+; LAZY-NEXT:    ret i32 [[FROM_MASK]]
+;
+; AGGRESSIVE-LABEL: define i32 @obtain_i32_masked(
+; AGGRESSIVE-SAME: i32 [[FROM:%.*]]) {
+; AGGRESSIVE-NEXT:    [[FROM_MASK:%.*]] = and i32 [[FROM]], -16711681
+; AGGRESSIVE-NEXT:    ret i32 [[FROM_MASK]]
+;
+  %get.0 = and i32 %from, 255
+  %shr.1 = lshr i32 %from, 8
+  %mask.1 = and i32 %shr.1, 255
+  %get.1 = shl i32 %mask.1, 8
+  %out.1 = or i32 %get.0, %get.1
+
+  %shr.3 = lshr i32 %from, 24
+  %mask.3 = and i32 %shr.3, 255
+  %get.3 = shl i32 %mask.3, 24
+  %out.2 = or i32 %out.1, %get.3
+
+  ret i32 %out.2
+}
+
+define i64 @obtain_i64(i64 %from) {
+; LAZY-LABEL: define i64 @obtain_i64(
+; LAZY-SAME: i64 [[FROM:%.*]]) {
+; LAZY-NEXT:    [[FROM_MASK:%.*]] = and i64 [[FROM]], 4294967295
+; LAZY-NEXT:    ret i64 [[FROM_MASK]]
+;
+; AGGRESSIVE-LABEL: define i64 @obtain_i64(
+; AGGRESSIVE-SAME: i64 [[FROM:%.*]]) {
+; AGGRESSIVE-NEXT:    [[FROM_MASK:%.*]] = and i64 [[FROM]], 4294967295
+; AGGRESSIVE-NEXT:    ret i64 [[FROM_MASK]]
+;
+  %mask.0 = and i64 %from, 255
+  %get.0 = shl i64 %mask.0, 0
+  %shr.1 = lshr i64 %from, 8
+  %mask.1 = and i64 %shr.1, 255
+  %get.1 = shl i64 %mask.1, 8
+  %out.1 = or i64 %get.0, %get.1
+
+  %shr.2 = lshr i64 %from, 16
+  %mask.2 = and i64 %shr.2, 255
+  %get.2 = shl i64 %mask.2, 16
+  %shr.3 = lshr i64 %from, 24
+  %mask.3 = and i64 %shr.3, 255
+  %get.3 = shl i64 %mask.3, 24
+  %out.2 = or i64 %get.2, %get.3
+
+  %out = or i64 %out.1, %out.2
+  ret i64 %out
+}
+
+define i64 @obtain_i64_shifted(i64 %from) {
+; LAZY-LABEL: define i64 @obtain_i64_shifted(
+; LAZY-SAME: i64 [[FROM:%.*]]) {
+; LAZY-NEXT:    [[FROM_SHIFT:%.*]] = shl i64 [[FROM]], 32
+; LAZY-NEXT:    ret i64 [[FROM_SHIFT]]
+;
+; AGGRESSIVE-LABEL: define i64 @obtain_i64_shifted(
+; AGGRESSIVE-SAME: i64 [[FROM:%.*]]) {
+; AGGRESSIVE-NEXT:    [[FROM_SHIFT:%.*]] = shl i64 [[FROM]], 32
+; AGGRESSIVE-NEXT:    ret i64 [[FROM_SHIFT]]
+;
+  %mask.0 = and i64 %from, 255
+  %get.0 = shl i64 %mask.0, 32
+  %shr.1 = lshr i64 %from, 8
+  %mask.1 = and i64 %shr.1, 255
+  %get.1 = shl i64 %mask.1, 40
+  %out.1 = or i64 %get.0, %get.1
+
+  %shr.2 = lshr i64 %from, 16
+  %mask.2 = and i64 %shr.2, 255
+  %get.2 = shl i64 %mask.2, 48
+  %shr.3 = lshr i64 %from, 24
+  %mask.3 = and i64 %shr.3, 255
+  %get.3 = shl i64 %mask.3, 56
+  %out.2 = or i64 %get.2, %get.3
+
+  %out = or i64 %out.1, %out.2
+  ret i64 %out
+}
+
+define i64 @obtain_i64_zext(i32 %from) {
+; LAZY-LABEL: define i64 @obtain_i64_zext(
+; LAZY-SAME: i32 [[FROM:%.*]]) {
+; LAZY-NEXT:    [[FROM_CAST:%.*]] = zext i32 [[FROM]] to i64
+; LAZY-NEXT:    [[FROM_SHIFT:%.*]] = shl i64 [[FROM_CAST]], 32
+; LAZY-NEXT:    ret i64 [[FROM_SHIFT]]
+;
+; AGGRESSIVE-LABEL: define i64 @obtain_i64_zext(
+; AGGRESSIVE-SAME: i32 [[FROM:%.*]]) {
+; AGGRESSIVE-NEXT:    [[FROM_CAST:%.*]] = zext i32 [[FROM]] to i64
+; AGGRESSIVE-NEXT:    [[FROM_SHIFT:%.*]] = shl i64 [[FROM_CAST]], 32
+; AGGRESSIVE-NEXT:    ret i64 [[FROM_SHIFT]]
+;
+  %mask.0 = and i32 %from, 255
+  %zext.0 = zext i32 %mask.0 to i64
+  %get.0 = shl i64 %zext.0, 32
+  %shr.1 = lshr i32 %from, 8
+  %mask.1 = and i32 %shr.1, 255
+  %zext.1 = zext i32 %mask.1 to i64
+  %get.1 = shl i64 %zext.1, 40
+  %out.1 = or i64 %get.0, %get.1
+
+  %shr.2 = lshr i32 %from, 16
+  %mask.2 = and i32 %shr.2, 255
+  %zext.2 = zext i32 %mask.2 to i64
+  %get.2 = shl i64 %zext.2, 48
+  %shr.3 = lshr i32 %from, 24
+  %mask.3 = and i32 %shr.3, 255
+  %zext.3 = zext i32 %mask.3 to i64
+  %get.3 = shl i64 %zext.3, 56
+  %out.2 = or i64 %get.2, %get.3
+
+  %out = or i64 %out.1, %out.2
+  ret i64 %out
+}
+
+define i64 @combine(i32 %bot, i32 %top) {
----------------
zGoldthorpe wrote:

I've made a new PR to canonicalise this particular pattern in [https://github.com/llvm/llvm-project/pull/147737](#147737)

https://github.com/llvm/llvm-project/pull/146364


More information about the llvm-commits mailing list