diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index b82ed25a6d0cf..c6029b428ed39 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -120,6 +120,9 @@ static bool refineInstruction(SCCPSolver &Solver, }; if (isa(Inst)) { + if (Inst.hasNoSignedWrap() && Inst.hasNoUnsignedWrap()) + return false; + auto RangeA = GetRange(Inst.getOperand(0)); auto RangeB = GetRange(Inst.getOperand(1)); if (!Inst.hasNoUnsignedWrap()) { @@ -146,6 +149,24 @@ static bool refineInstruction(SCCPSolver &Solver, Inst.setNonNeg(); Changed = true; } + } else if (TruncInst *TI = dyn_cast(&Inst)) { + if (TI->hasNoSignedWrap() && TI->hasNoUnsignedWrap()) + return false; + + auto Range = GetRange(Inst.getOperand(0)); + uint64_t DestWidth = TI->getDestTy()->getScalarSizeInBits(); + if (!TI->hasNoUnsignedWrap()) { + if (Range.getActiveBits() <= DestWidth) { + TI->setHasNoUnsignedWrap(true); + Changed = true; + } + } + if (!TI->hasNoSignedWrap()) { + if (Range.getMinSignedBits() <= DestWidth) { + TI->setHasNoSignedWrap(true); + Changed = true; + } + } } return Changed; diff --git a/llvm/test/Transforms/PhaseOrdering/ARM/arm_mult_q15.ll b/llvm/test/Transforms/PhaseOrdering/ARM/arm_mult_q15.ll index 36bcda4c43ca9..c6126727598ef 100644 --- a/llvm/test/Transforms/PhaseOrdering/ARM/arm_mult_q15.ll +++ b/llvm/test/Transforms/PhaseOrdering/ARM/arm_mult_q15.ll @@ -69,7 +69,7 @@ define void @arm_mult_q15(ptr %pSrcA, ptr %pSrcB, ptr noalias %pDst, i32 %blockS ; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[CONV2]], [[CONV]] ; CHECK-NEXT: [[SHR:%.*]] = ashr i32 [[MUL]], 15 ; CHECK-NEXT: [[SPEC_SELECT_I:%.*]] = tail call i32 @llvm.smin.i32(i32 [[SHR]], i32 32767) -; CHECK-NEXT: [[CONV3:%.*]] = trunc i32 [[SPEC_SELECT_I]] to i16 +; CHECK-NEXT: [[CONV3:%.*]] = trunc nsw i32 [[SPEC_SELECT_I]] to i16 ; CHECK-NEXT: [[INCDEC_PTR4]] = getelementptr inbounds i8, ptr [[PDST_ADDR_04]], i32 2 ; CHECK-NEXT: store i16 [[CONV3]], ptr [[PDST_ADDR_04]], align 2 ; CHECK-NEXT: [[DEC]] = add i32 [[BLKCNT_06]], -1 diff --git a/llvm/test/Transforms/SCCP/conditions-ranges.ll b/llvm/test/Transforms/SCCP/conditions-ranges.ll index f17b6c1317263..25719d2bee521 100644 --- a/llvm/test/Transforms/SCCP/conditions-ranges.ll +++ b/llvm/test/Transforms/SCCP/conditions-ranges.ll @@ -178,7 +178,7 @@ define i32 @f5(i64 %sz) { ; CHECK-NEXT: br label [[COND_END]] ; CHECK: cond.end: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ [[DIV]], [[COND_TRUE]] ], [ 1, [[ENTRY:%.*]] ] -; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[COND]] to i32 +; CHECK-NEXT: [[CONV:%.*]] = trunc nuw nsw i64 [[COND]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] ; entry: @@ -759,7 +759,7 @@ define i32 @udiv_1(i64 %sz) { ; CHECK-NEXT: br label [[COND_END]] ; CHECK: cond.end: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ [[DIV]], [[COND_TRUE]] ], [ 1, [[ENTRY:%.*]] ] -; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[COND]] to i32 +; CHECK-NEXT: [[CONV:%.*]] = trunc nuw nsw i64 [[COND]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] ; entry: @@ -786,7 +786,7 @@ define i32 @udiv_2(i64 %sz) { ; CHECK-NEXT: br label [[COND_END]] ; CHECK: cond.end: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ 0, [[COND_TRUE]] ], [ 1, [[ENTRY:%.*]] ] -; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[COND]] to i32 +; CHECK-NEXT: [[CONV:%.*]] = trunc nuw nsw i64 [[COND]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] ; entry: diff --git a/llvm/test/Transforms/SCCP/ip-ranges-casts.ll b/llvm/test/Transforms/SCCP/ip-ranges-casts.ll index b97b3a0c45816..80d90922c2fbd 100644 --- a/llvm/test/Transforms/SCCP/ip-ranges-casts.ll +++ b/llvm/test/Transforms/SCCP/ip-ranges-casts.ll @@ -4,7 +4,7 @@ ; x = [100, 301) define internal i1 @f.trunc(i32 %x) { ; CHECK-LABEL: @f.trunc( -; CHECK-NEXT: [[T_1:%.*]] = trunc i32 [[X:%.*]] to i16 +; CHECK-NEXT: [[T_1:%.*]] = trunc nuw nsw i32 [[X:%.*]] to i16 ; CHECK-NEXT: [[C_2:%.*]] = icmp sgt i16 [[T_1]], 299 ; CHECK-NEXT: [[C_4:%.*]] = icmp slt i16 [[T_1]], 101 ; CHECK-NEXT: [[RES_1:%.*]] = add nuw nsw i1 false, [[C_2]] @@ -329,7 +329,7 @@ define i64 @caller.sext_to_zext(i32 %i) { ; CHECK-LABEL: @caller.sext_to_zext( ; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[I:%.*]], 9 ; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 -; CHECK-NEXT: [[T:%.*]] = call i64 @f.sext_to_zext(i32 [[CONV]]) +; CHECK-NEXT: [[T:%.*]] = call i64 @f.sext_to_zext(i32 [[CONV]]), !range [[RNG0:![0-9]+]] ; CHECK-NEXT: ret i64 [[T]] ; %cmp = icmp sle i32 %i, 9 diff --git a/llvm/test/Transforms/SCCP/trunc-nuw-nsw-flags.ll b/llvm/test/Transforms/SCCP/trunc-nuw-nsw-flags.ll new file mode 100644 index 0000000000000..fc3e56011d46c --- /dev/null +++ b/llvm/test/Transforms/SCCP/trunc-nuw-nsw-flags.ll @@ -0,0 +1,179 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -passes=ipsccp -S %s | FileCheck %s + +define i16 @range_from_and_nuw(i32 %a) { +; CHECK-LABEL: define i16 @range_from_and_nuw( +; CHECK-SAME: i32 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[AND1:%.*]] = and i32 [[A]], 65535 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i32 [[AND1]] to i16 +; CHECK-NEXT: ret i16 [[TRUNC1]] +; +entry: + %and1 = and i32 %a, 65535 + %trunc1 = trunc i32 %and1 to i16 + ret i16 %trunc1 +} + +define i8 @range_from_or_nsw(i16 %a) { +; CHECK-LABEL: define i8 @range_from_or_nsw( +; CHECK-SAME: i16 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[AND1:%.*]] = or i16 [[A]], -128 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nsw i16 [[AND1]] to i8 +; CHECK-NEXT: ret i8 [[TRUNC1]] +; +entry: + %and1 = or i16 %a, 65408 + %trunc1 = trunc i16 %and1 to i8 + ret i8 %trunc1 +} + +define i16 @range_from_and_nuw_nsw(i32 %a) { +; CHECK-LABEL: define i16 @range_from_and_nuw_nsw( +; CHECK-SAME: i32 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[AND1:%.*]] = and i32 [[A]], 32767 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw nsw i32 [[AND1]] to i16 +; CHECK-NEXT: ret i16 [[TRUNC1]] +; +entry: + %and1 = and i32 %a, 32767 + %trunc1 = trunc i32 %and1 to i16 + ret i16 %trunc1 +} + +define <4 x i16> @range_from_and_nuw_vec(<4 x i32> %a) { +; CHECK-LABEL: define <4 x i16> @range_from_and_nuw_vec( +; CHECK-SAME: <4 x i32> [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[AND1:%.*]] = and <4 x i32> [[A]], +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc <4 x i32> [[AND1]] to <4 x i16> +; CHECK-NEXT: ret <4 x i16> [[TRUNC1]] +; +entry: + %and1 = and <4 x i32> %a, + %trunc1 = trunc <4 x i32> %and1 to <4 x i16> + ret <4 x i16> %trunc1 +} + +define i4 @range_from_sge_sle(i8 %a) { +; CHECK-LABEL: define i4 @range_from_sge_sle( +; CHECK-SAME: i8 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SGT:%.*]] = icmp sge i8 [[A]], 0 +; CHECK-NEXT: [[SLT:%.*]] = icmp sle i8 [[A]], 15 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[SGT]], [[SLT]] +; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[AND1:%.*]] = and i8 [[A]], 7 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i8 [[A]] to i4 +; CHECK-NEXT: [[TRUNC2:%.*]] = trunc nuw nsw i8 [[AND1]] to i4 +; CHECK-NEXT: [[XOR1:%.*]] = xor i4 [[TRUNC1]], [[TRUNC2]] +; CHECK-NEXT: ret i4 [[XOR1]] +; CHECK: else: +; CHECK-NEXT: [[TRUNC3:%.*]] = trunc i8 [[A]] to i4 +; CHECK-NEXT: ret i4 [[TRUNC3]] +; +entry: + %sgt = icmp sge i8 %a, 0 + %slt = icmp sle i8 %a, 15 + %and = and i1 %sgt, %slt + br i1 %and, label %then, label %else + +then: + %and1 = and i8 %a, 7 + + %trunc1 = trunc i8 %a to i4 + %trunc2 = trunc i8 %and1 to i4 + %xor1 = xor i4 %trunc1, %trunc2 + ret i4 %xor1 + +else: + %trunc3 = trunc i8 %a to i4 + ret i4 %trunc3 +} + +define i16 @range_from_sext(i16 %a) { +; CHECK-LABEL: define i16 @range_from_sext( +; CHECK-SAME: i16 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SEXT1:%.*]] = sext i16 [[A]] to i32 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nsw i32 [[SEXT1]] to i16 +; CHECK-NEXT: ret i16 [[TRUNC1]] +; +entry: + %sext1 = sext i16 %a to i32 + %trunc1 = trunc i32 %sext1 to i16 + ret i16 %trunc1 +} + +define i16 @range_from_zext(i16 %a) { +; CHECK-LABEL: define i16 @range_from_zext( +; CHECK-SAME: i16 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ZEXT1:%.*]] = zext i16 [[A]] to i32 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i32 [[ZEXT1]] to i16 +; CHECK-NEXT: ret i16 [[TRUNC1]] +; +entry: + %zext1 = zext i16 %a to i32 + %trunc1 = trunc i32 %zext1 to i16 + ret i16 %trunc1 +} + +define i1 @range_from_select_i1_nuw(i1 %c) { +; CHECK-LABEL: define i1 @range_from_select_i1_nuw( +; CHECK-SAME: i1 [[C:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SELECT1:%.*]] = select i1 [[C]], i16 0, i16 1 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i16 [[SELECT1]] to i1 +; CHECK-NEXT: ret i1 [[TRUNC1]] +; +entry: + %select1 = select i1 %c, i16 0, i16 1 + %trunc1 = trunc i16 %select1 to i1 + ret i1 %trunc1 +} + +define i1 @range_from_select_i1_nsw(i1 %c) { +; CHECK-LABEL: define i1 @range_from_select_i1_nsw( +; CHECK-SAME: i1 [[C:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SELECT1:%.*]] = select i1 [[C]], i16 0, i16 -1 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nsw i16 [[SELECT1]] to i1 +; CHECK-NEXT: ret i1 [[TRUNC1]] +; +entry: + %select1 = select i1 %c, i16 0, i16 -1 + %trunc1 = trunc i16 %select1 to i1 + ret i1 %trunc1 +} + +define i1 @range_from_select_i1_fail(i1 %c) { +; CHECK-LABEL: define i1 @range_from_select_i1_fail( +; CHECK-SAME: i1 [[C:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SELECT1:%.*]] = select i1 [[C]], i16 1, i16 -1 +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc i16 [[SELECT1]] to i1 +; CHECK-NEXT: ret i1 [[TRUNC1]] +; +entry: + %select1 = select i1 %c, i16 1, i16 -1 + %trunc1 = trunc i16 %select1 to i1 + ret i1 %trunc1 +} + +define i8 @range_from_trunc_fail(i32 %a) { +; CHECK-LABEL: define i8 @range_from_trunc_fail( +; CHECK-SAME: i32 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TRUNC1:%.*]] = trunc i32 [[A]] to i16 +; CHECK-NEXT: [[TRUNC2:%.*]] = trunc i16 [[TRUNC1]] to i8 +; CHECK-NEXT: ret i8 [[TRUNC2]] +; +entry: + %trunc1 = trunc i32 %a to i16 + %trunc2 = trunc i16 %trunc1 to i8 + ret i8 %trunc2 +} diff --git a/llvm/test/Transforms/SCCP/widening.ll b/llvm/test/Transforms/SCCP/widening.ll index 2223ca44bccdb..473594eb988da 100644 --- a/llvm/test/Transforms/SCCP/widening.ll +++ b/llvm/test/Transforms/SCCP/widening.ll @@ -438,9 +438,9 @@ define void @foo(ptr %arg) { ; SCCP-NEXT: [[TMP:%.*]] = zext i8 undef to i32 ; SCCP-NEXT: [[TMP2:%.*]] = load i64, ptr [[ARG:%.*]], align 8 ; SCCP-NEXT: switch i32 [[TMP]], label [[BB20:%.*]] [ -; SCCP-NEXT: i32 1, label [[BB3:%.*]] -; SCCP-NEXT: i32 2, label [[BB4:%.*]] -; SCCP-NEXT: i32 4, label [[BB19:%.*]] +; SCCP-NEXT: i32 1, label [[BB3:%.*]] +; SCCP-NEXT: i32 2, label [[BB4:%.*]] +; SCCP-NEXT: i32 4, label [[BB19:%.*]] ; SCCP-NEXT: ] ; SCCP: bb3: ; SCCP-NEXT: unreachable @@ -449,7 +449,7 @@ define void @foo(ptr %arg) { ; SCCP-NEXT: [[TMP6:%.*]] = and i64 [[TMP5]], 3 ; SCCP-NEXT: [[TMP7:%.*]] = sub nuw nsw i64 3, [[TMP6]] ; SCCP-NEXT: [[TMP8:%.*]] = shl nuw nsw i64 [[TMP7]], 1 -; SCCP-NEXT: [[TMP9:%.*]] = trunc i64 [[TMP8]] to i32 +; SCCP-NEXT: [[TMP9:%.*]] = trunc nuw nsw i64 [[TMP8]] to i32 ; SCCP-NEXT: [[TMP10:%.*]] = zext nneg i32 [[TMP9]] to i64 ; SCCP-NEXT: br label [[BB11:%.*]] ; SCCP: bb11: @@ -475,9 +475,9 @@ define void @foo(ptr %arg) { ; IPSCCP-NEXT: [[TMP:%.*]] = zext i8 undef to i32 ; IPSCCP-NEXT: [[TMP2:%.*]] = load i64, ptr [[ARG:%.*]], align 8 ; IPSCCP-NEXT: switch i32 [[TMP]], label [[BB20:%.*]] [ -; IPSCCP-NEXT: i32 1, label [[BB3:%.*]] -; IPSCCP-NEXT: i32 2, label [[BB4:%.*]] -; IPSCCP-NEXT: i32 4, label [[BB19:%.*]] +; IPSCCP-NEXT: i32 1, label [[BB3:%.*]] +; IPSCCP-NEXT: i32 2, label [[BB4:%.*]] +; IPSCCP-NEXT: i32 4, label [[BB19:%.*]] ; IPSCCP-NEXT: ] ; IPSCCP: bb3: ; IPSCCP-NEXT: unreachable @@ -486,7 +486,7 @@ define void @foo(ptr %arg) { ; IPSCCP-NEXT: [[TMP6:%.*]] = and i64 [[TMP5]], 3 ; IPSCCP-NEXT: [[TMP7:%.*]] = sub nuw nsw i64 3, [[TMP6]] ; IPSCCP-NEXT: [[TMP8:%.*]] = shl nuw nsw i64 [[TMP7]], 1 -; IPSCCP-NEXT: [[TMP9:%.*]] = trunc i64 [[TMP8]] to i32 +; IPSCCP-NEXT: [[TMP9:%.*]] = trunc nuw nsw i64 [[TMP8]] to i32 ; IPSCCP-NEXT: [[TMP10:%.*]] = zext nneg i32 [[TMP9]] to i64 ; IPSCCP-NEXT: br label [[BB11:%.*]] ; IPSCCP: bb11: @@ -615,7 +615,7 @@ define ptr @wobble(ptr %arg, i32 %arg1) align 2 { ; SCCP-NEXT: [[TMP22:%.*]] = icmp eq i32 [[TMP21]], 0 ; SCCP-NEXT: br i1 [[TMP22]], label [[BB23:%.*]], label [[BB25:%.*]] ; SCCP: bb23: -; SCCP-NEXT: [[TMP24:%.*]] = trunc i32 [[TMP3]] to i16 +; SCCP-NEXT: [[TMP24:%.*]] = trunc nuw i32 [[TMP3]] to i16 ; SCCP-NEXT: store i16 [[TMP24]], ptr [[TMP17]], align 2 ; SCCP-NEXT: br label [[BB31]] ; SCCP: bb25: @@ -639,7 +639,7 @@ define ptr @wobble(ptr %arg, i32 %arg1) align 2 { ; SCCP-NEXT: br i1 [[C_2]], label [[BB39:%.*]], label [[BB58:%.*]] ; SCCP: bb39: ; SCCP-NEXT: [[TMP40:%.*]] = add nsw i32 [[TMP11]], -1 -; SCCP-NEXT: [[TMP41:%.*]] = trunc i32 [[TMP3]] to i16 +; SCCP-NEXT: [[TMP41:%.*]] = trunc nuw i32 [[TMP3]] to i16 ; SCCP-NEXT: store i16 [[TMP41]], ptr @global.11, align 1 ; SCCP-NEXT: [[TMP43:%.*]] = add i32 [[TMP7]], [[TMP40]] ; SCCP-NEXT: [[TMP44:%.*]] = mul i32 [[TMP43]], 4 @@ -697,7 +697,7 @@ define ptr @wobble(ptr %arg, i32 %arg1) align 2 { ; IPSCCP-NEXT: [[TMP22:%.*]] = icmp eq i32 [[TMP21]], 0 ; IPSCCP-NEXT: br i1 [[TMP22]], label [[BB23:%.*]], label [[BB25:%.*]] ; IPSCCP: bb23: -; IPSCCP-NEXT: [[TMP24:%.*]] = trunc i32 [[TMP3]] to i16 +; IPSCCP-NEXT: [[TMP24:%.*]] = trunc nuw i32 [[TMP3]] to i16 ; IPSCCP-NEXT: store i16 [[TMP24]], ptr [[TMP17]], align 2 ; IPSCCP-NEXT: br label [[BB31]] ; IPSCCP: bb25: @@ -720,7 +720,7 @@ define ptr @wobble(ptr %arg, i32 %arg1) align 2 { ; IPSCCP-NEXT: [[C_2:%.*]] = icmp eq i32 [[TMP11]], 8 ; IPSCCP-NEXT: br i1 [[C_2]], label [[BB39:%.*]], label [[BB58:%.*]] ; IPSCCP: bb39: -; IPSCCP-NEXT: [[TMP41:%.*]] = trunc i32 [[TMP3]] to i16 +; IPSCCP-NEXT: [[TMP41:%.*]] = trunc nuw i32 [[TMP3]] to i16 ; IPSCCP-NEXT: store i16 [[TMP41]], ptr @global.11, align 1 ; IPSCCP-NEXT: [[TMP43:%.*]] = add i32 [[TMP7]], 7 ; IPSCCP-NEXT: [[TMP44:%.*]] = mul i32 [[TMP43]], 4