diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 18fc46584d09a..f6ada292b93b1 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -11751,6 +11751,10 @@ Overview: The '``uitofp``' instruction regards ``value`` as an unsigned integer and converts that value to the ``ty2`` type. +The ``nneg`` (non-negative) flag, if present, specifies that the +operand is non-negative. This property may be used by optimization +passes to later convert the ``uitofp`` into a ``sitofp``. + Arguments: """""""""" @@ -11768,6 +11772,9 @@ integer quantity and converts it to the corresponding floating-point value. If the value cannot be exactly represented, it is rounded using the default rounding mode. +If the ``nneg`` flag is set, and the ``uitofp`` argument is negative, +the result is a poison value. + Example: """""""" @@ -11777,6 +11784,9 @@ Example: %X = uitofp i32 257 to float ; yields float:257.0 %Y = uitofp i8 -1 to double ; yields double:255.0 + %a = uitofp nneg i32 256 to i32 ; yields float:256.0 + %b = uitofp nneg i32 -256 to i32 ; yields i32 poison + '``sitofp .. to``' Instruction ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 2e2ec9a1c8302..f381273c46cfb 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -2068,11 +2068,17 @@ class IRBuilderBase { return CreateCast(Instruction::FPToSI, V, DestTy, Name); } - Value *CreateUIToFP(Value *V, Type *DestTy, const Twine &Name = ""){ + Value *CreateUIToFP(Value *V, Type *DestTy, const Twine &Name = "", + bool IsNonNeg = false) { if (IsFPConstrained) return CreateConstrainedFPCast(Intrinsic::experimental_constrained_uitofp, V, DestTy, nullptr, Name); - return CreateCast(Instruction::UIToFP, V, DestTy, Name); + if (Value *Folded = Folder.FoldCast(Instruction::UIToFP, V, DestTy)) + return Folded; + Instruction *I = Insert(new UIToFPInst(V, DestTy), Name); + if (IsNonNeg) + I->setNonNeg(); + return I; } Value *CreateSIToFP(Value *V, Type *DestTy, const Twine &Name = ""){ diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index e4e5fa15c399e..cfe1b11ade5a4 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -927,13 +927,19 @@ class CastInst : public UnaryInstruction { } }; -/// Instruction that can have a nneg flag (only zext). +/// Instruction that can have a nneg flag (zext/uitofp). class PossiblyNonNegInst : public CastInst { public: enum { NonNeg = (1 << 0) }; static bool classof(const Instruction *I) { - return I->getOpcode() == Instruction::ZExt; + switch (I->getOpcode()) { + case Instruction::ZExt: + case Instruction::UIToFP: + return true; + default: + return false; + } } static bool classof(const Value *V) { diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 8609f3d6276ce..f546e05a5d37d 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -6816,6 +6816,7 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB, } // Casts. + case lltok::kw_uitofp: case lltok::kw_zext: { bool NonNeg = EatIfPresent(lltok::kw_nneg); bool Res = parseCast(Inst, PFS, KeywordVal); @@ -6843,7 +6844,6 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB, case lltok::kw_fpext: case lltok::kw_bitcast: case lltok::kw_addrspacecast: - case lltok::kw_uitofp: case lltok::kw_sitofp: case lltok::kw_fptoui: case lltok::kw_fptosi: diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 5ea6cc2c6b526..92c349525aff5 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -5039,7 +5039,7 @@ Error BitcodeReader::parseFunctionBody(Function *F) { } if (OpNum < Record.size()) { - if (Opc == Instruction::ZExt) { + if (Opc == Instruction::ZExt || Opc == Instruction::UIToFP) { if (Record[OpNum] & (1 << bitc::PNNI_NON_NEG)) cast(I)->setNonNeg(true); } else if (Opc == Instruction::Trunc) { diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index 0602a55b9fe7f..b9efe9cdcfe31 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -388,7 +388,7 @@ void Instruction::setIsExact(bool b) { } void Instruction::setNonNeg(bool b) { - assert(isa(this) && "Must be zext"); + assert(isa(this) && "Must be zext/uitofp"); SubclassOptionalData = (SubclassOptionalData & ~PossiblyNonNegInst::NonNeg) | (b * PossiblyNonNegInst::NonNeg); } @@ -408,7 +408,7 @@ bool Instruction::hasNoSignedWrap() const { } bool Instruction::hasNonNeg() const { - assert(isa(this) && "Must be zext"); + assert(isa(this) && "Must be zext/uitofp"); return (SubclassOptionalData & PossiblyNonNegInst::NonNeg) != 0; } @@ -441,6 +441,7 @@ void Instruction::dropPoisonGeneratingFlags() { cast(this)->setIsInBounds(false); break; + case Instruction::UIToFP: case Instruction::ZExt: setNonNeg(false); break; diff --git a/llvm/lib/IR/Operator.cpp b/llvm/lib/IR/Operator.cpp index 7b4449cd825f9..ccc624d854429 100644 --- a/llvm/lib/IR/Operator.cpp +++ b/llvm/lib/IR/Operator.cpp @@ -44,6 +44,7 @@ bool Operator::hasPoisonGeneratingFlags() const { // Note: inrange exists on constexpr only return GEP->isInBounds() || GEP->getInRange() != std::nullopt; } + case Instruction::UIToFP: case Instruction::ZExt: if (auto *NNI = dyn_cast(this)) return NNI->hasNonNeg(); diff --git a/llvm/test/Assembler/flags.ll b/llvm/test/Assembler/flags.ll index d75b0cb0ea824..e0ad8bf000be1 100644 --- a/llvm/test/Assembler/flags.ll +++ b/llvm/test/Assembler/flags.ll @@ -256,6 +256,13 @@ define i64 @test_zext(i32 %a) { ret i64 %res } +define float @test_uitofp(i32 %a) { +; CHECK: %res = uitofp nneg i32 %a to float + %res = uitofp nneg i32 %a to float + ret float %res +} + + define i64 @test_or(i64 %a, i64 %b) { ; CHECK: %res = or disjoint i64 %a, %b %res = or disjoint i64 %a, %b diff --git a/llvm/test/Bitcode/flags.ll b/llvm/test/Bitcode/flags.ll index 96995ec570c93..fd56694ccceb2 100644 --- a/llvm/test/Bitcode/flags.ll +++ b/llvm/test/Bitcode/flags.ll @@ -18,6 +18,8 @@ second: ; preds = %first %z = add i32 %a, 0 ; [#uses=0] %hh = zext nneg i32 %a to i64 %ll = zext i32 %s to i64 + %ff = uitofp nneg i32 %a to float + %bb = uitofp i32 %s to float %jj = or disjoint i32 %a, 0 %oo = or i32 %a, 0 %tu = trunc nuw i32 %a to i16 @@ -39,6 +41,8 @@ first: ; preds = %entry %zz = add i32 %a, 0 ; [#uses=0] %kk = zext nneg i32 %a to i64 %rr = zext i32 %ss to i64 + %ww = uitofp nneg i32 %a to float + %xx = uitofp i32 %ss to float %mm = or disjoint i32 %a, 0 %nn = or i32 %a, 0 %tuu = trunc nuw i32 %a to i16 diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll index e8105b6287d0c..668f3033ed4b7 100644 --- a/llvm/test/Transforms/InstCombine/freeze.ll +++ b/llvm/test/Transforms/InstCombine/freeze.ll @@ -1127,6 +1127,17 @@ define i32 @freeze_zext_nneg(i8 %x) { ret i32 %fr } +define float @freeze_uitofp_nneg(i8 %x) { +; CHECK-LABEL: @freeze_uitofp_nneg( +; CHECK-NEXT: [[X_FR:%.*]] = freeze i8 [[X:%.*]] +; CHECK-NEXT: [[UITOFP:%.*]] = uitofp i8 [[X_FR]] to float +; CHECK-NEXT: ret float [[UITOFP]] +; + %uitofp = uitofp nneg i8 %x to float + %fr = freeze float %uitofp + ret float %fr +} + define i32 @propagate_drop_flags_or(i32 %arg) { ; CHECK-LABEL: @propagate_drop_flags_or( ; CHECK-NEXT: [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]] diff --git a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll index 4a4c94098ab94..887d182016818 100644 --- a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll +++ b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll @@ -125,6 +125,37 @@ F: ret i32 %z2 } + +define float @hoist_uitofp_flags_preserve(i1 %C, i8 %x) { +; CHECK-LABEL: @hoist_uitofp_flags_preserve( +; CHECK-NEXT: common.ret: +; CHECK-NEXT: [[Z1:%.*]] = uitofp nneg i8 [[X:%.*]] to float +; CHECK-NEXT: ret float [[Z1]] +; + br i1 %C, label %T, label %F +T: + %z1 = uitofp nneg i8 %x to float + ret float %z1 +F: + %z2 = uitofp nneg i8 %x to float + ret float %z2 +} + +define float @hoist_uitofp_flags_drop(i1 %C, i8 %x) { +; CHECK-LABEL: @hoist_uitofp_flags_drop( +; CHECK-NEXT: common.ret: +; CHECK-NEXT: [[Z1:%.*]] = uitofp i8 [[X:%.*]] to float +; CHECK-NEXT: ret float [[Z1]] +; + br i1 %C, label %T, label %F +T: + %z1 = uitofp nneg i8 %x to float + ret float %z1 +F: + %z2 = uitofp i8 %x to float + ret float %z2 +} + define i32 @hoist_or_flags_preserve(i1 %C, i32 %x, i32 %y) { ; CHECK-LABEL: @hoist_or_flags_preserve( ; CHECK-NEXT: common.ret: