Skip to content

Commit e8b70e9

Browse files
authored
[TableGen] Make !and and !or short-circuit (#113963)
The idea is that by preemptively simplifying the result of `!and` and `!or`, we can fold some of the conditional operators, like `!if` or `!cond`, as early as possible.
1 parent bf30b6c commit e8b70e9

File tree

3 files changed

+35
-2
lines changed

3 files changed

+35
-2
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,7 +1646,8 @@ and non-0 as true.
16461646
``!and(``\ *a*\ ``,`` *b*\ ``, ...)``
16471647
This operator does a bitwise AND on *a*, *b*, etc., and produces the
16481648
result. A logical AND can be performed if all the arguments are either
1649-
0 or 1.
1649+
0 or 1. This operator is short-circuit to 0 when the left-most operand
1650+
is 0.
16501651

16511652
``!cast<``\ *type*\ ``>(``\ *a*\ ``)``
16521653
This operator performs a cast on *a* and produces the result.
@@ -1872,7 +1873,8 @@ and non-0 as true.
18721873
``!or(``\ *a*\ ``,`` *b*\ ``, ...)``
18731874
This operator does a bitwise OR on *a*, *b*, etc., and produces the
18741875
result. A logical OR can be performed if all the arguments are either
1875-
0 or 1.
1876+
0 or 1. This operator is short-circuit to -1 (all ones) the left-most
1877+
operand is -1.
18761878

18771879
``!range([``\ *start*\ ``,]`` *end*\ ``[,``\ *step*\ ``])``
18781880
This operator produces half-open range sequence ``[start : end : step)`` as

llvm/lib/TableGen/Record.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,23 @@ const Init *BinOpInit::resolveReferences(Resolver &R) const {
15431543
const Init *lhs = LHS->resolveReferences(R);
15441544
const Init *rhs = RHS->resolveReferences(R);
15451545

1546+
unsigned Opc = getOpcode();
1547+
if (Opc == AND || Opc == OR) {
1548+
// Short-circuit. Regardless whether this is a logical or bitwise
1549+
// AND/OR.
1550+
// Ideally we could also short-circuit `!or(true, ...)`, but it's
1551+
// difficult to do it right without knowing if rest of the operands
1552+
// are all `bit` or not. Therefore, we're only implementing a relatively
1553+
// limited version of short-circuit against all ones (`true` is casted
1554+
// to 1 rather than all ones before we evaluate `!or`).
1555+
if (const auto *LHSi = dyn_cast_or_null<IntInit>(
1556+
lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
1557+
if ((Opc == AND && !LHSi->getValue()) ||
1558+
(Opc == OR && LHSi->getValue() == -1))
1559+
return LHSi;
1560+
}
1561+
}
1562+
15461563
if (LHS != lhs || RHS != rhs)
15471564
return (BinOpInit::get(getOpcode(), lhs, rhs, getType()))
15481565
->Fold(R.getCurrentRecord());

llvm/test/TableGen/true-false.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ def rec7 {
6767
bits<3> flags = { true, false, true };
6868
}
6969

70+
// `!and` and `!or` should be short-circuit such that `!tail` on empty list will never
71+
// be evaluated.
72+
// CHECK: def rec8
73+
// CHECK: list<int> newSeq = [];
74+
// CHECK: list<int> newSeq2 = [];
75+
76+
class Foo <list<int> seq = []> {
77+
bit unresolved = !ne(!find(NAME, "BAR"), -1);
78+
list<int> newSeq = !if(!and(false, unresolved), !tail(seq), seq);
79+
list<int> newSeq2 = !if(!or(-1, unresolved), seq, !tail(seq));
80+
}
81+
82+
def rec8 : Foo<>;
83+
7084
#ifdef ERROR1
7185
// ERROR1: Record name '1' is not a string
7286

0 commit comments

Comments
 (0)