Skip to content

Commit d9962c4

Browse files
authored
[IR] Add disjoint flag for Or instructions. (#72583)
This flag indicates that every bit is known to be zero in at least one of the inputs. This allows the Or to be treated as an Add since there is no possibility of a carry from any bit. If the flag is present and this property does not hold, the result is poison. This makes it easier to reverse the InstCombine transform that turns Add into Or. This is inspired by a comment here #71955 (comment) Discourse thread https://discourse.llvm.org/t/rfc-add-or-disjoint-flag/75036
1 parent 5d501b1 commit d9962c4

File tree

17 files changed

+135
-3
lines changed

17 files changed

+135
-3
lines changed

llvm/docs/LangRef.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9981,6 +9981,7 @@ Syntax:
99819981
::
99829982

99839983
<result> = or <ty> <op1>, <op2> ; yields ty:result
9984+
<result> = or disjoint <ty> <op1>, <op2> ; yields ty:result
99849985

99859986
Overview:
99869987
"""""""""
@@ -10012,6 +10013,12 @@ The truth table used for the '``or``' instruction is:
1001210013
| 1 | 1 | 1 |
1001310014
+-----+-----+-----+
1001410015

10016+
``disjoint`` means that for each bit, that bit is zero in at least one of the
10017+
inputs. This allows the Or to be treated as an Add since no carry can occur from
10018+
any bit. If the disjoint keyword is present, the result value of the ``or`` is a
10019+
:ref:`poison value <poisonvalues>` if both inputs have a one in the same bit
10020+
position. For vectors, only the element containing the bit is poison.
10021+
1001510022
Example:
1001610023
""""""""
1001710024

llvm/include/llvm/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ enum Kind {
109109
kw_nuw,
110110
kw_nsw,
111111
kw_exact,
112+
kw_disjoint,
112113
kw_inbounds,
113114
kw_nneg,
114115
kw_inrange,

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,10 @@ enum PossiblyNonNegInstOptionalFlags { PNNI_NON_NEG = 0 };
512512
/// PossiblyExactOperator's SubclassOptionalData contents.
513513
enum PossiblyExactOperatorOptionalFlags { PEO_EXACT = 0 };
514514

515+
/// PossiblyDisjointInstOptionalFlags - Flags for serializing
516+
/// PossiblyDisjointInst's SubclassOptionalData contents.
517+
enum PossiblyDisjointInstOptionalFlags { PDI_DISJOINT = 0 };
518+
515519
/// Encoded AtomicOrdering values.
516520
enum AtomicOrderingCodes {
517521
ORDERING_NOTATOMIC = 0,

llvm/include/llvm/IR/InstrTypes.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,29 @@ struct OperandTraits<BinaryOperator> :
415415

416416
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BinaryOperator, Value)
417417

418+
/// An or instruction, which can be marked as "disjoint", indicating that the
419+
/// inputs don't have a 1 in the same bit position. Meaning this instruction
420+
/// can also be treated as an add.
421+
class PossiblyDisjointInst : public BinaryOperator {
422+
public:
423+
enum { IsDisjoint = (1 << 0) };
424+
425+
void setIsDisjoint(bool B) {
426+
SubclassOptionalData =
427+
(SubclassOptionalData & ~IsDisjoint) | (B * IsDisjoint);
428+
}
429+
430+
bool isDisjoint() const { return SubclassOptionalData & IsDisjoint; }
431+
432+
static bool classof(const Instruction *I) {
433+
return I->getOpcode() == Instruction::Or;
434+
}
435+
436+
static bool classof(const Value *V) {
437+
return isa<Instruction>(V) && classof(cast<Instruction>(V));
438+
}
439+
};
440+
418441
//===----------------------------------------------------------------------===//
419442
// CastInst Class
420443
//===----------------------------------------------------------------------===//

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ lltok::Kind LLLexer::LexIdentifier() {
564564
KEYWORD(nuw);
565565
KEYWORD(nsw);
566566
KEYWORD(exact);
567+
KEYWORD(disjoint);
567568
KEYWORD(inbounds);
568569
KEYWORD(nneg);
569570
KEYWORD(inrange);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6370,8 +6370,15 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB,
63706370
case lltok::kw_srem:
63716371
return parseArithmetic(Inst, PFS, KeywordVal,
63726372
/*IsFP*/ false);
6373+
case lltok::kw_or: {
6374+
bool Disjoint = EatIfPresent(lltok::kw_disjoint);
6375+
if (parseLogical(Inst, PFS, KeywordVal))
6376+
return true;
6377+
if (Disjoint)
6378+
cast<PossiblyDisjointInst>(Inst)->setIsDisjoint(true);
6379+
return false;
6380+
}
63736381
case lltok::kw_and:
6374-
case lltok::kw_or:
63756382
case lltok::kw_xor:
63766383
return parseLogical(Inst, PFS, KeywordVal);
63776384
case lltok::kw_icmp:

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4866,12 +4866,14 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
48664866
Opc == Instruction::AShr) {
48674867
if (Record[OpNum] & (1 << bitc::PEO_EXACT))
48684868
cast<BinaryOperator>(I)->setIsExact(true);
4869+
} else if (Opc == Instruction::Or) {
4870+
if (Record[OpNum] & (1 << bitc::PDI_DISJOINT))
4871+
cast<PossiblyDisjointInst>(I)->setIsDisjoint(true);
48694872
} else if (isa<FPMathOperator>(I)) {
48704873
FastMathFlags FMF = getDecodedFastMathFlags(Record[OpNum]);
48714874
if (FMF.any())
48724875
I->setFastMathFlags(FMF);
48734876
}
4874-
48754877
}
48764878
break;
48774879
}

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,9 @@ static uint64_t getOptimizationFlags(const Value *V) {
15401540
} else if (const auto *PEO = dyn_cast<PossiblyExactOperator>(V)) {
15411541
if (PEO->isExact())
15421542
Flags |= 1 << bitc::PEO_EXACT;
1543+
} else if (const auto *PDI = dyn_cast<PossiblyDisjointInst>(V)) {
1544+
if (PDI->isDisjoint())
1545+
Flags |= 1 << bitc::PDI_DISJOINT;
15431546
} else if (const auto *FPMO = dyn_cast<FPMathOperator>(V)) {
15441547
if (FPMO->hasAllowReassoc())
15451548
Flags |= bitc::AllowReassoc;

llvm/lib/IR/AsmWriter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,10 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) {
13551355
dyn_cast<PossiblyExactOperator>(U)) {
13561356
if (Div->isExact())
13571357
Out << " exact";
1358+
} else if (const PossiblyDisjointInst *PDI =
1359+
dyn_cast<PossiblyDisjointInst>(U)) {
1360+
if (PDI->isDisjoint())
1361+
Out << " disjoint";
13581362
} else if (const GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
13591363
if (GEP->isInBounds())
13601364
Out << " inbounds";

llvm/lib/IR/Instruction.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ void Instruction::dropPoisonGeneratingFlags() {
357357
cast<PossiblyExactOperator>(this)->setIsExact(false);
358358
break;
359359

360+
case Instruction::Or:
361+
cast<PossiblyDisjointInst>(this)->setIsDisjoint(false);
362+
break;
363+
360364
case Instruction::GetElementPtr:
361365
cast<GetElementPtrInst>(this)->setIsInBounds(false);
362366
break;
@@ -532,6 +536,10 @@ void Instruction::copyIRFlags(const Value *V, bool IncludeWrapFlags) {
532536
if (isa<PossiblyExactOperator>(this))
533537
setIsExact(PE->isExact());
534538

539+
if (auto *SrcPD = dyn_cast<PossiblyDisjointInst>(V))
540+
if (auto *DestPD = dyn_cast<PossiblyDisjointInst>(this))
541+
DestPD->setIsDisjoint(SrcPD->isDisjoint());
542+
535543
// Copy the fast-math flags.
536544
if (auto *FP = dyn_cast<FPMathOperator>(V))
537545
if (isa<FPMathOperator>(this))
@@ -558,6 +566,10 @@ void Instruction::andIRFlags(const Value *V) {
558566
if (isa<PossiblyExactOperator>(this))
559567
setIsExact(isExact() && PE->isExact());
560568

569+
if (auto *SrcPD = dyn_cast<PossiblyDisjointInst>(V))
570+
if (auto *DestPD = dyn_cast<PossiblyDisjointInst>(this))
571+
DestPD->setIsDisjoint(DestPD->isDisjoint() && SrcPD->isDisjoint());
572+
561573
if (auto *FP = dyn_cast<FPMathOperator>(V)) {
562574
if (isa<FPMathOperator>(this)) {
563575
FastMathFlags FM = getFastMathFlags();

llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,11 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Value *V, APInt DemandedMask,
231231
// If either the LHS or the RHS are One, the result is One.
232232
if (SimplifyDemandedBits(I, 1, DemandedMask, RHSKnown, Depth + 1) ||
233233
SimplifyDemandedBits(I, 0, DemandedMask & ~RHSKnown.One, LHSKnown,
234-
Depth + 1))
234+
Depth + 1)) {
235+
// Disjoint flag may not longer hold.
236+
I->dropPoisonGeneratingFlags();
235237
return I;
238+
}
236239
assert(!RHSKnown.hasConflict() && "Bits known to be one AND zero?");
237240
assert(!LHSKnown.hasConflict() && "Bits known to be one AND zero?");
238241

llvm/test/Assembler/flags.ll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,8 @@ define i64 @test_zext(i32 %a) {
256256
ret i64 %res
257257
}
258258

259+
define i64 @test_or(i64 %a, i64 %b) {
260+
; CHECK: %res = or disjoint i64 %a, %b
261+
%res = or disjoint i64 %a, %b
262+
ret i64 %res
263+
}

llvm/test/Bitcode/compatibility.ll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,10 @@ define void @instructions.bitwise_binops(i8 %op1, i8 %op2) {
13591359
xor i8 %op1, %op2
13601360
; CHECK: xor i8 %op1, %op2
13611361

1362+
; disjoint
1363+
or disjoint i8 %op1, %op2
1364+
; CHECK: or disjoint i8 %op1, %op2
1365+
13621366
ret void
13631367
}
13641368

llvm/test/Bitcode/flags.ll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ second: ; preds = %first
1818
%z = add i32 %a, 0 ; <i32> [#uses=0]
1919
%hh = zext nneg i32 %a to i64
2020
%ll = zext i32 %s to i64
21+
%jj = or disjoint i32 %a, 0
22+
%oo = or i32 %a, 0
2123
unreachable
2224

2325
first: ; preds = %entry
@@ -28,5 +30,7 @@ first: ; preds = %entry
2830
%zz = add i32 %a, 0 ; <i32> [#uses=0]
2931
%kk = zext nneg i32 %a to i64
3032
%rr = zext i32 %ss to i64
33+
%mm = or disjoint i32 %a, 0
34+
%nn = or i32 %a, 0
3135
br label %second
3236
}

llvm/test/Transforms/InstCombine/freeze.ll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,17 @@ define i32 @freeze_zext_nneg(i8 %x) {
11271127
ret i32 %fr
11281128
}
11291129

1130+
define i32 @propagate_drop_flags_or(i32 %arg) {
1131+
; CHECK-LABEL: @propagate_drop_flags_or(
1132+
; CHECK-NEXT: [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
1133+
; CHECK-NEXT: [[V1:%.*]] = or i32 [[ARG_FR]], 2
1134+
; CHECK-NEXT: ret i32 [[V1]]
1135+
;
1136+
%v1 = or disjoint i32 %arg, 2
1137+
%v1.fr = freeze i32 %v1
1138+
ret i32 %v1.fr
1139+
}
1140+
11301141
!0 = !{}
11311142
!1 = !{i64 4}
11321143
!2 = !{i32 0, i32 100}

llvm/test/Transforms/InstCombine/or.ll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,3 +1576,14 @@ define <4 x i1> @and_or_not_or_logical_vec(<4 x i32> %ap, <4 x i32> %bp) {
15761576
%Z = or <4 x i1> %X, %Y
15771577
ret <4 x i1> %Z
15781578
}
1579+
1580+
; Make sure SimplifyDemandedBits drops the disjoint flag.
1581+
define i8 @drop_disjoint(i8 %x) {
1582+
; CHECK-LABEL: @drop_disjoint(
1583+
; CHECK-NEXT: [[B:%.*]] = or i8 [[X:%.*]], 1
1584+
; CHECK-NEXT: ret i8 [[B]]
1585+
;
1586+
%a = and i8 %x, -2
1587+
%b = or disjoint i8 %a, 1
1588+
ret i8 %b
1589+
}

llvm/test/Transforms/SimplifyCFG/HoistCode.ll

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,33 @@ F:
124124
%z2 = zext i8 %x to i32
125125
ret i32 %z2
126126
}
127+
128+
define i32 @hoist_or_flags_preserve(i1 %C, i32 %x, i32 %y) {
129+
; CHECK-LABEL: @hoist_or_flags_preserve(
130+
; CHECK-NEXT: common.ret:
131+
; CHECK-NEXT: [[Z1:%.*]] = or disjoint i32 [[X:%.*]], [[Y:%.*]]
132+
; CHECK-NEXT: ret i32 [[Z1]]
133+
;
134+
br i1 %C, label %T, label %F
135+
T:
136+
%z1 = or disjoint i32 %x, %y
137+
ret i32 %z1
138+
F:
139+
%z2 = or disjoint i32 %x, %y
140+
ret i32 %z2
141+
}
142+
143+
define i32 @hoist_or_flags_drop(i1 %C, i32 %x, i32 %y) {
144+
; CHECK-LABEL: @hoist_or_flags_drop(
145+
; CHECK-NEXT: common.ret:
146+
; CHECK-NEXT: [[Z1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
147+
; CHECK-NEXT: ret i32 [[Z1]]
148+
;
149+
br i1 %C, label %T, label %F
150+
T:
151+
%z1 = or i32 %x, %y
152+
ret i32 %z1
153+
F:
154+
%z2 = or disjoint i32 %x, %y
155+
ret i32 %z2
156+
}

0 commit comments

Comments
 (0)