diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index fdef49e310f81..ccf918f0b6dbe 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -11,11 +11,13 @@ //===----------------------------------------------------------------------===// #include "InstCombineInternal.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/SetVector.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/Value.h" #include "llvm/Support/KnownBits.h" #include "llvm/Transforms/InstCombine/InstCombiner.h" #include @@ -969,6 +971,25 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) { Changed = true; } + const APInt *C1; + Value *V1; + // OP = { lshr, ashr } + // trunc ( OP i8 C1, V1) to i1 -> icmp eq V1, log_2(C1) iff C1 is power of 2 + if (DestWidth == 1 && match(Src, m_Shr(m_Power2(C1), m_Value(V1)))) { + Value *Right = ConstantInt::get(V1->getType(), C1->countr_zero()); + Value *Icmp = Builder.CreateICmpEQ(V1, Right); + return replaceInstUsesWith(Trunc, Icmp); + } + + // OP = { lshr, ashr } + // trunc ( OP i8 C1, V1) to i1 -> icmp ult V1, log_2(C1 + 1) iff (C1 + 1) is + // power of 2 + if (DestWidth == 1 && match(Src, m_Shr(m_LowBitMask(C1), m_Value(V1)))) { + Value *Right = ConstantInt::get(V1->getType(), C1->countr_one()); + Value *Icmp = Builder.CreateICmpULT(V1, Right); + return replaceInstUsesWith(Trunc, Icmp); + } + return Changed ? &Trunc : nullptr; } diff --git a/llvm/test/Transforms/InstCombine/trunc-lshr.ll b/llvm/test/Transforms/InstCombine/trunc-lshr.ll index 4364b09cfa709..c443b35cb1c1e 100644 --- a/llvm/test/Transforms/InstCombine/trunc-lshr.ll +++ b/llvm/test/Transforms/InstCombine/trunc-lshr.ll @@ -1,6 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -S -passes=instcombine < %s | FileCheck %s +declare void @use(i8) + define i1 @test1(i32 %i, ptr %p) { ; CHECK-LABEL: define i1 @test1( ; CHECK-SAME: i32 [[I:%.*]], ptr [[P:%.*]]) { @@ -93,3 +95,127 @@ define i1 @test5(i32 %i, ptr %p) { ret i1 %op } +define i1 @fold_lshr_power_of_2(i8 %x) { +; CHECK-LABEL: define i1 @fold_lshr_power_of_2( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %lshr = lshr i8 16, %x ; 16 is a power of 2 + %trunc = trunc i8 %lshr to i1 + ret i1 %trunc +} + +define i1 @fold_lshr_power_of_2_minus_1(i8 %x) { +; CHECK-LABEL: define i1 @fold_lshr_power_of_2_minus_1( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %lshr = lshr i8 15, %x + %trunc = trunc i8 %lshr to i1 + ret i1 %trunc +} + +define i1 @fold_ashr_power_of_2(i8 %x) { +; CHECK-LABEL: define i1 @fold_ashr_power_of_2( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %ashr = ashr i8 16, %x + %trunc = trunc i8 %ashr to i1 + ret i1 %trunc +} + +define i1 @fold_ashr_power_of_2_minus_1(i8 %x) { +; CHECK-LABEL: define i1 @fold_ashr_power_of_2_minus_1( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %ashr = ashr i8 15, %x ; (15 + 1) is a power of 2 + %trunc = trunc i8 %ashr to i1 + ret i1 %trunc +} + +define i1 @fold_lshr_power_of_2_multi_use(i8 %x) { +; CHECK-LABEL: define i1 @fold_lshr_power_of_2_multi_use( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 16, [[X]] +; CHECK-NEXT: call void @use(i8 [[LSHR]]) +; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %lshr = lshr i8 16, %x ; 16 is a power of 2 + call void @use(i8 %lshr) + %trunc = trunc i8 %lshr to i1 + ret i1 %trunc +} + +define i1 @fold_lshr_power_of_2_minus_1_multi_use(i8 %x) { +; CHECK-LABEL: define i1 @fold_lshr_power_of_2_minus_1_multi_use( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 15, [[X]] +; CHECK-NEXT: call void @use(i8 [[LSHR]]) +; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %lshr = lshr i8 15, %x + call void @use(i8 %lshr) + %trunc = trunc i8 %lshr to i1 + ret i1 %trunc +} + +define i1 @fold_ashr_power_of_2_multi_use(i8 %x) { +; CHECK-LABEL: define i1 @fold_ashr_power_of_2_multi_use( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[ASHR:%.*]] = lshr i8 16, [[X]] +; CHECK-NEXT: call void @use(i8 [[ASHR]]) +; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %ashr = ashr i8 16, %x + call void @use(i8 %ashr) + %trunc = trunc i8 %ashr to i1 + ret i1 %trunc +} + +define i1 @fold_ashr_power_of_2_minus_1_multi_use(i8 %x) { +; CHECK-LABEL: define i1 @fold_ashr_power_of_2_minus_1_multi_use( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[ASHR:%.*]] = lshr i8 15, [[X]] +; CHECK-NEXT: call void @use(i8 [[ASHR]]) +; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %ashr = ashr i8 15, %x ; (15 + 1) is a power of 2 + call void @use(i8 %ashr) + %trunc = trunc i8 %ashr to i1 + ret i1 %trunc +} + +define i1 @negative_test_fold_lshr(i8 %x) { +; CHECK-LABEL: define i1 @negative_test_fold_lshr( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 9, [[X]] +; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[LSHR]] to i1 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %lshr = lshr i8 9, %x ; 9 or (9 + 1) is not a power of 2 + %trunc = trunc i8 %lshr to i1 + ret i1 %trunc +} + +; Negative Test for arithmetic shift right +define i1 @negative_test_fold_ashr(i8 %x) { +; CHECK-LABEL: define i1 @negative_test_fold_ashr( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[ASHR:%.*]] = lshr i8 9, [[X]] +; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[ASHR]] to i1 +; CHECK-NEXT: ret i1 [[TRUNC]] +; + %ashr = ashr i8 9, %x ; 9 or (9 + 1) is not a power of 2 + %trunc = trunc i8 %ashr to i1 + ret i1 %trunc +}