Skip to content

Commit ed3f06b

Browse files
nikickarouzakisp
andauthored
[IR] Add zext nneg flag (#67982)
Add an nneg flag to the zext instruction, which specifies that the argument is non-negative. Otherwise, the result is a poison value. The primary use-case for the flag is to preserve information when sext gets replaced with zext due to range-based canonicalization. The nneg flag allows us to convert the zext back into an sext later. This is useful for some optimizations (e.g. a signed icmp can fold with sext but not zext), as well as some targets (e.g. RISCV prefers sext over zext). Discourse thread: https://discourse.llvm.org/t/rfc-add-zext-nneg-flag/73914 This patch is based on https://reviews.llvm.org/D156444 by @Panagiotis156, with some implementation simplifications and additional tests. --------- Co-authored-by: Panagiotis K <[email protected]>
1 parent 41f3b83 commit ed3f06b

File tree

16 files changed

+156
-4
lines changed

16 files changed

+156
-4
lines changed

llvm/docs/LangRef.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11229,6 +11229,10 @@ Overview:
1122911229

1123011230
The '``zext``' instruction zero extends its operand to type ``ty2``.
1123111231

11232+
The ``nneg`` (non-negative) flag, if present, specifies that the operand is
11233+
non-negative. This property may be used by optimization passes to later
11234+
convert the ``zext`` into a ``sext``.
11235+
1123211236
Arguments:
1123311237
""""""""""
1123411238

@@ -11245,6 +11249,9 @@ until it reaches the size of the destination type, ``ty2``.
1124511249

1124611250
When zero extending from i1, the result will always be either 0 or 1.
1124711251

11252+
If the ``nneg`` flag is set, and the ``zext`` argument is negative, the result
11253+
is a poison value.
11254+
1124811255
Example:
1124911256
""""""""
1125011257

@@ -11254,6 +11261,9 @@ Example:
1125411261
%Y = zext i1 true to i32 ; yields i32:1
1125511262
%Z = zext <2 x i16> <i16 8, i16 7> to <2 x i32> ; yields <i32 8, i32 7>
1125611263

11264+
%a = zext nneg i8 127 to i16 ; yields i16 127
11265+
%b = zext nneg i8 -1 to i16 ; yields i16 poison
11266+
1125711267
.. _i_sext:
1125811268

1125911269
'``sext .. to``' Instruction

llvm/include/llvm/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ enum Kind {
110110
kw_nsw,
111111
kw_exact,
112112
kw_inbounds,
113+
kw_nneg,
113114
kw_inrange,
114115
kw_addrspace,
115116
kw_section,

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ enum FastMathMap {
505505
AllowReassoc = (1 << 7)
506506
};
507507

508+
/// Flags for serializing PossiblyNonNegInst's SubclassOptionalData contents.
509+
enum PossiblyNonNegInstOptionalFlags { PNNI_NON_NEG = 0 };
510+
508511
/// PossiblyExactOperatorOptionalFlags - Flags for serializing
509512
/// PossiblyExactOperator's SubclassOptionalData contents.
510513
enum PossiblyExactOperatorOptionalFlags { PEO_EXACT = 0 };

llvm/include/llvm/IR/InstrTypes.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,20 @@ class CastInst : public UnaryInstruction {
692692
}
693693
};
694694

695+
/// Instruction that can have a nneg flag (only zext).
696+
class PossiblyNonNegInst : public CastInst {
697+
public:
698+
enum { NonNeg = (1 << 0) };
699+
700+
static bool classof(const Instruction *I) {
701+
return I->getOpcode() == Instruction::ZExt;
702+
}
703+
704+
static bool classof(const Value *V) {
705+
return isa<Instruction>(V) && classof(cast<Instruction>(V));
706+
}
707+
};
708+
695709
//===----------------------------------------------------------------------===//
696710
// CmpInst Class
697711
//===----------------------------------------------------------------------===//

llvm/include/llvm/IR/Instruction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,19 @@ class Instruction : public User,
410410
/// which supports this flag. See LangRef.html for the meaning of this flag.
411411
void setIsExact(bool b = true);
412412

413+
/// Set or clear the nneg flag on this instruction, which must be a zext
414+
/// instruction.
415+
void setNonNeg(bool b = true);
416+
413417
/// Determine whether the no unsigned wrap flag is set.
414418
bool hasNoUnsignedWrap() const LLVM_READONLY;
415419

416420
/// Determine whether the no signed wrap flag is set.
417421
bool hasNoSignedWrap() const LLVM_READONLY;
418422

423+
/// Determine whether the the nneg flag is set.
424+
bool hasNonNeg() const LLVM_READONLY;
425+
419426
/// Return true if this operator has flags which may cause this instruction
420427
/// to evaluate to poison despite having non-poison inputs.
421428
bool hasPoisonGeneratingFlags() const LLVM_READONLY;

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ lltok::Kind LLLexer::LexIdentifier() {
565565
KEYWORD(nsw);
566566
KEYWORD(exact);
567567
KEYWORD(inbounds);
568+
KEYWORD(nneg);
568569
KEYWORD(inrange);
569570
KEYWORD(addrspace);
570571
KEYWORD(section);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6383,8 +6383,16 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB,
63836383
}
63846384

63856385
// Casts.
6386+
case lltok::kw_zext: {
6387+
bool NonNeg = EatIfPresent(lltok::kw_nneg);
6388+
bool Res = parseCast(Inst, PFS, KeywordVal);
6389+
if (Res != 0)
6390+
return Res;
6391+
if (NonNeg)
6392+
Inst->setNonNeg();
6393+
return 0;
6394+
}
63866395
case lltok::kw_trunc:
6387-
case lltok::kw_zext:
63886396
case lltok::kw_sext:
63896397
case lltok::kw_fptrunc:
63906398
case lltok::kw_fpext:

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4877,12 +4877,13 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
48774877
Value *Op;
48784878
unsigned OpTypeID;
48794879
if (getValueTypePair(Record, OpNum, NextValueNo, Op, OpTypeID, CurBB) ||
4880-
OpNum+2 != Record.size())
4880+
OpNum + 1 > Record.size())
48814881
return error("Invalid record");
48824882

4883-
ResTypeID = Record[OpNum];
4883+
ResTypeID = Record[OpNum++];
48844884
Type *ResTy = getTypeByID(ResTypeID);
4885-
int Opc = getDecodedCastOpcode(Record[OpNum + 1]);
4885+
int Opc = getDecodedCastOpcode(Record[OpNum++]);
4886+
48864887
if (Opc == -1 || !ResTy)
48874888
return error("Invalid record");
48884889
Instruction *Temp = nullptr;
@@ -4898,6 +4899,9 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
48984899
return error("Invalid cast");
48994900
I = CastInst::Create(CastOp, Op, ResTy);
49004901
}
4902+
if (OpNum < Record.size() && isa<PossiblyNonNegInst>(I) &&
4903+
(Record[OpNum] & (1 << bitc::PNNI_NON_NEG)))
4904+
I->setNonNeg(true);
49014905
InstructionList.push_back(I);
49024906
break;
49034907
}

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ enum {
123123
FUNCTION_INST_BINOP_ABBREV,
124124
FUNCTION_INST_BINOP_FLAGS_ABBREV,
125125
FUNCTION_INST_CAST_ABBREV,
126+
FUNCTION_INST_CAST_FLAGS_ABBREV,
126127
FUNCTION_INST_RET_VOID_ABBREV,
127128
FUNCTION_INST_RET_VAL_ABBREV,
128129
FUNCTION_INST_UNREACHABLE_ABBREV,
@@ -1551,6 +1552,9 @@ static uint64_t getOptimizationFlags(const Value *V) {
15511552
Flags |= bitc::AllowContract;
15521553
if (FPMO->hasApproxFunc())
15531554
Flags |= bitc::ApproxFunc;
1555+
} else if (const auto *NNI = dyn_cast<PossiblyNonNegInst>(V)) {
1556+
if (NNI->hasNonNeg())
1557+
Flags |= 1 << bitc::PNNI_NON_NEG;
15541558
}
15551559

15561560
return Flags;
@@ -2827,6 +2831,12 @@ void ModuleBitcodeWriter::writeInstruction(const Instruction &I,
28272831
AbbrevToUse = FUNCTION_INST_CAST_ABBREV;
28282832
Vals.push_back(VE.getTypeID(I.getType()));
28292833
Vals.push_back(getEncodedCastOpcode(I.getOpcode()));
2834+
uint64_t Flags = getOptimizationFlags(&I);
2835+
if (Flags != 0) {
2836+
if (AbbrevToUse == FUNCTION_INST_CAST_ABBREV)
2837+
AbbrevToUse = FUNCTION_INST_CAST_FLAGS_ABBREV;
2838+
Vals.push_back(Flags);
2839+
}
28302840
} else {
28312841
assert(isa<BinaryOperator>(I) && "Unknown instruction!");
28322842
Code = bitc::FUNC_CODE_INST_BINOP;
@@ -3648,6 +3658,18 @@ void ModuleBitcodeWriter::writeBlockInfo() {
36483658
FUNCTION_INST_CAST_ABBREV)
36493659
llvm_unreachable("Unexpected abbrev ordering!");
36503660
}
3661+
{ // INST_CAST_FLAGS abbrev for FUNCTION_BLOCK.
3662+
auto Abbv = std::make_shared<BitCodeAbbrev>();
3663+
Abbv->Add(BitCodeAbbrevOp(bitc::FUNC_CODE_INST_CAST));
3664+
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // OpVal
3665+
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, // dest ty
3666+
VE.computeBitsRequiredForTypeIndicies()));
3667+
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 4)); // opc
3668+
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // flags
3669+
if (Stream.EmitBlockInfoAbbrev(bitc::FUNCTION_BLOCK_ID, Abbv) !=
3670+
FUNCTION_INST_CAST_FLAGS_ABBREV)
3671+
llvm_unreachable("Unexpected abbrev ordering!");
3672+
}
36513673

36523674
{ // INST_RET abbrev for FUNCTION_BLOCK.
36533675
auto Abbv = std::make_shared<BitCodeAbbrev>();

llvm/lib/IR/AsmWriter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,9 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) {
13481348
} else if (const GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
13491349
if (GEP->isInBounds())
13501350
Out << " inbounds";
1351+
} else if (const auto *NNI = dyn_cast<PossiblyNonNegInst>(U)) {
1352+
if (NNI->hasNonNeg())
1353+
Out << " nneg";
13511354
}
13521355
}
13531356

llvm/lib/IR/Instruction.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ void Instruction::setIsExact(bool b) {
171171
cast<PossiblyExactOperator>(this)->setIsExact(b);
172172
}
173173

174+
void Instruction::setNonNeg(bool b) {
175+
assert(isa<PossiblyNonNegInst>(this) && "Must be zext");
176+
SubclassOptionalData = (SubclassOptionalData & ~PossiblyNonNegInst::NonNeg) |
177+
(b * PossiblyNonNegInst::NonNeg);
178+
}
179+
174180
bool Instruction::hasNoUnsignedWrap() const {
175181
return cast<OverflowingBinaryOperator>(this)->hasNoUnsignedWrap();
176182
}
@@ -179,6 +185,11 @@ bool Instruction::hasNoSignedWrap() const {
179185
return cast<OverflowingBinaryOperator>(this)->hasNoSignedWrap();
180186
}
181187

188+
bool Instruction::hasNonNeg() const {
189+
assert(isa<PossiblyNonNegInst>(this) && "Must be zext");
190+
return (SubclassOptionalData & PossiblyNonNegInst::NonNeg) != 0;
191+
}
192+
182193
bool Instruction::hasPoisonGeneratingFlags() const {
183194
return cast<Operator>(this)->hasPoisonGeneratingFlags();
184195
}
@@ -203,7 +214,12 @@ void Instruction::dropPoisonGeneratingFlags() {
203214
case Instruction::GetElementPtr:
204215
cast<GetElementPtrInst>(this)->setIsInBounds(false);
205216
break;
217+
218+
case Instruction::ZExt:
219+
setNonNeg(false);
220+
break;
206221
}
222+
207223
if (isa<FPMathOperator>(this)) {
208224
setHasNoNaNs(false);
209225
setHasNoInfs(false);
@@ -378,6 +394,10 @@ void Instruction::copyIRFlags(const Value *V, bool IncludeWrapFlags) {
378394
if (auto *SrcGEP = dyn_cast<GetElementPtrInst>(V))
379395
if (auto *DestGEP = dyn_cast<GetElementPtrInst>(this))
380396
DestGEP->setIsInBounds(SrcGEP->isInBounds() || DestGEP->isInBounds());
397+
398+
if (auto *NNI = dyn_cast<PossiblyNonNegInst>(V))
399+
if (isa<PossiblyNonNegInst>(this))
400+
setNonNeg(NNI->hasNonNeg());
381401
}
382402

383403
void Instruction::andIRFlags(const Value *V) {
@@ -403,6 +423,10 @@ void Instruction::andIRFlags(const Value *V) {
403423
if (auto *SrcGEP = dyn_cast<GetElementPtrInst>(V))
404424
if (auto *DestGEP = dyn_cast<GetElementPtrInst>(this))
405425
DestGEP->setIsInBounds(SrcGEP->isInBounds() && DestGEP->isInBounds());
426+
427+
if (auto *NNI = dyn_cast<PossiblyNonNegInst>(V))
428+
if (isa<PossiblyNonNegInst>(this))
429+
setNonNeg(hasNonNeg() && NNI->hasNonNeg());
406430
}
407431

408432
const char *Instruction::getOpcodeName(unsigned OpCode) {

llvm/lib/IR/Operator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ bool Operator::hasPoisonGeneratingFlags() const {
3737
// Note: inrange exists on constexpr only
3838
return GEP->isInBounds() || GEP->getInRangeIndex() != std::nullopt;
3939
}
40+
case Instruction::ZExt:
41+
if (auto *NNI = dyn_cast<PossiblyNonNegInst>(this))
42+
return NNI->hasNonNeg();
43+
return false;
4044
default:
4145
if (const auto *FP = dyn_cast<FPMathOperator>(this))
4246
return FP->hasNoNaNs() || FP->hasNoInfs();

llvm/test/Assembler/flags.ll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,9 @@ define i64 @mul_unsigned_ce() {
260260
ret i64 mul nuw (i64 ptrtoint (ptr @addr to i64), i64 91)
261261
}
262262

263+
define i64 @test_zext(i32 %a) {
264+
; CHECK: %res = zext nneg i32 %a to i64
265+
%res = zext nneg i32 %a to i64
266+
ret i64 %res
267+
}
268+

llvm/test/Bitcode/flags.ll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ second: ; preds = %first
1616
%s = add nsw i32 %a, 0 ; <i32> [#uses=0]
1717
%us = add nuw nsw i32 %a, 0 ; <i32> [#uses=0]
1818
%z = add i32 %a, 0 ; <i32> [#uses=0]
19+
%hh = zext nneg i32 %a to i64
20+
%ll = zext i32 %s to i64
1921
unreachable
2022

2123
first: ; preds = %entry
@@ -24,5 +26,7 @@ first: ; preds = %entry
2426
%ss = add nsw i32 %a, 0 ; <i32> [#uses=0]
2527
%uuss = add nuw nsw i32 %a, 0 ; <i32> [#uses=0]
2628
%zz = add i32 %a, 0 ; <i32> [#uses=0]
29+
%kk = zext nneg i32 %a to i64
30+
%rr = zext i32 %ss to i64
2731
br label %second
2832
}

llvm/test/Transforms/InstCombine/freeze.ll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,17 @@ define i32 @freeze_ctpop(i32 %x) {
11161116
ret i32 %fr
11171117
}
11181118

1119+
define i32 @freeze_zext_nneg(i8 %x) {
1120+
; CHECK-LABEL: @freeze_zext_nneg(
1121+
; CHECK-NEXT: [[X_FR:%.*]] = freeze i8 [[X:%.*]]
1122+
; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X_FR]] to i32
1123+
; CHECK-NEXT: ret i32 [[ZEXT]]
1124+
;
1125+
%zext = zext nneg i8 %x to i32
1126+
%fr = freeze i32 %zext
1127+
ret i32 %fr
1128+
}
1129+
11191130
!0 = !{}
11201131
!1 = !{i64 4}
11211132
!2 = !{i32 0, i32 100}

llvm/test/Transforms/SimplifyCFG/HoistCode.ll

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,33 @@ end:
9494
%cond = phi fast float [ 0.0, %bb0 ], [ %x, %bb1 ], [ %x, %bb2 ]
9595
ret float %cond
9696
}
97+
98+
define i32 @hoist_zext_flags_preserve(i1 %C, i8 %x) {
99+
; CHECK-LABEL: @hoist_zext_flags_preserve(
100+
; CHECK-NEXT: common.ret:
101+
; CHECK-NEXT: [[Z1:%.*]] = zext nneg i8 [[X:%.*]] to i32
102+
; CHECK-NEXT: ret i32 [[Z1]]
103+
;
104+
br i1 %C, label %T, label %F
105+
T:
106+
%z1 = zext nneg i8 %x to i32
107+
ret i32 %z1
108+
F:
109+
%z2 = zext nneg i8 %x to i32
110+
ret i32 %z2
111+
}
112+
113+
define i32 @hoist_zext_flags_drop(i1 %C, i8 %x) {
114+
; CHECK-LABEL: @hoist_zext_flags_drop(
115+
; CHECK-NEXT: common.ret:
116+
; CHECK-NEXT: [[Z1:%.*]] = zext i8 [[X:%.*]] to i32
117+
; CHECK-NEXT: ret i32 [[Z1]]
118+
;
119+
br i1 %C, label %T, label %F
120+
T:
121+
%z1 = zext nneg i8 %x to i32
122+
ret i32 %z1
123+
F:
124+
%z2 = zext i8 %x to i32
125+
ret i32 %z2
126+
}

0 commit comments

Comments
 (0)