Skip to content

Commit

Permalink
[IR] Add disjoint flag for Or instructions. (#72583)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
topperc committed Nov 24, 2023
1 parent 5d501b1 commit d9962c4
Show file tree
Hide file tree
Showing 17 changed files with 135 additions and 3 deletions.
7 changes: 7 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9981,6 +9981,7 @@ Syntax:
::

<result> = or <ty> <op1>, <op2> ; yields ty:result
<result> = or disjoint <ty> <op1>, <op2> ; yields ty:result

Overview:
"""""""""
Expand Down Expand Up @@ -10012,6 +10013,12 @@ The truth table used for the '``or``' instruction is:
| 1 | 1 | 1 |
+-----+-----+-----+

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

Example:
""""""""

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ enum Kind {
kw_nuw,
kw_nsw,
kw_exact,
kw_disjoint,
kw_inbounds,
kw_nneg,
kw_inrange,
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,10 @@ enum PossiblyNonNegInstOptionalFlags { PNNI_NON_NEG = 0 };
/// PossiblyExactOperator's SubclassOptionalData contents.
enum PossiblyExactOperatorOptionalFlags { PEO_EXACT = 0 };

/// PossiblyDisjointInstOptionalFlags - Flags for serializing
/// PossiblyDisjointInst's SubclassOptionalData contents.
enum PossiblyDisjointInstOptionalFlags { PDI_DISJOINT = 0 };

/// Encoded AtomicOrdering values.
enum AtomicOrderingCodes {
ORDERING_NOTATOMIC = 0,
Expand Down
23 changes: 23 additions & 0 deletions llvm/include/llvm/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,29 @@ struct OperandTraits<BinaryOperator> :

DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BinaryOperator, Value)

/// An or instruction, which can be marked as "disjoint", indicating that the
/// inputs don't have a 1 in the same bit position. Meaning this instruction
/// can also be treated as an add.
class PossiblyDisjointInst : public BinaryOperator {
public:
enum { IsDisjoint = (1 << 0) };

void setIsDisjoint(bool B) {
SubclassOptionalData =
(SubclassOptionalData & ~IsDisjoint) | (B * IsDisjoint);
}

bool isDisjoint() const { return SubclassOptionalData & IsDisjoint; }

static bool classof(const Instruction *I) {
return I->getOpcode() == Instruction::Or;
}

static bool classof(const Value *V) {
return isa<Instruction>(V) && classof(cast<Instruction>(V));
}
};

//===----------------------------------------------------------------------===//
// CastInst Class
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nuw);
KEYWORD(nsw);
KEYWORD(exact);
KEYWORD(disjoint);
KEYWORD(inbounds);
KEYWORD(nneg);
KEYWORD(inrange);
Expand Down
9 changes: 8 additions & 1 deletion llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6370,8 +6370,15 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB,
case lltok::kw_srem:
return parseArithmetic(Inst, PFS, KeywordVal,
/*IsFP*/ false);
case lltok::kw_or: {
bool Disjoint = EatIfPresent(lltok::kw_disjoint);
if (parseLogical(Inst, PFS, KeywordVal))
return true;
if (Disjoint)
cast<PossiblyDisjointInst>(Inst)->setIsDisjoint(true);
return false;
}
case lltok::kw_and:
case lltok::kw_or:
case lltok::kw_xor:
return parseLogical(Inst, PFS, KeywordVal);
case lltok::kw_icmp:
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4866,12 +4866,14 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
Opc == Instruction::AShr) {
if (Record[OpNum] & (1 << bitc::PEO_EXACT))
cast<BinaryOperator>(I)->setIsExact(true);
} else if (Opc == Instruction::Or) {
if (Record[OpNum] & (1 << bitc::PDI_DISJOINT))
cast<PossiblyDisjointInst>(I)->setIsDisjoint(true);
} else if (isa<FPMathOperator>(I)) {
FastMathFlags FMF = getDecodedFastMathFlags(Record[OpNum]);
if (FMF.any())
I->setFastMathFlags(FMF);
}

}
break;
}
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,9 @@ static uint64_t getOptimizationFlags(const Value *V) {
} else if (const auto *PEO = dyn_cast<PossiblyExactOperator>(V)) {
if (PEO->isExact())
Flags |= 1 << bitc::PEO_EXACT;
} else if (const auto *PDI = dyn_cast<PossiblyDisjointInst>(V)) {
if (PDI->isDisjoint())
Flags |= 1 << bitc::PDI_DISJOINT;
} else if (const auto *FPMO = dyn_cast<FPMathOperator>(V)) {
if (FPMO->hasAllowReassoc())
Flags |= bitc::AllowReassoc;
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,10 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) {
dyn_cast<PossiblyExactOperator>(U)) {
if (Div->isExact())
Out << " exact";
} else if (const PossiblyDisjointInst *PDI =
dyn_cast<PossiblyDisjointInst>(U)) {
if (PDI->isDisjoint())
Out << " disjoint";
} else if (const GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
if (GEP->isInBounds())
Out << " inbounds";
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/IR/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ void Instruction::dropPoisonGeneratingFlags() {
cast<PossiblyExactOperator>(this)->setIsExact(false);
break;

case Instruction::Or:
cast<PossiblyDisjointInst>(this)->setIsDisjoint(false);
break;

case Instruction::GetElementPtr:
cast<GetElementPtrInst>(this)->setIsInBounds(false);
break;
Expand Down Expand Up @@ -532,6 +536,10 @@ void Instruction::copyIRFlags(const Value *V, bool IncludeWrapFlags) {
if (isa<PossiblyExactOperator>(this))
setIsExact(PE->isExact());

if (auto *SrcPD = dyn_cast<PossiblyDisjointInst>(V))
if (auto *DestPD = dyn_cast<PossiblyDisjointInst>(this))
DestPD->setIsDisjoint(SrcPD->isDisjoint());

// Copy the fast-math flags.
if (auto *FP = dyn_cast<FPMathOperator>(V))
if (isa<FPMathOperator>(this))
Expand All @@ -558,6 +566,10 @@ void Instruction::andIRFlags(const Value *V) {
if (isa<PossiblyExactOperator>(this))
setIsExact(isExact() && PE->isExact());

if (auto *SrcPD = dyn_cast<PossiblyDisjointInst>(V))
if (auto *DestPD = dyn_cast<PossiblyDisjointInst>(this))
DestPD->setIsDisjoint(DestPD->isDisjoint() && SrcPD->isDisjoint());

if (auto *FP = dyn_cast<FPMathOperator>(V)) {
if (isa<FPMathOperator>(this)) {
FastMathFlags FM = getFastMathFlags();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,11 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Value *V, APInt DemandedMask,
// If either the LHS or the RHS are One, the result is One.
if (SimplifyDemandedBits(I, 1, DemandedMask, RHSKnown, Depth + 1) ||
SimplifyDemandedBits(I, 0, DemandedMask & ~RHSKnown.One, LHSKnown,
Depth + 1))
Depth + 1)) {
// Disjoint flag may not longer hold.
I->dropPoisonGeneratingFlags();
return I;
}
assert(!RHSKnown.hasConflict() && "Bits known to be one AND zero?");
assert(!LHSKnown.hasConflict() && "Bits known to be one AND zero?");

Expand Down
5 changes: 5 additions & 0 deletions llvm/test/Assembler/flags.ll
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,8 @@ define i64 @test_zext(i32 %a) {
ret i64 %res
}

define i64 @test_or(i64 %a, i64 %b) {
; CHECK: %res = or disjoint i64 %a, %b
%res = or disjoint i64 %a, %b
ret i64 %res
}
4 changes: 4 additions & 0 deletions llvm/test/Bitcode/compatibility.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,10 @@ define void @instructions.bitwise_binops(i8 %op1, i8 %op2) {
xor i8 %op1, %op2
; CHECK: xor i8 %op1, %op2

; disjoint
or disjoint i8 %op1, %op2
; CHECK: or disjoint i8 %op1, %op2

ret void
}

Expand Down
4 changes: 4 additions & 0 deletions llvm/test/Bitcode/flags.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
%jj = or disjoint i32 %a, 0
%oo = or i32 %a, 0
unreachable

first: ; preds = %entry
Expand All @@ -28,5 +30,7 @@ first: ; preds = %entry
%zz = add i32 %a, 0 ; <i32> [#uses=0]
%kk = zext nneg i32 %a to i64
%rr = zext i32 %ss to i64
%mm = or disjoint i32 %a, 0
%nn = or i32 %a, 0
br label %second
}
11 changes: 11 additions & 0 deletions llvm/test/Transforms/InstCombine/freeze.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,17 @@ define i32 @freeze_zext_nneg(i8 %x) {
ret i32 %fr
}

define i32 @propagate_drop_flags_or(i32 %arg) {
; CHECK-LABEL: @propagate_drop_flags_or(
; CHECK-NEXT: [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT: [[V1:%.*]] = or i32 [[ARG_FR]], 2
; CHECK-NEXT: ret i32 [[V1]]
;
%v1 = or disjoint i32 %arg, 2
%v1.fr = freeze i32 %v1
ret i32 %v1.fr
}

!0 = !{}
!1 = !{i64 4}
!2 = !{i32 0, i32 100}
Expand Down
11 changes: 11 additions & 0 deletions llvm/test/Transforms/InstCombine/or.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1576,3 +1576,14 @@ define <4 x i1> @and_or_not_or_logical_vec(<4 x i32> %ap, <4 x i32> %bp) {
%Z = or <4 x i1> %X, %Y
ret <4 x i1> %Z
}

; Make sure SimplifyDemandedBits drops the disjoint flag.
define i8 @drop_disjoint(i8 %x) {
; CHECK-LABEL: @drop_disjoint(
; CHECK-NEXT: [[B:%.*]] = or i8 [[X:%.*]], 1
; CHECK-NEXT: ret i8 [[B]]
;
%a = and i8 %x, -2
%b = or disjoint i8 %a, 1
ret i8 %b
}
30 changes: 30 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/HoistCode.ll
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,33 @@ F:
%z2 = zext i8 %x to i32
ret i32 %z2
}

define i32 @hoist_or_flags_preserve(i1 %C, i32 %x, i32 %y) {
; CHECK-LABEL: @hoist_or_flags_preserve(
; CHECK-NEXT: common.ret:
; CHECK-NEXT: [[Z1:%.*]] = or disjoint i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i32 [[Z1]]
;
br i1 %C, label %T, label %F
T:
%z1 = or disjoint i32 %x, %y
ret i32 %z1
F:
%z2 = or disjoint i32 %x, %y
ret i32 %z2
}

define i32 @hoist_or_flags_drop(i1 %C, i32 %x, i32 %y) {
; CHECK-LABEL: @hoist_or_flags_drop(
; CHECK-NEXT: common.ret:
; CHECK-NEXT: [[Z1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i32 [[Z1]]
;
br i1 %C, label %T, label %F
T:
%z1 = or i32 %x, %y
ret i32 %z1
F:
%z2 = or disjoint i32 %x, %y
ret i32 %z2
}

0 comments on commit d9962c4

Please sign in to comment.