diff options
author | elhewaty <mohamedatef1698@gmail.com> | 2024-03-29 08:08:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-29 14:08:49 +0800 |
commit | 7d3924cee30a87a51f9dc04ec843ae6bc3d1c90e (patch) | |
tree | 7e23e8a759adcd9911b77834c6a79f350685cb4d | |
parent | ba6b2d22af177a72b132cdb8e9350a708f282d2c (diff) |
[IR] Add nowrap flags for trunc instruction (#85592)
This patch adds the nuw (no unsigned wrap) and nsw (no signed wrap)
poison-generating flags to the trunc instruction.
Discourse thread:
https://discourse.llvm.org/t/rfc-add-nowrap-flags-to-trunc/77453
-rw-r--r-- | llvm/docs/LangRef.rst | 8 | ||||
-rw-r--r-- | llvm/include/llvm/Bitcode/LLVMBitCodes.h | 7 | ||||
-rw-r--r-- | llvm/include/llvm/IR/Instructions.h | 35 | ||||
-rw-r--r-- | llvm/lib/AsmParser/LLParser.cpp | 14 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 16 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 5 | ||||
-rw-r--r-- | llvm/lib/IR/AsmWriter.cpp | 5 | ||||
-rw-r--r-- | llvm/lib/IR/Instruction.cpp | 32 | ||||
-rw-r--r-- | llvm/lib/IR/Operator.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp | 8 | ||||
-rw-r--r-- | llvm/test/Assembler/flags.ll | 48 | ||||
-rw-r--r-- | llvm/test/Bitcode/flags.ll | 29 | ||||
-rw-r--r-- | llvm/test/Transforms/InstCombine/freeze.ll | 19 | ||||
-rw-r--r-- | llvm/test/Transforms/InstCombine/trunc.ll | 37 | ||||
-rw-r--r-- | llvm/test/Transforms/SimplifyCFG/HoistCode.ll | 34 |
15 files changed, 281 insertions, 20 deletions
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 35aa3988d400..845254aa6e50 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -11419,6 +11419,9 @@ Syntax: :: <result> = trunc <ty> <value> to <ty2> ; yields ty2 + <result> = trunc nsw <ty> <value> to <ty2> ; yields ty2 + <result> = trunc nuw <ty> <value> to <ty2> ; yields ty2 + <result> = trunc nuw nsw <ty> <value> to <ty2> ; yields ty2 Overview: """"""""" @@ -11442,6 +11445,11 @@ and converts the remaining bits to ``ty2``. Since the source size must be larger than the destination size, ``trunc`` cannot be a *no-op cast*. It will always truncate bits. +If the ``nuw`` keyword is present, and any of the truncated bits are zero, +the result is a :ref:`poison value <poisonvalues>`. If the ``nsw`` keyword +is present, and any of the truncated bits are not the same as the top bit +of the truncation result, the result is a :ref:`poison value <poisonvalues>`. + Example: """""""" diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 4018ef03f960..909eb833c601 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -492,6 +492,13 @@ enum OverflowingBinaryOperatorOptionalFlags { OBO_NO_SIGNED_WRAP = 1 }; +/// TruncInstOptionalFlags - Flags for serializing +/// TruncInstOptionalFlags's SubclassOptionalData contents. +enum TruncInstOptionalFlags { + TIO_NO_UNSIGNED_WRAP = 0, + TIO_NO_SIGNED_WRAP = 1 +}; + /// FastMath Flags /// This is a fixed layout derived from the bitcode emitted by LLVM 5.0 /// intended to decouple the in-memory representation from the serialization. diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 4e4cf71a349d..4ffa6349871b 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -5345,6 +5345,8 @@ protected: TruncInst *cloneImpl() const; public: + enum { AnyWrap = 0, NoUnsignedWrap = (1 << 0), NoSignedWrap = (1 << 1) }; + /// Constructor with insert-before-instruction semantics TruncInst( Value *S, ///< The value to be truncated @@ -5376,6 +5378,39 @@ public: static bool classof(const Value *V) { return isa<Instruction>(V) && classof(cast<Instruction>(V)); } + + void setHasNoUnsignedWrap(bool B) { + SubclassOptionalData = + (SubclassOptionalData & ~NoUnsignedWrap) | (B * NoUnsignedWrap); + } + void setHasNoSignedWrap(bool B) { + SubclassOptionalData = + (SubclassOptionalData & ~NoSignedWrap) | (B * NoSignedWrap); + } + + /// Test whether this operation is known to never + /// undergo unsigned overflow, aka the nuw property. + bool hasNoUnsignedWrap() const { + return SubclassOptionalData & NoUnsignedWrap; + } + + /// Test whether this operation is known to never + /// undergo signed overflow, aka the nsw property. + bool hasNoSignedWrap() const { + return (SubclassOptionalData & NoSignedWrap) != 0; + } + + /// Returns the no-wrap kind of the operation. + unsigned getNoWrapKind() const { + unsigned NoWrapKind = 0; + if (hasNoUnsignedWrap()) + NoWrapKind |= NoUnsignedWrap; + + if (hasNoSignedWrap()) + NoWrapKind |= NoSignedWrap; + + return NoWrapKind; + } }; //===----------------------------------------------------------------------===// diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index adc1c153b9f6..fe49e52ae428 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -6814,7 +6814,19 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB, Inst->setNonNeg(); return 0; } - case lltok::kw_trunc: + case lltok::kw_trunc: { + bool NUW = EatIfPresent(lltok::kw_nuw); + bool NSW = EatIfPresent(lltok::kw_nsw); + if (!NUW) + NUW = EatIfPresent(lltok::kw_nuw); + if (parseCast(Inst, PFS, KeywordVal)) + return true; + if (NUW) + cast<TruncInst>(Inst)->setHasNoUnsignedWrap(true); + if (NSW) + cast<TruncInst>(Inst)->setHasNoSignedWrap(true); + return false; + } case lltok::kw_sext: case lltok::kw_fptrunc: case lltok::kw_fpext: diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 6ee93f17792b..aa6c9c95ca24 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -5022,9 +5022,19 @@ Error BitcodeReader::parseFunctionBody(Function *F) { return error("Invalid cast"); I = CastInst::Create(CastOp, Op, ResTy); } - if (OpNum < Record.size() && isa<PossiblyNonNegInst>(I) && - (Record[OpNum] & (1 << bitc::PNNI_NON_NEG))) - I->setNonNeg(true); + + if (OpNum < Record.size()) { + if (Opc == Instruction::ZExt) { + if (Record[OpNum] & (1 << bitc::PNNI_NON_NEG)) + cast<PossiblyNonNegInst>(I)->setNonNeg(true); + } else if (Opc == Instruction::Trunc) { + if (Record[OpNum] & (1 << bitc::TIO_NO_UNSIGNED_WRAP)) + cast<TruncInst>(I)->setHasNoUnsignedWrap(true); + if (Record[OpNum] & (1 << bitc::TIO_NO_SIGNED_WRAP)) + cast<TruncInst>(I)->setHasNoSignedWrap(true); + } + } + InstructionList.push_back(I); break; } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index a8b69f89e7de..221eeaae6e2b 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1640,6 +1640,11 @@ static uint64_t getOptimizationFlags(const Value *V) { } else if (const auto *NNI = dyn_cast<PossiblyNonNegInst>(V)) { if (NNI->hasNonNeg()) Flags |= 1 << bitc::PNNI_NON_NEG; + } else if (const auto *TI = dyn_cast<TruncInst>(V)) { + if (TI->hasNoSignedWrap()) + Flags |= 1 << bitc::TIO_NO_SIGNED_WRAP; + if (TI->hasNoUnsignedWrap()) + Flags |= 1 << bitc::TIO_NO_UNSIGNED_WRAP; } return Flags; diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 84690f026139..b778a14158ef 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1424,6 +1424,11 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) { } else if (const auto *NNI = dyn_cast<PossiblyNonNegInst>(U)) { if (NNI->hasNonNeg()) Out << " nneg"; + } else if (const auto *TI = dyn_cast<TruncInst>(U)) { + if (TI->hasNoUnsignedWrap()) + Out << " nuw"; + if (TI->hasNoSignedWrap()) + Out << " nsw"; } } diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index 47a7f2c9de79..0602a55b9fe7 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -370,11 +370,17 @@ bool Instruction::isOnlyUserOfAnyOperand() { } void Instruction::setHasNoUnsignedWrap(bool b) { - cast<OverflowingBinaryOperator>(this)->setHasNoUnsignedWrap(b); + if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this)) + Inst->setHasNoUnsignedWrap(b); + else + cast<TruncInst>(this)->setHasNoUnsignedWrap(b); } void Instruction::setHasNoSignedWrap(bool b) { - cast<OverflowingBinaryOperator>(this)->setHasNoSignedWrap(b); + if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this)) + Inst->setHasNoSignedWrap(b); + else + cast<TruncInst>(this)->setHasNoSignedWrap(b); } void Instruction::setIsExact(bool b) { @@ -388,11 +394,17 @@ void Instruction::setNonNeg(bool b) { } bool Instruction::hasNoUnsignedWrap() const { - return cast<OverflowingBinaryOperator>(this)->hasNoUnsignedWrap(); + if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this)) + return Inst->hasNoUnsignedWrap(); + + return cast<TruncInst>(this)->hasNoUnsignedWrap(); } bool Instruction::hasNoSignedWrap() const { - return cast<OverflowingBinaryOperator>(this)->hasNoSignedWrap(); + if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this)) + return Inst->hasNoSignedWrap(); + + return cast<TruncInst>(this)->hasNoSignedWrap(); } bool Instruction::hasNonNeg() const { @@ -432,6 +444,11 @@ void Instruction::dropPoisonGeneratingFlags() { case Instruction::ZExt: setNonNeg(false); break; + + case Instruction::Trunc: + cast<TruncInst>(this)->setHasNoUnsignedWrap(false); + cast<TruncInst>(this)->setHasNoSignedWrap(false); + break; } if (isa<FPMathOperator>(this)) { @@ -626,6 +643,13 @@ void Instruction::andIRFlags(const Value *V) { } } + if (auto *TI = dyn_cast<TruncInst>(V)) { + if (isa<TruncInst>(this)) { + setHasNoSignedWrap(hasNoSignedWrap() && TI->hasNoSignedWrap()); + setHasNoUnsignedWrap(hasNoUnsignedWrap() && TI->hasNoUnsignedWrap()); + } + } + if (auto *PE = dyn_cast<PossiblyExactOperator>(V)) if (isa<PossiblyExactOperator>(this)) setIsExact(isExact() && PE->isExact()); diff --git a/llvm/lib/IR/Operator.cpp b/llvm/lib/IR/Operator.cpp index b9cd219d94dc..495769279e33 100644 --- a/llvm/lib/IR/Operator.cpp +++ b/llvm/lib/IR/Operator.cpp @@ -27,6 +27,10 @@ bool Operator::hasPoisonGeneratingFlags() const { auto *OBO = cast<OverflowingBinaryOperator>(this); return OBO->hasNoUnsignedWrap() || OBO->hasNoSignedWrap(); } + case Instruction::Trunc: { + auto *TI = dyn_cast<TruncInst>(this); + return TI->hasNoUnsignedWrap() || TI->hasNoSignedWrap(); + } case Instruction::UDiv: case Instruction::SDiv: case Instruction::AShr: diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp index 3a3bcde7c3dc..74cffbc005c8 100644 --- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp +++ b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp @@ -59,6 +59,10 @@ PoisonFlags::PoisonFlags(const Instruction *I) { Disjoint = PDI->isDisjoint(); if (auto *PNI = dyn_cast<PossiblyNonNegInst>(I)) NNeg = PNI->hasNonNeg(); + if (auto *TI = dyn_cast<TruncInst>(I)) { + NUW = TI->hasNoUnsignedWrap(); + NSW = TI->hasNoSignedWrap(); + } } void PoisonFlags::apply(Instruction *I) { @@ -72,6 +76,10 @@ void PoisonFlags::apply(Instruction *I) { PDI->setIsDisjoint(Disjoint); if (auto *PNI = dyn_cast<PossiblyNonNegInst>(I)) PNI->setNonNeg(NNeg); + if (isa<TruncInst>(I)) { + I->setHasNoUnsignedWrap(NUW); + I->setHasNoSignedWrap(NSW); + } } /// ReuseOrCreateCast - Arrange for there to be a cast of V to Ty at IP, diff --git a/llvm/test/Assembler/flags.ll b/llvm/test/Assembler/flags.ll index 04bddd02f50c..d75b0cb0ea82 100644 --- a/llvm/test/Assembler/flags.ll +++ b/llvm/test/Assembler/flags.ll @@ -261,3 +261,51 @@ define i64 @test_or(i64 %a, i64 %b) { %res = or disjoint i64 %a, %b ret i64 %res } + +define i32 @test_trunc_signed(i64 %a) { +; CHECK: %res = trunc nsw i64 %a to i32 + %res = trunc nsw i64 %a to i32 + ret i32 %res +} + +define i32 @test_trunc_unsigned(i64 %a) { +; CHECK: %res = trunc nuw i64 %a to i32 + %res = trunc nuw i64 %a to i32 + ret i32 %res +} + +define i32 @test_trunc_both(i64 %a) { +; CHECK: %res = trunc nuw nsw i64 %a to i32 + %res = trunc nuw nsw i64 %a to i32 + ret i32 %res +} + +define i32 @test_trunc_both_reversed(i64 %a) { +; CHECK: %res = trunc nuw nsw i64 %a to i32 + %res = trunc nsw nuw i64 %a to i32 + ret i32 %res +} + +define <2 x i32> @test_trunc_signed_vector(<2 x i64> %a) { +; CHECK: %res = trunc nsw <2 x i64> %a to <2 x i32> + %res = trunc nsw <2 x i64> %a to <2 x i32> + ret <2 x i32> %res +} + +define <2 x i32> @test_trunc_unsigned_vector(<2 x i64> %a) { +; CHECK: %res = trunc nuw <2 x i64> %a to <2 x i32> + %res = trunc nuw <2 x i64> %a to <2 x i32> + ret <2 x i32> %res +} + +define <2 x i32> @test_trunc_both_vector(<2 x i64> %a) { +; CHECK: %res = trunc nuw nsw <2 x i64> %a to <2 x i32> + %res = trunc nuw nsw <2 x i64> %a to <2 x i32> + ret <2 x i32> %res +} + +define <2 x i32> @test_trunc_both_reversed_vector(<2 x i64> %a) { +; CHECK: %res = trunc nuw nsw <2 x i64> %a to <2 x i32> + %res = trunc nsw nuw <2 x i64> %a to <2 x i32> + ret <2 x i32> %res +} diff --git a/llvm/test/Bitcode/flags.ll b/llvm/test/Bitcode/flags.ll index e3fc827d865d..96995ec570c9 100644 --- a/llvm/test/Bitcode/flags.ll +++ b/llvm/test/Bitcode/flags.ll @@ -20,17 +20,34 @@ second: ; preds = %first %ll = zext i32 %s to i64 %jj = or disjoint i32 %a, 0 %oo = or i32 %a, 0 + %tu = trunc nuw i32 %a to i16 + %ts = trunc nsw i32 %a to i16 + %tus = trunc nuw nsw i32 %a to i16 + %t = trunc i32 %a to i16 + %tuv = trunc nuw <2 x i32> %aa to <2 x i16> + %tsv = trunc nsw <2 x i32> %aa to <2 x i16> + %tusv = trunc nuw nsw <2 x i32> %aa to <2 x i16> + %tv = trunc <2 x i32> %aa to <2 x i16> unreachable -first: ; preds = %entry - %a = bitcast i32 0 to i32 ; <i32> [#uses=8] - %uu = add nuw i32 %a, 0 ; <i32> [#uses=0] - %ss = add nsw i32 %a, 0 ; <i32> [#uses=0] - %uuss = add nuw nsw i32 %a, 0 ; <i32> [#uses=0] - %zz = add i32 %a, 0 ; <i32> [#uses=0] +first: ; preds = %entry + %aa = bitcast <2 x i32> <i32 0, i32 0> to <2 x i32> + %a = bitcast i32 0 to i32 ; <i32> [#uses=8] + %uu = add nuw i32 %a, 0 ; <i32> [#uses=0] + %ss = add nsw i32 %a, 0 ; <i32> [#uses=0] + %uuss = add nuw nsw i32 %a, 0 ; <i32> [#uses=0] + %zz = add i32 %a, 0 ; <i32> [#uses=0] %kk = zext nneg i32 %a to i64 %rr = zext i32 %ss to i64 %mm = or disjoint i32 %a, 0 %nn = or i32 %a, 0 + %tuu = trunc nuw i32 %a to i16 + %tss = trunc nsw i32 %a to i16 + %tuss = trunc nuw nsw i32 %a to i16 + %tt = trunc i32 %a to i16 + %ttuv = trunc nuw <2 x i32> %aa to <2 x i16> + %ttsv = trunc nsw <2 x i32> %aa to <2 x i16> + %ttusv = trunc nuw nsw <2 x i32> %aa to <2 x i16> + %ttv = trunc <2 x i32> %aa to <2 x i16> br label %second } diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll index da59101d5710..e8105b6287d0 100644 --- a/llvm/test/Transforms/InstCombine/freeze.ll +++ b/llvm/test/Transforms/InstCombine/freeze.ll @@ -1049,7 +1049,7 @@ exit: define ptr @freeze_load_noundef(ptr %ptr) { ; CHECK-LABEL: @freeze_load_noundef( -; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !noundef !0 +; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !noundef [[META0:![0-9]+]] ; CHECK-NEXT: ret ptr [[P]] ; %p = load ptr, ptr %ptr, !noundef !0 @@ -1059,7 +1059,7 @@ define ptr @freeze_load_noundef(ptr %ptr) { define ptr @freeze_load_dereferenceable(ptr %ptr) { ; CHECK-LABEL: @freeze_load_dereferenceable( -; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !dereferenceable !1 +; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !dereferenceable [[META1:![0-9]+]] ; CHECK-NEXT: ret ptr [[P]] ; %p = load ptr, ptr %ptr, !dereferenceable !1 @@ -1138,6 +1138,17 @@ define i32 @propagate_drop_flags_or(i32 %arg) { ret i32 %v1.fr } +define i32 @propagate_drop_flags_trunc(i64 %arg) { +; CHECK-LABEL: @propagate_drop_flags_trunc( +; CHECK-NEXT: [[ARG_FR:%.*]] = freeze i64 [[ARG:%.*]] +; CHECK-NEXT: [[V1:%.*]] = trunc i64 [[ARG_FR]] to i32 +; CHECK-NEXT: ret i32 [[V1]] +; + %v1 = trunc nsw nuw i64 %arg to i32 + %v1.fr = freeze i32 %v1 + ret i32 %v1.fr +} + !0 = !{} !1 = !{i64 4} !2 = !{i32 0, i32 100} @@ -1145,8 +1156,8 @@ define i32 @propagate_drop_flags_or(i32 %arg) { ; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } ; CHECK: attributes #[[ATTR1]] = { nounwind } ;. -; CHECK: [[META0:![0-9]+]] = !{} -; CHECK: [[META1:![0-9]+]] = !{i64 4} +; CHECK: [[META0]] = !{} +; CHECK: [[META1]] = !{i64 4} ; CHECK: [[RNG2]] = !{i32 0, i32 100} ; CHECK: [[RNG3]] = !{i32 0, i32 33} ;. diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll index c6bc06d666d0..760825d6b1da 100644 --- a/llvm/test/Transforms/InstCombine/trunc.ll +++ b/llvm/test/Transforms/InstCombine/trunc.ll @@ -1021,3 +1021,40 @@ define i16 @PR44545(i32 %t0, i32 %data) { %sub = add nsw i16 %cast, -1 ret i16 %sub } + +; Make sure that SimplifyDemandedBits drops the nowrap flags +define i8 @drop_nsw_trunc(i16 %x, i16 %y) { +; CHECK-LABEL: @drop_nsw_trunc( +; CHECK-NEXT: [[AND2:%.*]] = and i16 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[RES:%.*]] = trunc i16 [[AND2]] to i8 +; CHECK-NEXT: ret i8 [[RES]] +; + %and = and i16 %x, 255 + %and2 = and i16 %and, %y + %res = trunc nsw i16 %and2 to i8 + ret i8 %res +} + +define i8 @drop_nuw_trunc(i16 %x, i16 %y) { +; CHECK-LABEL: @drop_nuw_trunc( +; CHECK-NEXT: [[AND2:%.*]] = and i16 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[B:%.*]] = trunc i16 [[AND2]] to i8 +; CHECK-NEXT: ret i8 [[B]] +; + %and = and i16 %x, 255 + %and2 = and i16 %and, %y + %res = trunc nuw i16 %and2 to i8 + ret i8 %res +} + +define i8 @drop_both_trunc(i16 %x, i16 %y) { +; CHECK-LABEL: @drop_both_trunc( +; CHECK-NEXT: [[AND2:%.*]] = and i16 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[RES:%.*]] = trunc i16 [[AND2]] to i8 +; CHECK-NEXT: ret i8 [[RES]] +; + %and = and i16 %x, 255 + %and2 = and i16 %and, %y + %res = trunc nuw nsw i16 %and2 to i8 + ret i8 %res +} diff --git a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll index a081eddfc456..4a4c94098ab9 100644 --- a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll +++ b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll @@ -64,8 +64,8 @@ define float @PR39535min_switch(i64 %i, float %x) { ; CHECK-LABEL: @PR39535min_switch( ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i64 [[I:%.*]], label [[END:%.*]] [ -; CHECK-NEXT: i64 1, label [[BB1:%.*]] -; CHECK-NEXT: i64 2, label [[BB2:%.*]] +; CHECK-NEXT: i64 1, label [[BB1:%.*]] +; CHECK-NEXT: i64 2, label [[BB2:%.*]] ; CHECK-NEXT: ] ; CHECK: bb1: ; CHECK-NEXT: br label [[END]] @@ -154,3 +154,33 @@ F: %z2 = or disjoint i32 %x, %y ret i32 %z2 } + +define i16 @hoist_trunc_flags_preserve(i1 %C, i32 %x) { +; CHECK-LABEL: @hoist_trunc_flags_preserve( +; CHECK-NEXT: common.ret: +; CHECK-NEXT: [[Z1:%.*]] = trunc nuw nsw i32 [[X:%.*]] to i16 +; CHECK-NEXT: ret i16 [[Z1]] +; + br i1 %C, label %T, label %F +T: + %z1 = trunc nsw nuw i32 %x to i16 + ret i16 %z1 +F: + %z2 = trunc nsw nuw i32 %x to i16 + ret i16 %z2 +} + +define i16 @hoist_trunc_flags_drop(i1 %C, i32 %x) { +; CHECK-LABEL: @hoist_trunc_flags_drop( +; CHECK-NEXT: common.ret: +; CHECK-NEXT: [[Z1:%.*]] = trunc i32 [[X:%.*]] to i16 +; CHECK-NEXT: ret i16 [[Z1]] +; + br i1 %C, label %T, label %F +T: + %z1 = trunc i32 %x to i16 + ret i16 %z1 +F: + %z2 = trunc nsw nuw i32 %x to i16 + ret i16 %z2 +} |