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
--- /dev/null
+//===------------------ LogicCombine.h --------------------------*- C++ -*-===//\r
+//\r
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\r
+// See https://llvm.org/LICENSE.txt for license information.\r
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\r
+//\r
+//===----------------------------------------------------------------------===//\r
+\r
+#include "LogicalExpr.h"\r
+#include "llvm/ADT/DenseMap.h"\r
+#include "llvm/ADT/SetVector.h"\r
+#include "llvm/ADT/Statistic.h"\r
+#include "llvm/IR/InstrTypes.h"\r
+#include "llvm/IR/Instruction.h"\r
+#include "llvm/Support/Allocator.h"\r
+\r
+namespace llvm {\r
+\r
+class LogicCombiner;\r
+\r
+class LogicalOpNode {\r
+private:\r
+ LogicCombiner *Helper;\r
+ Value *Val;\r
+ LogicalExpr Expr;\r
+ // TODO: Add weight to measure cost for more than one use value\r
+\r
+ void printAndChain(raw_ostream &OS, uint64_t LeafBits) const;\r
+\r
+public:\r
+ LogicalOpNode(LogicCombiner *OpsHelper, Value *SrcVal,\r
+ const LogicalExpr &SrcExpr)\r
+ : Helper(OpsHelper), Val(SrcVal), Expr(SrcExpr) {}\r
+ ~LogicalOpNode() {}\r
+\r
+ Value *getValue() const { return Val; }\r
+ const LogicalExpr &getExpr() const { return Expr; }\r
+ void print(raw_ostream &OS) const;\r
+};\r
+\r
+class LogicCombiner {\r
+public:\r
+ LogicCombiner() {}\r
+ ~LogicCombiner() { clear(); }\r
+\r
+ Value *simplify(Value *Root);\r
+\r
+private:\r
+ friend class LogicalOpNode;\r
+\r
+ SpecificBumpPtrAllocator<LogicalOpNode> Alloc;\r
+ SmallDenseMap<Value *, LogicalOpNode *, 16> LogicalOpNodes;\r
+ SmallSetVector<Value *, 8> LeafValues;\r
+\r
+ void clear();\r
+\r
+ LogicalOpNode *visitLeafNode(Value *Val, unsigned Depth);\r
+ LogicalOpNode *visitBinOp(BinaryOperator *BO, unsigned Depth);\r
+ LogicalOpNode *getLogicalOpNode(Value *Val, unsigned Depth = 0);\r
+ Value *logicalOpToValue(LogicalOpNode *Node);\r
+};\r
+\r
+inline raw_ostream &operator<<(raw_ostream &OS, const LogicalOpNode &I) {\r
+ I.print(OS);\r
+ return OS;\r
+}\r
+\r
+} // namespace llvm\r
--- /dev/null
+//===------------------- LogicalExpr.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file defines LogicalExpr, a class that represent a logical value by
+/// a set of bitsets.
+///
+/// For a logical expression represented by bitset, the "and" logic
+/// operator represented by "&" is translated to "*" and is then evaluated as
+/// the "or" of the bitset. For example, pattern "a & b" is represented by the
+/// logical expression "01 * 10", and the expression is reduced to "11". So the
+/// operation "&" between two logical expressions (not "xor", only "and" chain)
+/// is actually bitwise "or" of the masks. There are two exceptions:
+/// If one of the operands is constant 0, the entire bitset represents 0.
+/// If one of the operands is constant -1, the result is the other one.
+///
+/// The evaluation of a pattern for bitwise "xor" is represented by a "+" math
+/// operator. But it also has one exception to normal math rules: if two masks
+/// are identical, we remove them. For example with "a ^ a", the logical
+/// expression is "1 + 1". We eliminate them from the logical expression.
+///
+/// We use commutative, associative, and distributive laws of arithmetic
+/// multiplication and addition to reduce the expression. An example for the
+/// LogicalExpr caculation:
+/// ((a & b) | (a ^ c)) ^ (!(b & c) & a)
+/// Mask for the leafs are: a --> 001, b --> 010, c -->100
+/// First step is expand the pattern to:
+/// (((a & b) & (a ^ c)) ^ (a & b) ^ (a ^ c)) ^ (((b & c) ^ -1) & a)
+/// Use logical expression to represent the pattern:
+/// 001 * 010 * (001 + 100) + 001 * 010 + 001 + 100 + (010 * 100 + -1C) *
+/// 001
+/// Expression after distributive laws:
+/// 001 * 010 * 001 + 001 * 010 * 100 + 001 * 010 + 001 + 100 + 010 * 100 *
+/// 001 + -1C * 001
+/// Calculate multiplication:
+/// 011 + 111 + 011 + 001 + 100 + 111 + 001
+/// Calculate addition:
+/// 100
+/// Restore to value
+/// c
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DenseSet.h"
+
+namespace llvm {
+// TODO: can we use APInt define the mask to enlarge the max leaf number?
+typedef SmallDenseSet<uint64_t, 8> ExprAddChain;
+
+class LogicalExpr {
+private:
+ ExprAddChain AddChain;
+
+public:
+ static const uint64_t ExprAllOne = 0x8000000000000000;
+
+ LogicalExpr() {}
+ LogicalExpr(uint64_t BitSet) {
+ if (BitSet != 0)
+ AddChain.insert(BitSet);
+ }
+ LogicalExpr(const ExprAddChain &SrcAddChain) : AddChain(SrcAddChain) {
+ }
+
+ unsigned size() const { return AddChain.size(); }
+ ExprAddChain::iterator begin() { return AddChain.begin(); }
+ ExprAddChain::iterator end() { return AddChain.end(); }
+ ExprAddChain::const_iterator begin() const { return AddChain.begin(); }
+ ExprAddChain::const_iterator end() const { return AddChain.end(); }
+
+ LogicalExpr &operator*=(const LogicalExpr &RHS) {
+ ExprAddChain NewChain;
+ for (auto LHS : AddChain) {
+ for (auto RHS : RHS.AddChain) {
+ uint64_t NewBitSet;
+ // Except the special case one value "*" -1 is just return itself, the
+ // other "*" operation is actually "|" LHS and RHS 's bitset. For
+ // example: ab * bd = abd The expression ab * bd convert to bitset will
+ // be 0b0011 * 0b1010. The result abd convert to bitset will become
+ // 0b1011.
+ if (LHS == ExprAllOne)
+ NewBitSet = RHS;
+ else if (RHS == ExprAllOne)
+ NewBitSet = LHS;
+ else
+ NewBitSet = LHS | RHS;
+ assert(NewBitSet == ExprAllOne || (NewBitSet & ExprAllOne) == 0);
+ // a ^ a -> 0
+ auto InsertPair = NewChain.insert(NewBitSet);
+ if (!InsertPair.second)
+ NewChain.erase(InsertPair.first);
+ }
+ }
+
+ AddChain = NewChain;
+ return *this;
+ }
+
+ LogicalExpr &operator+=(const LogicalExpr &RHS) {
+ for (auto RHS : RHS.AddChain) {
+ // a ^ a -> 0
+ auto InsertPair = AddChain.insert(RHS);
+ if (!InsertPair.second)
+ AddChain.erase(InsertPair.first);
+ }
+ return *this;
+ }
+};
+
+inline LogicalExpr operator*(LogicalExpr a, const LogicalExpr &b) {
+ a *= b;
+ return a;
+}
+
+inline LogicalExpr operator+(LogicalExpr a, const LogicalExpr &b) {
+ a += b;
+ return a;
+}
+
+inline LogicalExpr operator&(const LogicalExpr &a, const LogicalExpr &b) {
+ return a * b;
+}
+
+inline LogicalExpr operator^(const LogicalExpr &a, const LogicalExpr &b) {
+ return a + b;
+}
+
+inline LogicalExpr operator|(const LogicalExpr &a, const LogicalExpr &b) {
+ return a * b + a + b;
+}
+
+inline LogicalExpr operator~(const LogicalExpr &a) {
+ LogicalExpr AllOneExpr(LogicalExpr::ExprAllOne);
+ return a + AllOneExpr;
+}
+
+} // namespace llvm
Lint.cpp
Loads.cpp
Local.cpp
+ LogicCombine.cpp
LoopAccessAnalysis.cpp
LoopAnalysisManager.cpp
LoopCacheAnalysis.cpp
--- /dev/null
+//===--------------------- LogicCombine.cpp -------------------------------===//\r
+//\r
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\r
+// See https://llvm.org/LICENSE.txt for license information.\r
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\r
+//\r
+//===----------------------------------------------------------------------===//\r
+/// \file\r
+/// This file attempts to find the simplest expression for a bitwise logic\r
+/// operation chain. We canonicalize all other ops to "&"/"^".\r
+/// For example:\r
+/// a | b --> (a & b) ^ a ^ b\r
+/// c ? a : b --> (c & a) ^ ((c ^ true) & b)\r
+/// We use a set of bitset to represent the expression. Any value that is not a\r
+/// logic operation is a leaf node. Leaf node is 1 bit in the bitset. For\r
+/// example, we have source a, b, c. The bit for a is 1, b is 2, c is 4.\r
+/// a & b & c --> {0b111}\r
+/// a & b ^ c & a --> {0b011, 0b101}\r
+/// a & b ^ c & a ^ b --> {0b011, 0b101, 0b010}\r
+/// Every bitset is an "&" chain. The set of bitset is a "^" chain.\r
+/// Based on boolean ring, we can treat "&" as ring multiplication and "^" as\r
+/// ring addition. After that, any logic value can be represented as a chain of\r
+/// bitsets. For example:\r
+/// r1 = (a | b) & c -> r1 = (a * b * c) + (a * c) + (b * c) ->\r
+/// {0b111, 0b101, 0b110}\r
+/// Finally we need to rebuild the simplest pattern from the expression.\r
+///\r
+/// Reference: https://en.wikipedia.org/wiki/Boolean_ring\r
+///\r
+//===----------------------------------------------------------------------===//\r
+\r
+#include "llvm/Analysis/LogicCombine.h"\r
+#include "llvm/ADT/Statistic.h"\r
+#include "llvm/ADT/StringExtras.h"\r
+#include "llvm/IR/Constants.h"\r
+#include "llvm/Support/CommandLine.h"\r
+#include "llvm/Support/Debug.h"\r
+\r
+using namespace llvm;\r
+\r
+#define DEBUG_TYPE "logic-combine"\r
+\r
+STATISTIC(NumLogicalOpsSimplified, "Number of logical operations simplified");\r
+\r
+static cl::opt<unsigned> MaxLogicOpLeafsToScan(\r
+ "logic-combine-max-leafs", cl::init(8), cl::Hidden,\r
+ cl::desc("Max leafs of logic ops to scan for logical combine."));\r
+\r
+static cl::opt<unsigned> MaxDepthLogicOpsToScan(\r
+ "logic-combine-max-depth", cl::init(8), cl::Hidden,\r
+ cl::desc("Max depth of logic ops to scan for logical combine."));\r
+\r
+void LogicalOpNode::printAndChain(raw_ostream &OS, uint64_t LeafBits) const {\r
+ if (LeafBits == LogicalExpr::ExprAllOne) {\r
+ OS << "-1";\r
+ return;\r
+ }\r
+\r
+ if (LeafBits == 0)\r
+ return;\r
+\r
+ unsigned LeafCnt = popcount(LeafBits);\r
+ if (LeafCnt == 1) {\r
+ Helper->LeafValues[Log2_64(LeafBits)]->printAsOperand(OS, false);\r
+ return;\r
+ }\r
+\r
+ unsigned LeafIdx;\r
+ ListSeparator LS(" * ");\r
+ for (unsigned I = 0; I < LeafCnt; I++) {\r
+ LeafIdx = countr_zero(LeafBits);\r
+ OS << LS;\r
+ Helper->LeafValues[LeafIdx]->printAsOperand(OS, false);\r
+ LeafBits -= (1ULL << LeafIdx);\r
+ }\r
+}\r
+\r
+void LogicalOpNode::print(raw_ostream &OS) const {\r
+ Val->printAsOperand(OS, false);\r
+ OS << " --> ";\r
+ if (Expr.size() == 0) {\r
+ OS << "0\n";\r
+ return;\r
+ }\r
+\r
+ ListSeparator LS(" + ");\r
+ for (auto I = Expr.begin(); I != Expr.end(); I++) {\r
+ OS << LS;\r
+ printAndChain(OS, *I);\r
+ }\r
+\r
+ OS << "\n";\r
+}\r
+\r
+void LogicCombiner::clear() {\r
+ LogicalOpNodes.clear();\r
+ LeafValues.clear();\r
+}\r
+\r
+LogicalOpNode *LogicCombiner::visitLeafNode(Value *Val, unsigned Depth) {\r
+ // Depth is 0 means the root is not logical operation. We can't\r
+ // do anything for that.\r
+ if (Depth == 0 || LeafValues.size() >= MaxLogicOpLeafsToScan)\r
+ return nullptr;\r
+\r
+ uint64_t ExprVal = 1ULL << LeafValues.size();\r
+ // Constant Zero,AllOne are special leaf nodes. They involve\r
+ // LogicalExpr's calculation so we must detect them at first.\r
+ if (auto ConstVal = dyn_cast<ConstantInt>(Val)) {\r
+ if (ConstVal->isZero())\r
+ ExprVal = 0;\r
+ else if (ConstVal->isAllOnesValue())\r
+ ExprVal = LogicalExpr::ExprAllOne;\r
+ }\r
+ if (ExprVal != LogicalExpr::ExprAllOne && ExprVal != 0)\r
+ LeafValues.insert(Val);\r
+ LogicalOpNode *Node =\r
+ new (Alloc.Allocate()) LogicalOpNode(this, Val, LogicalExpr(ExprVal));\r
+ LogicalOpNodes[Val] = Node;\r
+ return Node;\r
+}\r
+\r
+LogicalOpNode *LogicCombiner::visitBinOp(BinaryOperator *BO, unsigned Depth) {\r
+ if (!BO->isBitwiseLogicOp())\r
+ return visitLeafNode(BO, Depth);\r
+\r
+ LogicalOpNode *LHS = getLogicalOpNode(BO->getOperand(0), Depth + 1);\r
+ if (LHS == nullptr)\r
+ return nullptr;\r
+\r
+ LogicalOpNode *RHS = getLogicalOpNode(BO->getOperand(1), Depth + 1);\r
+ if (RHS == nullptr)\r
+ return nullptr;\r
+\r
+ LogicalOpNode *Node;\r
+ if (BO->getOpcode() == Instruction::And)\r
+ Node = new (Alloc.Allocate())\r
+ LogicalOpNode(this, BO, LHS->getExpr() & RHS->getExpr());\r
+ else if (BO->getOpcode() == Instruction::Or)\r
+ Node = new (Alloc.Allocate())\r
+ LogicalOpNode(this, BO, LHS->getExpr() | RHS->getExpr());\r
+ else\r
+ Node = new (Alloc.Allocate())\r
+ LogicalOpNode(this, BO, LHS->getExpr() ^ RHS->getExpr());\r
+ LogicalOpNodes[BO] = Node;\r
+ return Node;\r
+}\r
+\r
+LogicalOpNode *LogicCombiner::getLogicalOpNode(Value *Val, unsigned Depth) {\r
+ if (Depth == MaxDepthLogicOpsToScan)\r
+ return nullptr;\r
+\r
+ if (LogicalOpNodes.find(Val) == LogicalOpNodes.end()) {\r
+ LogicalOpNode *Node;\r
+\r
+ // TODO: add select instruction support\r
+ if (auto *BO = dyn_cast<BinaryOperator>(Val))\r
+ Node = visitBinOp(BO, Depth);\r
+ else\r
+ Node = visitLeafNode(Val, Depth);\r
+\r
+ if (!Node)\r
+ return nullptr;\r
+ LLVM_DEBUG(dbgs() << *Node);\r
+ }\r
+ return LogicalOpNodes[Val];\r
+}\r
+\r
+Value *LogicCombiner::logicalOpToValue(LogicalOpNode *Node) {\r
+ const LogicalExpr &Expr = Node->getExpr();\r
+ // Empty when all leaf bits are erased from the set because a ^ a = 0.\r
+ if (Expr.size() == 0)\r
+ return Constant::getNullValue(Node->getValue()->getType());\r
+\r
+ if (Expr.size() == 1) {\r
+ uint64_t LeafBits = *Expr.begin();\r
+ if (LeafBits == 0)\r
+ return Constant::getNullValue(Node->getValue()->getType());\r
+ // ExprAllOne is not in the LeafValues\r
+ if (LeafBits == LogicalExpr::ExprAllOne)\r
+ return Constant::getAllOnesValue(Node->getValue()->getType());\r
+\r
+ if (popcount(LeafBits) == 1)\r
+ return LeafValues[Log2_64(LeafBits)];\r
+ }\r
+\r
+ // TODO: find the simplest form from logical expression when it is not\r
+ // only an "and" chain.\r
+\r
+ return nullptr;\r
+}\r
+\r
+Value *LogicCombiner::simplify(Value *Root) {\r
+ assert(MaxLogicOpLeafsToScan <= 63 &&\r
+ "Logical leaf node can't be larger than 63.");\r
+ LogicalOpNode *RootNode = getLogicalOpNode(Root);\r
+ if (RootNode == nullptr)\r
+ return nullptr;\r
+\r
+ Value *NewRoot = logicalOpToValue(RootNode);\r
+ if (NewRoot == nullptr || NewRoot == Root)\r
+ return nullptr;\r
+\r
+ LogicalOpNodes.erase(Root);\r
+ NumLogicalOpsSimplified++;\r
+ return NewRoot;\r
+}\r
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BasicAliasAnalysis.h"
#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/LogicCombine.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
return true;
}
+/// Reduce bitwise logic sequences.
+static bool foldBitwiseLogic(Function &F, DominatorTree &DT) {
+ bool MadeChange = false;
+ for (BasicBlock &BB : F) {
+ // Ignore unreachable basic blocks.
+ if (!DT.isReachableFromEntry(&BB))
+ continue;
+
+ // TODO: Combining at the function-level would allow more caching of nodes
+ // which saves on compile-time, but it may hit the max value limits before
+ // finding a solution. We could split the combiner based on types to make
+ // the code more efficient, adjust the value of max depth/values, or use
+ // APInt to support tracking more than 63 leaf values.
+ LogicCombiner LC;
+ for (Instruction &I : BB) {
+ if (I.isBitwiseLogicOp()) {
+ Value *NewV = LC.simplify(&I);
+ if (NewV) {
+ MadeChange = true;
+ I.replaceAllUsesWith(NewV);
+ }
+ }
+ }
+ }
+ return MadeChange;
+}
+
/// This is the entry point for folds that could be implemented in regular
/// InstCombine, but they are separated because they are not expected to
/// occur frequently and/or have more than a constant-length pattern match.
static bool foldUnusualPatterns(Function &F, DominatorTree &DT,
TargetTransformInfo &TTI,
TargetLibraryInfo &TLI, AliasAnalysis &AA) {
- bool MadeChange = false;
+ bool MadeChange = foldBitwiseLogic(F, DT);
for (BasicBlock &BB : F) {
// Ignore unreachable basic blocks.
if (!DT.isReachableFromEntry(&BB))
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=aggressive-instcombine -S | FileCheck %s
+; RUN: opt < %s -passes=aggressive-instcombine -logic-combine-max-depth=6 -S | FileCheck %s
define i8 @leaf1_and_aa(i8 %a) {
; CHECK-LABEL: @leaf1_and_aa(
-; CHECK-NEXT: [[AND_AA:%.*]] = and i8 [[A:%.*]], [[A]]
-; CHECK-NEXT: ret i8 [[AND_AA]]
+; CHECK-NEXT: ret i8 [[A:%.*]]
;
%and.aa = and i8 %a, %a
ret i8 %and.aa
define i8 @leaf1_and_a_false(i8 %a) {
; CHECK-LABEL: @leaf1_and_a_false(
-; CHECK-NEXT: [[AND_AA:%.*]] = and i8 [[A:%.*]], 0
-; CHECK-NEXT: ret i8 [[AND_AA]]
+; CHECK-NEXT: ret i8 0
;
%and.aa = and i8 %a, 0
ret i8 %and.aa
define i8 @leaf1_xor_aa(i8 %a) {
; CHECK-LABEL: @leaf1_xor_aa(
-; CHECK-NEXT: [[XOR_AA:%.*]] = xor i8 [[A:%.*]], [[A]]
-; CHECK-NEXT: ret i8 [[XOR_AA]]
+; CHECK-NEXT: ret i8 0
;
%xor.aa = xor i8 %a, %a
ret i8 %xor.aa
define i8 @leaf1_and_not(i8 %a) {
; CHECK-LABEL: @leaf1_and_not(
-; CHECK-NEXT: [[NOT_A:%.*]] = xor i8 [[A:%.*]], -1
-; CHECK-NEXT: [[AND:%.*]] = and i8 [[A]], [[NOT_A]]
-; CHECK-NEXT: ret i8 [[AND]]
+; CHECK-NEXT: ret i8 0
;
%not.a = xor i8 %a, -1
%and = and i8 %a, %not.a
define i8 @leaf1_or_not(i8 %a) {
; CHECK-LABEL: @leaf1_or_not(
-; CHECK-NEXT: [[NOT_A:%.*]] = xor i8 [[A:%.*]], -1
-; CHECK-NEXT: [[OR:%.*]] = or i8 [[A]], [[NOT_A]]
-; CHECK-NEXT: ret i8 [[OR]]
+; CHECK-NEXT: ret i8 -1
;
%not.a = xor i8 %a, -1
%or = or i8 %a, %not.a
define i8 @leaf2_xor(i8 %a, i8 %b) {
; CHECK-LABEL: @leaf2_xor(
-; CHECK-NEXT: [[AB:%.*]] = xor i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[XOR_AB_A:%.*]] = xor i8 [[AB]], [[A]]
-; CHECK-NEXT: ret i8 [[XOR_AB_A]]
+; CHECK-NEXT: ret i8 [[B:%.*]]
;
%ab = xor i8 %a, %b
%xor.ab.a = xor i8 %ab, %a
define i8 @leaf2_xor_ret_const_false(i8 %a, i8 %b) {
; CHECK-LABEL: @leaf2_xor_ret_const_false(
-; CHECK-NEXT: [[XOR_AB:%.*]] = xor i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[XOR_AB_A:%.*]] = xor i8 [[XOR_AB]], [[A]]
-; CHECK-NEXT: [[XOR_AB_A_B:%.*]] = xor i8 [[XOR_AB_A]], [[B]]
-; CHECK-NEXT: ret i8 [[XOR_AB_A_B]]
+; CHECK-NEXT: ret i8 0
;
%xor.ab = xor i8 %a, %b
%xor.ab.a = xor i8 %xor.ab, %a
define i8 @leaf2_or_ret_leaf(i8 %a, i8 %b) {
; CHECK-LABEL: @leaf2_or_ret_leaf(
-; CHECK-NEXT: [[OR_AB:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[AND_AB:%.*]] = and i8 [[A]], [[B]]
-; CHECK-NEXT: [[XOR1:%.*]] = xor i8 [[OR_AB]], [[AND_AB]]
-; CHECK-NEXT: [[XOR2:%.*]] = xor i8 [[XOR1]], [[A]]
-; CHECK-NEXT: ret i8 [[XOR2]]
+; CHECK-NEXT: ret i8 [[B:%.*]]
;
%or.ab = or i8 %a, %b
%and.ab = and i8 %a, %b
define i8 @leaf2_or_ret_const_false(i8 %a, i8 %b) {
; CHECK-LABEL: @leaf2_or_ret_const_false(
-; CHECK-NEXT: [[OR_AB:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[AND_AB:%.*]] = and i8 [[A]], [[B]]
-; CHECK-NEXT: [[XOR1:%.*]] = xor i8 [[OR_AB]], [[AND_AB]]
-; CHECK-NEXT: [[XOR2:%.*]] = xor i8 [[XOR1]], [[A]]
-; CHECK-NEXT: [[XOR3:%.*]] = xor i8 [[XOR1]], [[B]]
-; CHECK-NEXT: ret i8 [[XOR3]]
+; CHECK-NEXT: ret i8 0
;
%or.ab = or i8 %a, %b
%and.ab = and i8 %a, %b
%xor1 = xor i8 %or.ab, %and.ab
%xor2 = xor i8 %xor1, %a
- %xor3 = xor i8 %xor1, %b
+ %xor3 = xor i8 %xor2, %b
ret i8 %xor3
}
define i1 @leaf2_type_is_i1(i1 %a, i1 %b) {
; CHECK-LABEL: @leaf2_type_is_i1(
-; CHECK-NEXT: [[XOR_AB:%.*]] = xor i1 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[NOT_A:%.*]] = xor i1 [[A]], true
-; CHECK-NEXT: [[XOR2:%.*]] = xor i1 [[NOT_A]], [[B]]
-; CHECK-NEXT: [[OR:%.*]] = or i1 [[XOR2]], [[XOR_AB]]
-; CHECK-NEXT: ret i1 [[OR]]
+; CHECK-NEXT: ret i1 true
;
%xor.ab = xor i1 %a, %b
%not.a = xor i1 %a, true
define i8 @leaf3_complex_ret_const_false(i8 %a, i8 %b, i8 %c) {
; CHECK-LABEL: @leaf3_complex_ret_const_false(
-; CHECK-NEXT: [[AB:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[ABC:%.*]] = or i8 [[AB]], [[C:%.*]]
-; CHECK-NEXT: [[NOT_ABC:%.*]] = xor i8 [[ABC]], -1
-; CHECK-NEXT: [[R:%.*]] = and i8 [[NOT_ABC]], [[A]]
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 0
;
%ab = or i8 %a, %b
%abc = or i8 %ab, %c
define i8 @leaf3_complex_ret_leaf(i8 %a, i8 %b, i8 %c) {
; CHECK-LABEL: @leaf3_complex_ret_leaf(
-; CHECK-NEXT: [[AB:%.*]] = and i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[BC:%.*]] = and i8 [[B]], [[C:%.*]]
-; CHECK-NEXT: [[XOR_AC:%.*]] = xor i8 [[A]], [[C]]
-; CHECK-NEXT: [[OR:%.*]] = or i8 [[AB]], [[XOR_AC]]
-; CHECK-NEXT: [[NOT_BC:%.*]] = xor i8 [[BC]], -1
-; CHECK-NEXT: [[AND:%.*]] = and i8 [[NOT_BC]], [[A]]
-; CHECK-NEXT: [[COND:%.*]] = xor i8 [[AND]], [[OR]]
-; CHECK-NEXT: ret i8 [[COND]]
+; CHECK-NEXT: ret i8 [[C:%.*]]
;
%ab = and i8 %a, %b
%bc = and i8 %b, %c
define i8 @leaf4_ret_const_true(i8 %a, i8 %b, i8 %c, i8 %d) {
; CHECK-LABEL: @leaf4_ret_const_true(
-; CHECK-NEXT: [[BD:%.*]] = and i8 [[B:%.*]], [[D:%.*]]
-; CHECK-NEXT: [[NOT_BD:%.*]] = xor i8 [[BD]], -1
-; CHECK-NEXT: [[XOR_AB:%.*]] = xor i8 [[A:%.*]], [[B]]
-; CHECK-NEXT: [[OR1:%.*]] = or i8 [[XOR_AB]], [[C:%.*]]
-; CHECK-NEXT: [[OR2:%.*]] = or i8 [[OR1]], [[NOT_BD]]
-; CHECK-NEXT: [[OR3:%.*]] = or i8 [[OR2]], [[A]]
-; CHECK-NEXT: ret i8 [[OR3]]
+; CHECK-NEXT: ret i8 -1
;
%bd = and i8 %b, %d
%not.bd = xor i8 %bd, -1
define i8 @leaf4_ret_leaf(i8 %a, i8 %b, i8 %c, i8 %d) {
; CHECK-LABEL: @leaf4_ret_leaf(
-; CHECK-NEXT: [[BD:%.*]] = and i8 [[B:%.*]], [[D:%.*]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[BD]], [[C:%.*]]
-; CHECK-NEXT: [[NOT_BD:%.*]] = xor i8 [[XOR]], -1
-; CHECK-NEXT: [[XOR_AB:%.*]] = xor i8 [[A:%.*]], [[B]]
-; CHECK-NEXT: [[OR1:%.*]] = or i8 [[XOR_AB]], [[C]]
-; CHECK-NEXT: [[OR2:%.*]] = or i8 [[OR1]], [[NOT_BD]]
-; CHECK-NEXT: [[OR3:%.*]] = or i8 [[OR2]], [[A]]
-; CHECK-NEXT: [[AND:%.*]] = and i8 [[OR3]], [[B]]
-; CHECK-NEXT: ret i8 [[AND]]
+; CHECK-NEXT: ret i8 [[B:%.*]]
;
%bd = and i8 %b, %d
%xor = xor i8 %bd, %c
define i8 @leaf4_ret_leaf2(i8 %a, i8 %b, i8 %c, i8 %d) {
; CHECK-LABEL: @leaf4_ret_leaf2(
-; CHECK-NEXT: [[BD:%.*]] = and i8 [[B:%.*]], [[D:%.*]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[BD]], [[C:%.*]]
-; CHECK-NEXT: [[NOT_BD:%.*]] = xor i8 [[XOR]], -1
-; CHECK-NEXT: [[XOR_AB:%.*]] = xor i8 [[A:%.*]], [[B]]
-; CHECK-NEXT: [[OR1:%.*]] = or i8 [[XOR_AB]], [[C]]
-; CHECK-NEXT: [[OR2:%.*]] = or i8 [[OR1]], [[NOT_BD]]
-; CHECK-NEXT: [[OR3:%.*]] = or i8 [[OR2]], [[A]]
-; CHECK-NEXT: [[AND:%.*]] = and i8 [[OR3]], [[B]]
-; CHECK-NEXT: ret i8 [[AND]]
+; CHECK-NEXT: ret i8 [[B:%.*]]
;
%bd = and i8 %b, %d
%xor = xor i8 %bd, %c
%and = and i8 %or3, %b
ret i8 %and
}
+
+; Negative test case 1 for max leaf number:
+; This case's max leaf number is 9, if we adjust max depth limitation
+; to larger than 8, it will return %a1
+
+define i8 @leaf8_negative_leafnum(i8 %a1, i8 %a2, i8 %a3, i8 %a4, i8 %a5, i8 %a6, i8 %a7, i8 %a8, i8 %a9) {
+; CHECK-LABEL: @leaf8_negative_leafnum(
+; CHECK-NEXT: [[A12:%.*]] = xor i8 [[A1:%.*]], [[A2:%.*]]
+; CHECK-NEXT: [[A34:%.*]] = xor i8 [[A3:%.*]], [[A4:%.*]]
+; CHECK-NEXT: [[A56:%.*]] = xor i8 [[A5:%.*]], [[A6:%.*]]
+; CHECK-NEXT: [[A78:%.*]] = xor i8 [[A7:%.*]], [[A8:%.*]]
+; CHECK-NEXT: [[A14:%.*]] = xor i8 [[A12]], [[A34]]
+; CHECK-NEXT: [[A58:%.*]] = xor i8 [[A56]], [[A78]]
+; CHECK-NEXT: [[A18:%.*]] = xor i8 [[A14]], [[A58]]
+; CHECK-NEXT: [[A19:%.*]] = xor i8 [[A18]], [[A9:%.*]]
+; CHECK-NEXT: [[A23:%.*]] = xor i8 [[A2]], [[A3]]
+; CHECK-NEXT: [[A45:%.*]] = xor i8 [[A4]], [[A5]]
+; CHECK-NEXT: [[A67:%.*]] = xor i8 [[A6]], [[A7]]
+; CHECK-NEXT: [[A89:%.*]] = xor i8 [[A8]], [[A9]]
+; CHECK-NEXT: [[A25:%.*]] = xor i8 [[A23]], [[A45]]
+; CHECK-NEXT: [[A69:%.*]] = xor i8 [[A67]], [[A89]]
+; CHECK-NEXT: [[A29:%.*]] = xor i8 [[A25]], [[A69]]
+; CHECK-NEXT: [[R:%.*]] = xor i8 [[A19]], [[A29]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %a12 = xor i8 %a1, %a2
+ %a34 = xor i8 %a3, %a4
+ %a56 = xor i8 %a5, %a6
+ %a78 = xor i8 %a7, %a8
+ %a14 = xor i8 %a12, %a34
+ %a58 = xor i8 %a56, %a78
+ %a18 = xor i8 %a14, %a58
+ %a19 = xor i8 %a18, %a9
+ %a23 = xor i8 %a2, %a3
+ %a45 = xor i8 %a4, %a5
+ %a67 = xor i8 %a6, %a7
+ %a89 = xor i8 %a8, %a9
+ %a25 = xor i8 %a23, %a45
+ %a69 = xor i8 %a67, %a89
+ %a29 = xor i8 %a25, %a69
+ %r = xor i8 %a19, %a29
+ ret i8 %r
+}
+
+; Negative test case 2 for max leaf number:
+; Constant value is also a leaf node.
+
+define i8 @leaf8_negative_leafnum_const(i8 %a1, i8 %a2) {
+; CHECK-LABEL: @leaf8_negative_leafnum_const(
+; CHECK-NEXT: [[AND1:%.*]] = and i8 [[A1:%.*]], 1
+; CHECK-NEXT: call void @use8(i8 [[AND1]])
+; CHECK-NEXT: [[AND2:%.*]] = and i8 [[A1]], 2
+; CHECK-NEXT: call void @use8(i8 [[AND2]])
+; CHECK-NEXT: [[AND3:%.*]] = and i8 [[A1]], 3
+; CHECK-NEXT: call void @use8(i8 [[AND3]])
+; CHECK-NEXT: [[AND4:%.*]] = and i8 [[A1]], 4
+; CHECK-NEXT: call void @use8(i8 [[AND4]])
+; CHECK-NEXT: [[AND5:%.*]] = and i8 [[A1]], 5
+; CHECK-NEXT: call void @use8(i8 [[AND5]])
+; CHECK-NEXT: [[AND6:%.*]] = and i8 [[A1]], 6
+; CHECK-NEXT: call void @use8(i8 [[AND6]])
+; CHECK-NEXT: [[AND7:%.*]] = and i8 [[A1]], 7
+; CHECK-NEXT: call void @use8(i8 [[AND7]])
+; CHECK-NEXT: [[R:%.*]] = xor i8 [[A2:%.*]], [[A2]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %and1 = and i8 %a1, 1
+ call void @use8(i8 %and1)
+ %and2 = and i8 %a1, 2
+ call void @use8(i8 %and2)
+ %and3 = and i8 %a1, 3
+ call void @use8(i8 %and3)
+ %and4 = and i8 %a1, 4
+ call void @use8(i8 %and4)
+ %and5 = and i8 %a1, 5
+ call void @use8(i8 %and5)
+ %and6 = and i8 %a1, 6
+ call void @use8(i8 %and6)
+ %and7 = and i8 %a1, 7
+ call void @use8(i8 %and7)
+ %r = xor i8 %a2, %a2
+ ret i8 %r
+}
+
+declare void @use8(i8)