Skip to content

Commit 97dcbea

Browse files
committed
[LogicCombine 1/?] Implement a general way to simplify logical operations.
This patch involves boolean ring to simplify logical operations. We can treat `&` as ring multiplication and `^` as ring addition. So we need to canonicalize all other operations to `*` `+`. Like: ``` a & b -> a * b a ^ b -> a + b ~a -> a + 1 a | b -> a * b + a + b c ? a : b -> c * a + (c + 1) * b ``` In the code, we use a mask set to represent an expression. Every value that is not comes from logical operations could be a bit in the mask. The mask itself is a multiplication chain. The mask set is an addiction chain. We can calculate two expressions based on boolean algebras. For now, the initial patch only enabled on and/or/xor, Later we can enhance the code step by step. Reference: https://en.wikipedia.org/wiki/Boolean_ring Reviewed By: spatel Differential Revision: https://reviews.llvm.org/D142803
1 parent 28eef3b commit 97dcbea

File tree

6 files changed

+547
-76
lines changed

6 files changed

+547
-76
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===------------------ LogicCombine.h --------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "LogicalExpr.h"
10+
#include "llvm/ADT/DenseMap.h"
11+
#include "llvm/ADT/SetVector.h"
12+
#include "llvm/ADT/Statistic.h"
13+
#include "llvm/IR/InstrTypes.h"
14+
#include "llvm/IR/Instruction.h"
15+
#include "llvm/Support/Allocator.h"
16+
17+
namespace llvm {
18+
19+
class LogicCombiner;
20+
21+
class LogicalOpNode {
22+
private:
23+
LogicCombiner *Helper;
24+
Value *Val;
25+
LogicalExpr Expr;
26+
// TODO: Add weight to measure cost for more than one use value
27+
28+
void printAndChain(raw_ostream &OS, uint64_t LeafBits) const;
29+
30+
public:
31+
LogicalOpNode(LogicCombiner *OpsHelper, Value *SrcVal,
32+
const LogicalExpr &SrcExpr)
33+
: Helper(OpsHelper), Val(SrcVal), Expr(SrcExpr) {}
34+
~LogicalOpNode() {}
35+
36+
Value *getValue() const { return Val; }
37+
const LogicalExpr &getExpr() const { return Expr; }
38+
void print(raw_ostream &OS) const;
39+
};
40+
41+
class LogicCombiner {
42+
public:
43+
LogicCombiner() {}
44+
~LogicCombiner() { clear(); }
45+
46+
Value *simplify(Value *Root);
47+
48+
private:
49+
friend class LogicalOpNode;
50+
51+
SpecificBumpPtrAllocator<LogicalOpNode> Alloc;
52+
SmallDenseMap<Value *, LogicalOpNode *, 16> LogicalOpNodes;
53+
SmallSetVector<Value *, 8> LeafValues;
54+
55+
void clear();
56+
57+
LogicalOpNode *visitLeafNode(Value *Val, unsigned Depth);
58+
LogicalOpNode *visitBinOp(BinaryOperator *BO, unsigned Depth);
59+
LogicalOpNode *getLogicalOpNode(Value *Val, unsigned Depth = 0);
60+
Value *logicalOpToValue(LogicalOpNode *Node);
61+
};
62+
63+
inline raw_ostream &operator<<(raw_ostream &OS, const LogicalOpNode &I) {
64+
I.print(OS);
65+
return OS;
66+
}
67+
68+
} // namespace llvm
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===------------------- LogicalExpr.h --------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
/// This file defines LogicalExpr, a class that represent a logical value by
10+
/// a set of bitsets.
11+
///
12+
/// For a logical expression represented by bitset, the "and" logic
13+
/// operator represented by "&" is translated to "*" and is then evaluated as
14+
/// the "or" of the bitset. For example, pattern "a & b" is represented by the
15+
/// logical expression "01 * 10", and the expression is reduced to "11". So the
16+
/// operation "&" between two logical expressions (not "xor", only "and" chain)
17+
/// is actually bitwise "or" of the masks. There are two exceptions:
18+
/// If one of the operands is constant 0, the entire bitset represents 0.
19+
/// If one of the operands is constant -1, the result is the other one.
20+
///
21+
/// The evaluation of a pattern for bitwise "xor" is represented by a "+" math
22+
/// operator. But it also has one exception to normal math rules: if two masks
23+
/// are identical, we remove them. For example with "a ^ a", the logical
24+
/// expression is "1 + 1". We eliminate them from the logical expression.
25+
///
26+
/// We use commutative, associative, and distributive laws of arithmetic
27+
/// multiplication and addition to reduce the expression. An example for the
28+
/// LogicalExpr caculation:
29+
/// ((a & b) | (a ^ c)) ^ (!(b & c) & a)
30+
/// Mask for the leafs are: a --> 001, b --> 010, c -->100
31+
/// First step is expand the pattern to:
32+
/// (((a & b) & (a ^ c)) ^ (a & b) ^ (a ^ c)) ^ (((b & c) ^ -1) & a)
33+
/// Use logical expression to represent the pattern:
34+
/// 001 * 010 * (001 + 100) + 001 * 010 + 001 + 100 + (010 * 100 + -1C) *
35+
/// 001
36+
/// Expression after distributive laws:
37+
/// 001 * 010 * 001 + 001 * 010 * 100 + 001 * 010 + 001 + 100 + 010 * 100 *
38+
/// 001 + -1C * 001
39+
/// Calculate multiplication:
40+
/// 011 + 111 + 011 + 001 + 100 + 111 + 001
41+
/// Calculate addition:
42+
/// 100
43+
/// Restore to value
44+
/// c
45+
//===----------------------------------------------------------------------===//
46+
47+
#include "llvm/ADT/DenseSet.h"
48+
49+
namespace llvm {
50+
// TODO: can we use APInt define the mask to enlarge the max leaf number?
51+
typedef SmallDenseSet<uint64_t, 8> ExprAddChain;
52+
53+
class LogicalExpr {
54+
private:
55+
ExprAddChain AddChain;
56+
57+
public:
58+
static const uint64_t ExprAllOne = 0x8000000000000000;
59+
60+
LogicalExpr() {}
61+
LogicalExpr(uint64_t BitSet) {
62+
if (BitSet != 0)
63+
AddChain.insert(BitSet);
64+
}
65+
LogicalExpr(const ExprAddChain &SrcAddChain) : AddChain(SrcAddChain) {
66+
}
67+
68+
unsigned size() const { return AddChain.size(); }
69+
ExprAddChain::iterator begin() { return AddChain.begin(); }
70+
ExprAddChain::iterator end() { return AddChain.end(); }
71+
ExprAddChain::const_iterator begin() const { return AddChain.begin(); }
72+
ExprAddChain::const_iterator end() const { return AddChain.end(); }
73+
74+
LogicalExpr &operator*=(const LogicalExpr &RHS) {
75+
ExprAddChain NewChain;
76+
for (auto LHS : AddChain) {
77+
for (auto RHS : RHS.AddChain) {
78+
uint64_t NewBitSet;
79+
// Except the special case one value "*" -1 is just return itself, the
80+
// other "*" operation is actually "|" LHS and RHS 's bitset. For
81+
// example: ab * bd = abd The expression ab * bd convert to bitset will
82+
// be 0b0011 * 0b1010. The result abd convert to bitset will become
83+
// 0b1011.
84+
if (LHS == ExprAllOne)
85+
NewBitSet = RHS;
86+
else if (RHS == ExprAllOne)
87+
NewBitSet = LHS;
88+
else
89+
NewBitSet = LHS | RHS;
90+
assert(NewBitSet == ExprAllOne || (NewBitSet & ExprAllOne) == 0);
91+
// a ^ a -> 0
92+
auto InsertPair = NewChain.insert(NewBitSet);
93+
if (!InsertPair.second)
94+
NewChain.erase(InsertPair.first);
95+
}
96+
}
97+
98+
AddChain = NewChain;
99+
return *this;
100+
}
101+
102+
LogicalExpr &operator+=(const LogicalExpr &RHS) {
103+
for (auto RHS : RHS.AddChain) {
104+
// a ^ a -> 0
105+
auto InsertPair = AddChain.insert(RHS);
106+
if (!InsertPair.second)
107+
AddChain.erase(InsertPair.first);
108+
}
109+
return *this;
110+
}
111+
};
112+
113+
inline LogicalExpr operator*(LogicalExpr a, const LogicalExpr &b) {
114+
a *= b;
115+
return a;
116+
}
117+
118+
inline LogicalExpr operator+(LogicalExpr a, const LogicalExpr &b) {
119+
a += b;
120+
return a;
121+
}
122+
123+
inline LogicalExpr operator&(const LogicalExpr &a, const LogicalExpr &b) {
124+
return a * b;
125+
}
126+
127+
inline LogicalExpr operator^(const LogicalExpr &a, const LogicalExpr &b) {
128+
return a + b;
129+
}
130+
131+
inline LogicalExpr operator|(const LogicalExpr &a, const LogicalExpr &b) {
132+
return a * b + a + b;
133+
}
134+
135+
inline LogicalExpr operator~(const LogicalExpr &a) {
136+
LogicalExpr AllOneExpr(LogicalExpr::ExprAllOne);
137+
return a + AllOneExpr;
138+
}
139+
140+
} // namespace llvm

llvm/lib/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ add_llvm_component_library(LLVMAnalysis
8787
Lint.cpp
8888
Loads.cpp
8989
Local.cpp
90+
LogicCombine.cpp
9091
LoopAccessAnalysis.cpp
9192
LoopAnalysisManager.cpp
9293
LoopCacheAnalysis.cpp

0 commit comments

Comments
 (0)