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:
Noah Goldstein 2024-03-20 16:46:24 -05:00
parent 788be0d9fc
commit 9170e38575
11 changed files with 85 additions and 8 deletions

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -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 = ""){

View File

@ -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) {

View File

@ -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:

View File

@ -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) {

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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:%.*]]

View File

@ -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: