Add support for nneg
flag with uitofp
As noted when #82404 was pushed (canonicalizing `sitofp` -> `uitofp`), different signedness on fp casts can have dramatic performance implications on different backends. So, it makes to create a reliable means for the backend to pick its cast signedness if either are correct. Further, this allows us to start canonicalizing `sitofp`- > `uitofp` which may easy middle end analysis. Closes #86141
This commit is contained in:
parent
788be0d9fc
commit
9170e38575
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -2068,11 +2068,17 @@ public:
|
||||
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 = ""){
|
||||
|
@ -927,13 +927,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// 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) {
|
||||
|
@ -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:
|
||||
|
@ -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<PossiblyNonNegInst>(I)->setNonNeg(true);
|
||||
} else if (Opc == Instruction::Trunc) {
|
||||
|
@ -388,7 +388,7 @@ void Instruction::setIsExact(bool b) {
|
||||
}
|
||||
|
||||
void Instruction::setNonNeg(bool b) {
|
||||
assert(isa<PossiblyNonNegInst>(this) && "Must be zext");
|
||||
assert(isa<PossiblyNonNegInst>(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<PossiblyNonNegInst>(this) && "Must be zext");
|
||||
assert(isa<PossiblyNonNegInst>(this) && "Must be zext/uitofp");
|
||||
return (SubclassOptionalData & PossiblyNonNegInst::NonNeg) != 0;
|
||||
}
|
||||
|
||||
@ -441,6 +441,7 @@ void Instruction::dropPoisonGeneratingFlags() {
|
||||
cast<GetElementPtrInst>(this)->setIsInBounds(false);
|
||||
break;
|
||||
|
||||
case Instruction::UIToFP:
|
||||
case Instruction::ZExt:
|
||||
setNonNeg(false);
|
||||
break;
|
||||
|
@ -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<PossiblyNonNegInst>(this))
|
||||
return NNI->hasNonNeg();
|
||||
|
@ -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
|
||||
|
@ -18,6 +18,8 @@ second: ; preds = %first
|
||||
%z = add i32 %a, 0 ; <i32> [#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 ; <i32> [#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
|
||||
|
@ -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:%.*]]
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user