llvm-project/llvm/lib/Analysis/LogicCombine.cpp
chenglin.bi 97dcbea63e [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
2023-03-02 20:46:16 +08:00

208 lines
6.7 KiB
C++

//===--------------------- LogicCombine.cpp -------------------------------===//
//
// 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 attempts to find the simplest expression for a bitwise logic
/// operation chain. We canonicalize all other ops to "&"/"^".
/// For example:
/// a | b --> (a & b) ^ a ^ b
/// c ? a : b --> (c & a) ^ ((c ^ true) & b)
/// We use a set of bitset to represent the expression. Any value that is not a
/// logic operation is a leaf node. Leaf node is 1 bit in the bitset. For
/// example, we have source a, b, c. The bit for a is 1, b is 2, c is 4.
/// a & b & c --> {0b111}
/// a & b ^ c & a --> {0b011, 0b101}
/// a & b ^ c & a ^ b --> {0b011, 0b101, 0b010}
/// Every bitset is an "&" chain. The set of bitset is a "^" chain.
/// Based on boolean ring, we can treat "&" as ring multiplication and "^" as
/// ring addition. After that, any logic value can be represented as a chain of
/// bitsets. For example:
/// r1 = (a | b) & c -> r1 = (a * b * c) + (a * c) + (b * c) ->
/// {0b111, 0b101, 0b110}
/// Finally we need to rebuild the simplest pattern from the expression.
///
/// Reference: https://en.wikipedia.org/wiki/Boolean_ring
///
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/LogicCombine.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/Constants.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "logic-combine"
STATISTIC(NumLogicalOpsSimplified, "Number of logical operations simplified");
static cl::opt<unsigned> MaxLogicOpLeafsToScan(
"logic-combine-max-leafs", cl::init(8), cl::Hidden,
cl::desc("Max leafs of logic ops to scan for logical combine."));
static cl::opt<unsigned> MaxDepthLogicOpsToScan(
"logic-combine-max-depth", cl::init(8), cl::Hidden,
cl::desc("Max depth of logic ops to scan for logical combine."));
void LogicalOpNode::printAndChain(raw_ostream &OS, uint64_t LeafBits) const {
if (LeafBits == LogicalExpr::ExprAllOne) {
OS << "-1";
return;
}
if (LeafBits == 0)
return;
unsigned LeafCnt = popcount(LeafBits);
if (LeafCnt == 1) {
Helper->LeafValues[Log2_64(LeafBits)]->printAsOperand(OS, false);
return;
}
unsigned LeafIdx;
ListSeparator LS(" * ");
for (unsigned I = 0; I < LeafCnt; I++) {
LeafIdx = countr_zero(LeafBits);
OS << LS;
Helper->LeafValues[LeafIdx]->printAsOperand(OS, false);
LeafBits -= (1ULL << LeafIdx);
}
}
void LogicalOpNode::print(raw_ostream &OS) const {
Val->printAsOperand(OS, false);
OS << " --> ";
if (Expr.size() == 0) {
OS << "0\n";
return;
}
ListSeparator LS(" + ");
for (auto I = Expr.begin(); I != Expr.end(); I++) {
OS << LS;
printAndChain(OS, *I);
}
OS << "\n";
}
void LogicCombiner::clear() {
LogicalOpNodes.clear();
LeafValues.clear();
}
LogicalOpNode *LogicCombiner::visitLeafNode(Value *Val, unsigned Depth) {
// Depth is 0 means the root is not logical operation. We can't
// do anything for that.
if (Depth == 0 || LeafValues.size() >= MaxLogicOpLeafsToScan)
return nullptr;
uint64_t ExprVal = 1ULL << LeafValues.size();
// Constant Zero,AllOne are special leaf nodes. They involve
// LogicalExpr's calculation so we must detect them at first.
if (auto ConstVal = dyn_cast<ConstantInt>(Val)) {
if (ConstVal->isZero())
ExprVal = 0;
else if (ConstVal->isAllOnesValue())
ExprVal = LogicalExpr::ExprAllOne;
}
if (ExprVal != LogicalExpr::ExprAllOne && ExprVal != 0)
LeafValues.insert(Val);
LogicalOpNode *Node =
new (Alloc.Allocate()) LogicalOpNode(this, Val, LogicalExpr(ExprVal));
LogicalOpNodes[Val] = Node;
return Node;
}
LogicalOpNode *LogicCombiner::visitBinOp(BinaryOperator *BO, unsigned Depth) {
if (!BO->isBitwiseLogicOp())
return visitLeafNode(BO, Depth);
LogicalOpNode *LHS = getLogicalOpNode(BO->getOperand(0), Depth + 1);
if (LHS == nullptr)
return nullptr;
LogicalOpNode *RHS = getLogicalOpNode(BO->getOperand(1), Depth + 1);
if (RHS == nullptr)
return nullptr;
LogicalOpNode *Node;
if (BO->getOpcode() == Instruction::And)
Node = new (Alloc.Allocate())
LogicalOpNode(this, BO, LHS->getExpr() & RHS->getExpr());
else if (BO->getOpcode() == Instruction::Or)
Node = new (Alloc.Allocate())
LogicalOpNode(this, BO, LHS->getExpr() | RHS->getExpr());
else
Node = new (Alloc.Allocate())
LogicalOpNode(this, BO, LHS->getExpr() ^ RHS->getExpr());
LogicalOpNodes[BO] = Node;
return Node;
}
LogicalOpNode *LogicCombiner::getLogicalOpNode(Value *Val, unsigned Depth) {
if (Depth == MaxDepthLogicOpsToScan)
return nullptr;
if (LogicalOpNodes.find(Val) == LogicalOpNodes.end()) {
LogicalOpNode *Node;
// TODO: add select instruction support
if (auto *BO = dyn_cast<BinaryOperator>(Val))
Node = visitBinOp(BO, Depth);
else
Node = visitLeafNode(Val, Depth);
if (!Node)
return nullptr;
LLVM_DEBUG(dbgs() << *Node);
}
return LogicalOpNodes[Val];
}
Value *LogicCombiner::logicalOpToValue(LogicalOpNode *Node) {
const LogicalExpr &Expr = Node->getExpr();
// Empty when all leaf bits are erased from the set because a ^ a = 0.
if (Expr.size() == 0)
return Constant::getNullValue(Node->getValue()->getType());
if (Expr.size() == 1) {
uint64_t LeafBits = *Expr.begin();
if (LeafBits == 0)
return Constant::getNullValue(Node->getValue()->getType());
// ExprAllOne is not in the LeafValues
if (LeafBits == LogicalExpr::ExprAllOne)
return Constant::getAllOnesValue(Node->getValue()->getType());
if (popcount(LeafBits) == 1)
return LeafValues[Log2_64(LeafBits)];
}
// TODO: find the simplest form from logical expression when it is not
// only an "and" chain.
return nullptr;
}
Value *LogicCombiner::simplify(Value *Root) {
assert(MaxLogicOpLeafsToScan <= 63 &&
"Logical leaf node can't be larger than 63.");
LogicalOpNode *RootNode = getLogicalOpNode(Root);
if (RootNode == nullptr)
return nullptr;
Value *NewRoot = logicalOpToValue(RootNode);
if (NewRoot == nullptr || NewRoot == Root)
return nullptr;
LogicalOpNodes.erase(Root);
NumLogicalOpsSimplified++;
return NewRoot;
}