Skip to content

Commit 7d3924c

Browse files
authored
[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
1 parent ba6b2d2 commit 7d3924c

File tree

15 files changed

+281
-20
lines changed

15 files changed

+281
-20
lines changed

llvm/docs/LangRef.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11419,6 +11419,9 @@ Syntax:
1141911419
::
1142011420

1142111421
<result> = trunc <ty> <value> to <ty2> ; yields ty2
11422+
<result> = trunc nsw <ty> <value> to <ty2> ; yields ty2
11423+
<result> = trunc nuw <ty> <value> to <ty2> ; yields ty2
11424+
<result> = trunc nuw nsw <ty> <value> to <ty2> ; yields ty2
1142211425

1142311426
Overview:
1142411427
"""""""""
@@ -11442,6 +11445,11 @@ and converts the remaining bits to ``ty2``. Since the source size must
1144211445
be larger than the destination size, ``trunc`` cannot be a *no-op cast*.
1144311446
It will always truncate bits.
1144411447

11448+
If the ``nuw`` keyword is present, and any of the truncated bits are zero,
11449+
the result is a :ref:`poison value <poisonvalues>`. If the ``nsw`` keyword
11450+
is present, and any of the truncated bits are not the same as the top bit
11451+
of the truncation result, the result is a :ref:`poison value <poisonvalues>`.
11452+
1144511453
Example:
1144611454
""""""""
1144711455

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,13 @@ enum OverflowingBinaryOperatorOptionalFlags {
492492
OBO_NO_SIGNED_WRAP = 1
493493
};
494494

495+
/// TruncInstOptionalFlags - Flags for serializing
496+
/// TruncInstOptionalFlags's SubclassOptionalData contents.
497+
enum TruncInstOptionalFlags {
498+
TIO_NO_UNSIGNED_WRAP = 0,
499+
TIO_NO_SIGNED_WRAP = 1
500+
};
501+
495502
/// FastMath Flags
496503
/// This is a fixed layout derived from the bitcode emitted by LLVM 5.0
497504
/// intended to decouple the in-memory representation from the serialization.

llvm/include/llvm/IR/Instructions.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5345,6 +5345,8 @@ class TruncInst : public CastInst {
53455345
TruncInst *cloneImpl() const;
53465346

53475347
public:
5348+
enum { AnyWrap = 0, NoUnsignedWrap = (1 << 0), NoSignedWrap = (1 << 1) };
5349+
53485350
/// Constructor with insert-before-instruction semantics
53495351
TruncInst(
53505352
Value *S, ///< The value to be truncated
@@ -5376,6 +5378,39 @@ class TruncInst : public CastInst {
53765378
static bool classof(const Value *V) {
53775379
return isa<Instruction>(V) && classof(cast<Instruction>(V));
53785380
}
5381+
5382+
void setHasNoUnsignedWrap(bool B) {
5383+
SubclassOptionalData =
5384+
(SubclassOptionalData & ~NoUnsignedWrap) | (B * NoUnsignedWrap);
5385+
}
5386+
void setHasNoSignedWrap(bool B) {
5387+
SubclassOptionalData =
5388+
(SubclassOptionalData & ~NoSignedWrap) | (B * NoSignedWrap);
5389+
}
5390+
5391+
/// Test whether this operation is known to never
5392+
/// undergo unsigned overflow, aka the nuw property.
5393+
bool hasNoUnsignedWrap() const {
5394+
return SubclassOptionalData & NoUnsignedWrap;
5395+
}
5396+
5397+
/// Test whether this operation is known to never
5398+
/// undergo signed overflow, aka the nsw property.
5399+
bool hasNoSignedWrap() const {
5400+
return (SubclassOptionalData & NoSignedWrap) != 0;
5401+
}
5402+
5403+
/// Returns the no-wrap kind of the operation.
5404+
unsigned getNoWrapKind() const {
5405+
unsigned NoWrapKind = 0;
5406+
if (hasNoUnsignedWrap())
5407+
NoWrapKind |= NoUnsignedWrap;
5408+
5409+
if (hasNoSignedWrap())
5410+
NoWrapKind |= NoSignedWrap;
5411+
5412+
return NoWrapKind;
5413+
}
53795414
};
53805415

53815416
//===----------------------------------------------------------------------===//

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6814,7 +6814,19 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB,
68146814
Inst->setNonNeg();
68156815
return 0;
68166816
}
6817-
case lltok::kw_trunc:
6817+
case lltok::kw_trunc: {
6818+
bool NUW = EatIfPresent(lltok::kw_nuw);
6819+
bool NSW = EatIfPresent(lltok::kw_nsw);
6820+
if (!NUW)
6821+
NUW = EatIfPresent(lltok::kw_nuw);
6822+
if (parseCast(Inst, PFS, KeywordVal))
6823+
return true;
6824+
if (NUW)
6825+
cast<TruncInst>(Inst)->setHasNoUnsignedWrap(true);
6826+
if (NSW)
6827+
cast<TruncInst>(Inst)->setHasNoSignedWrap(true);
6828+
return false;
6829+
}
68186830
case lltok::kw_sext:
68196831
case lltok::kw_fptrunc:
68206832
case lltok::kw_fpext:

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5022,9 +5022,19 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
50225022
return error("Invalid cast");
50235023
I = CastInst::Create(CastOp, Op, ResTy);
50245024
}
5025-
if (OpNum < Record.size() && isa<PossiblyNonNegInst>(I) &&
5026-
(Record[OpNum] & (1 << bitc::PNNI_NON_NEG)))
5027-
I->setNonNeg(true);
5025+
5026+
if (OpNum < Record.size()) {
5027+
if (Opc == Instruction::ZExt) {
5028+
if (Record[OpNum] & (1 << bitc::PNNI_NON_NEG))
5029+
cast<PossiblyNonNegInst>(I)->setNonNeg(true);
5030+
} else if (Opc == Instruction::Trunc) {
5031+
if (Record[OpNum] & (1 << bitc::TIO_NO_UNSIGNED_WRAP))
5032+
cast<TruncInst>(I)->setHasNoUnsignedWrap(true);
5033+
if (Record[OpNum] & (1 << bitc::TIO_NO_SIGNED_WRAP))
5034+
cast<TruncInst>(I)->setHasNoSignedWrap(true);
5035+
}
5036+
}
5037+
50285038
InstructionList.push_back(I);
50295039
break;
50305040
}

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,11 @@ static uint64_t getOptimizationFlags(const Value *V) {
16401640
} else if (const auto *NNI = dyn_cast<PossiblyNonNegInst>(V)) {
16411641
if (NNI->hasNonNeg())
16421642
Flags |= 1 << bitc::PNNI_NON_NEG;
1643+
} else if (const auto *TI = dyn_cast<TruncInst>(V)) {
1644+
if (TI->hasNoSignedWrap())
1645+
Flags |= 1 << bitc::TIO_NO_SIGNED_WRAP;
1646+
if (TI->hasNoUnsignedWrap())
1647+
Flags |= 1 << bitc::TIO_NO_UNSIGNED_WRAP;
16431648
}
16441649

16451650
return Flags;

llvm/lib/IR/AsmWriter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,11 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) {
14241424
} else if (const auto *NNI = dyn_cast<PossiblyNonNegInst>(U)) {
14251425
if (NNI->hasNonNeg())
14261426
Out << " nneg";
1427+
} else if (const auto *TI = dyn_cast<TruncInst>(U)) {
1428+
if (TI->hasNoUnsignedWrap())
1429+
Out << " nuw";
1430+
if (TI->hasNoSignedWrap())
1431+
Out << " nsw";
14271432
}
14281433
}
14291434

llvm/lib/IR/Instruction.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,17 @@ bool Instruction::isOnlyUserOfAnyOperand() {
370370
}
371371

372372
void Instruction::setHasNoUnsignedWrap(bool b) {
373-
cast<OverflowingBinaryOperator>(this)->setHasNoUnsignedWrap(b);
373+
if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this))
374+
Inst->setHasNoUnsignedWrap(b);
375+
else
376+
cast<TruncInst>(this)->setHasNoUnsignedWrap(b);
374377
}
375378

376379
void Instruction::setHasNoSignedWrap(bool b) {
377-
cast<OverflowingBinaryOperator>(this)->setHasNoSignedWrap(b);
380+
if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this))
381+
Inst->setHasNoSignedWrap(b);
382+
else
383+
cast<TruncInst>(this)->setHasNoSignedWrap(b);
378384
}
379385

380386
void Instruction::setIsExact(bool b) {
@@ -388,11 +394,17 @@ void Instruction::setNonNeg(bool b) {
388394
}
389395

390396
bool Instruction::hasNoUnsignedWrap() const {
391-
return cast<OverflowingBinaryOperator>(this)->hasNoUnsignedWrap();
397+
if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this))
398+
return Inst->hasNoUnsignedWrap();
399+
400+
return cast<TruncInst>(this)->hasNoUnsignedWrap();
392401
}
393402

394403
bool Instruction::hasNoSignedWrap() const {
395-
return cast<OverflowingBinaryOperator>(this)->hasNoSignedWrap();
404+
if (auto *Inst = dyn_cast<OverflowingBinaryOperator>(this))
405+
return Inst->hasNoSignedWrap();
406+
407+
return cast<TruncInst>(this)->hasNoSignedWrap();
396408
}
397409

398410
bool Instruction::hasNonNeg() const {
@@ -432,6 +444,11 @@ void Instruction::dropPoisonGeneratingFlags() {
432444
case Instruction::ZExt:
433445
setNonNeg(false);
434446
break;
447+
448+
case Instruction::Trunc:
449+
cast<TruncInst>(this)->setHasNoUnsignedWrap(false);
450+
cast<TruncInst>(this)->setHasNoSignedWrap(false);
451+
break;
435452
}
436453

437454
if (isa<FPMathOperator>(this)) {
@@ -626,6 +643,13 @@ void Instruction::andIRFlags(const Value *V) {
626643
}
627644
}
628645

646+
if (auto *TI = dyn_cast<TruncInst>(V)) {
647+
if (isa<TruncInst>(this)) {
648+
setHasNoSignedWrap(hasNoSignedWrap() && TI->hasNoSignedWrap());
649+
setHasNoUnsignedWrap(hasNoUnsignedWrap() && TI->hasNoUnsignedWrap());
650+
}
651+
}
652+
629653
if (auto *PE = dyn_cast<PossiblyExactOperator>(V))
630654
if (isa<PossiblyExactOperator>(this))
631655
setIsExact(isExact() && PE->isExact());

llvm/lib/IR/Operator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ bool Operator::hasPoisonGeneratingFlags() const {
2727
auto *OBO = cast<OverflowingBinaryOperator>(this);
2828
return OBO->hasNoUnsignedWrap() || OBO->hasNoSignedWrap();
2929
}
30+
case Instruction::Trunc: {
31+
auto *TI = dyn_cast<TruncInst>(this);
32+
return TI->hasNoUnsignedWrap() || TI->hasNoSignedWrap();
33+
}
3034
case Instruction::UDiv:
3135
case Instruction::SDiv:
3236
case Instruction::AShr:

llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ PoisonFlags::PoisonFlags(const Instruction *I) {
5959
Disjoint = PDI->isDisjoint();
6060
if (auto *PNI = dyn_cast<PossiblyNonNegInst>(I))
6161
NNeg = PNI->hasNonNeg();
62+
if (auto *TI = dyn_cast<TruncInst>(I)) {
63+
NUW = TI->hasNoUnsignedWrap();
64+
NSW = TI->hasNoSignedWrap();
65+
}
6266
}
6367

6468
void PoisonFlags::apply(Instruction *I) {
@@ -72,6 +76,10 @@ void PoisonFlags::apply(Instruction *I) {
7276
PDI->setIsDisjoint(Disjoint);
7377
if (auto *PNI = dyn_cast<PossiblyNonNegInst>(I))
7478
PNI->setNonNeg(NNeg);
79+
if (isa<TruncInst>(I)) {
80+
I->setHasNoUnsignedWrap(NUW);
81+
I->setHasNoSignedWrap(NSW);
82+
}
7583
}
7684

7785
/// ReuseOrCreateCast - Arrange for there to be a cast of V to Ty at IP,

llvm/test/Assembler/flags.ll

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,51 @@ define i64 @test_or(i64 %a, i64 %b) {
261261
%res = or disjoint i64 %a, %b
262262
ret i64 %res
263263
}
264+
265+
define i32 @test_trunc_signed(i64 %a) {
266+
; CHECK: %res = trunc nsw i64 %a to i32
267+
%res = trunc nsw i64 %a to i32
268+
ret i32 %res
269+
}
270+
271+
define i32 @test_trunc_unsigned(i64 %a) {
272+
; CHECK: %res = trunc nuw i64 %a to i32
273+
%res = trunc nuw i64 %a to i32
274+
ret i32 %res
275+
}
276+
277+
define i32 @test_trunc_both(i64 %a) {
278+
; CHECK: %res = trunc nuw nsw i64 %a to i32
279+
%res = trunc nuw nsw i64 %a to i32
280+
ret i32 %res
281+
}
282+
283+
define i32 @test_trunc_both_reversed(i64 %a) {
284+
; CHECK: %res = trunc nuw nsw i64 %a to i32
285+
%res = trunc nsw nuw i64 %a to i32
286+
ret i32 %res
287+
}
288+
289+
define <2 x i32> @test_trunc_signed_vector(<2 x i64> %a) {
290+
; CHECK: %res = trunc nsw <2 x i64> %a to <2 x i32>
291+
%res = trunc nsw <2 x i64> %a to <2 x i32>
292+
ret <2 x i32> %res
293+
}
294+
295+
define <2 x i32> @test_trunc_unsigned_vector(<2 x i64> %a) {
296+
; CHECK: %res = trunc nuw <2 x i64> %a to <2 x i32>
297+
%res = trunc nuw <2 x i64> %a to <2 x i32>
298+
ret <2 x i32> %res
299+
}
300+
301+
define <2 x i32> @test_trunc_both_vector(<2 x i64> %a) {
302+
; CHECK: %res = trunc nuw nsw <2 x i64> %a to <2 x i32>
303+
%res = trunc nuw nsw <2 x i64> %a to <2 x i32>
304+
ret <2 x i32> %res
305+
}
306+
307+
define <2 x i32> @test_trunc_both_reversed_vector(<2 x i64> %a) {
308+
; CHECK: %res = trunc nuw nsw <2 x i64> %a to <2 x i32>
309+
%res = trunc nsw nuw <2 x i64> %a to <2 x i32>
310+
ret <2 x i32> %res
311+
}

llvm/test/Bitcode/flags.ll

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,34 @@ second: ; preds = %first
2020
%ll = zext i32 %s to i64
2121
%jj = or disjoint i32 %a, 0
2222
%oo = or i32 %a, 0
23+
%tu = trunc nuw i32 %a to i16
24+
%ts = trunc nsw i32 %a to i16
25+
%tus = trunc nuw nsw i32 %a to i16
26+
%t = trunc i32 %a to i16
27+
%tuv = trunc nuw <2 x i32> %aa to <2 x i16>
28+
%tsv = trunc nsw <2 x i32> %aa to <2 x i16>
29+
%tusv = trunc nuw nsw <2 x i32> %aa to <2 x i16>
30+
%tv = trunc <2 x i32> %aa to <2 x i16>
2331
unreachable
2432

25-
first: ; preds = %entry
26-
%a = bitcast i32 0 to i32 ; <i32> [#uses=8]
27-
%uu = add nuw i32 %a, 0 ; <i32> [#uses=0]
28-
%ss = add nsw i32 %a, 0 ; <i32> [#uses=0]
29-
%uuss = add nuw nsw i32 %a, 0 ; <i32> [#uses=0]
30-
%zz = add i32 %a, 0 ; <i32> [#uses=0]
33+
first: ; preds = %entry
34+
%aa = bitcast <2 x i32> <i32 0, i32 0> to <2 x i32>
35+
%a = bitcast i32 0 to i32 ; <i32> [#uses=8]
36+
%uu = add nuw i32 %a, 0 ; <i32> [#uses=0]
37+
%ss = add nsw i32 %a, 0 ; <i32> [#uses=0]
38+
%uuss = add nuw nsw i32 %a, 0 ; <i32> [#uses=0]
39+
%zz = add i32 %a, 0 ; <i32> [#uses=0]
3140
%kk = zext nneg i32 %a to i64
3241
%rr = zext i32 %ss to i64
3342
%mm = or disjoint i32 %a, 0
3443
%nn = or i32 %a, 0
44+
%tuu = trunc nuw i32 %a to i16
45+
%tss = trunc nsw i32 %a to i16
46+
%tuss = trunc nuw nsw i32 %a to i16
47+
%tt = trunc i32 %a to i16
48+
%ttuv = trunc nuw <2 x i32> %aa to <2 x i16>
49+
%ttsv = trunc nsw <2 x i32> %aa to <2 x i16>
50+
%ttusv = trunc nuw nsw <2 x i32> %aa to <2 x i16>
51+
%ttv = trunc <2 x i32> %aa to <2 x i16>
3552
br label %second
3653
}

llvm/test/Transforms/InstCombine/freeze.ll

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ exit:
10491049

10501050
define ptr @freeze_load_noundef(ptr %ptr) {
10511051
; CHECK-LABEL: @freeze_load_noundef(
1052-
; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !noundef !0
1052+
; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !noundef [[META0:![0-9]+]]
10531053
; CHECK-NEXT: ret ptr [[P]]
10541054
;
10551055
%p = load ptr, ptr %ptr, !noundef !0
@@ -1059,7 +1059,7 @@ define ptr @freeze_load_noundef(ptr %ptr) {
10591059

10601060
define ptr @freeze_load_dereferenceable(ptr %ptr) {
10611061
; CHECK-LABEL: @freeze_load_dereferenceable(
1062-
; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !dereferenceable !1
1062+
; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !dereferenceable [[META1:![0-9]+]]
10631063
; CHECK-NEXT: ret ptr [[P]]
10641064
;
10651065
%p = load ptr, ptr %ptr, !dereferenceable !1
@@ -1138,15 +1138,26 @@ define i32 @propagate_drop_flags_or(i32 %arg) {
11381138
ret i32 %v1.fr
11391139
}
11401140

1141+
define i32 @propagate_drop_flags_trunc(i64 %arg) {
1142+
; CHECK-LABEL: @propagate_drop_flags_trunc(
1143+
; CHECK-NEXT: [[ARG_FR:%.*]] = freeze i64 [[ARG:%.*]]
1144+
; CHECK-NEXT: [[V1:%.*]] = trunc i64 [[ARG_FR]] to i32
1145+
; CHECK-NEXT: ret i32 [[V1]]
1146+
;
1147+
%v1 = trunc nsw nuw i64 %arg to i32
1148+
%v1.fr = freeze i32 %v1
1149+
ret i32 %v1.fr
1150+
}
1151+
11411152
!0 = !{}
11421153
!1 = !{i64 4}
11431154
!2 = !{i32 0, i32 100}
11441155
;.
11451156
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
11461157
; CHECK: attributes #[[ATTR1]] = { nounwind }
11471158
;.
1148-
; CHECK: [[META0:![0-9]+]] = !{}
1149-
; CHECK: [[META1:![0-9]+]] = !{i64 4}
1159+
; CHECK: [[META0]] = !{}
1160+
; CHECK: [[META1]] = !{i64 4}
11501161
; CHECK: [[RNG2]] = !{i32 0, i32 100}
11511162
; CHECK: [[RNG3]] = !{i32 0, i32 33}
11521163
;.

0 commit comments

Comments
 (0)