forked from hn2602439437/nudt-compiler-cpp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
4.8 KiB
197 lines
4.8 KiB
#include "ir/PassManager.h"
|
|
|
|
#include "ir/Analysis.h"
|
|
#include "ir/IR.h"
|
|
#include "MemoryUtils.h"
|
|
#include "PassUtils.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace ir {
|
|
namespace {
|
|
|
|
struct ExprKey {
|
|
Opcode opcode = Opcode::Add;
|
|
std::uintptr_t result_type = 0;
|
|
std::uintptr_t aux_type = 0;
|
|
std::vector<std::uintptr_t> operands;
|
|
|
|
bool operator==(const ExprKey& rhs) const {
|
|
return opcode == rhs.opcode && result_type == rhs.result_type &&
|
|
aux_type == rhs.aux_type && operands == rhs.operands;
|
|
}
|
|
};
|
|
|
|
struct ExprKeyHash {
|
|
std::size_t operator()(const ExprKey& key) const {
|
|
std::size_t h = static_cast<std::size_t>(key.opcode);
|
|
h ^= std::hash<std::uintptr_t>{}(key.result_type) + 0x9e3779b9 + (h << 6) +
|
|
(h >> 2);
|
|
h ^= std::hash<std::uintptr_t>{}(key.aux_type) + 0x9e3779b9 + (h << 6) +
|
|
(h >> 2);
|
|
for (auto operand : key.operands) {
|
|
h ^= std::hash<std::uintptr_t>{}(operand) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
|
}
|
|
return h;
|
|
}
|
|
};
|
|
|
|
struct ScopedExpr {
|
|
ExprKey key;
|
|
Value* previous = nullptr;
|
|
bool had_previous = false;
|
|
};
|
|
|
|
bool IsSupportedGVNInstruction(Instruction* inst) {
|
|
if (!inst || inst->IsVoid()) {
|
|
return false;
|
|
}
|
|
switch (inst->GetOpcode()) {
|
|
case Opcode::Add:
|
|
case Opcode::Sub:
|
|
case Opcode::Mul:
|
|
case Opcode::Div:
|
|
case Opcode::Rem:
|
|
case Opcode::FAdd:
|
|
case Opcode::FSub:
|
|
case Opcode::FMul:
|
|
case Opcode::FDiv:
|
|
case Opcode::FRem:
|
|
case Opcode::And:
|
|
case Opcode::Or:
|
|
case Opcode::Xor:
|
|
case Opcode::Shl:
|
|
case Opcode::AShr:
|
|
case Opcode::LShr:
|
|
case Opcode::ICmpEQ:
|
|
case Opcode::ICmpNE:
|
|
case Opcode::ICmpLT:
|
|
case Opcode::ICmpGT:
|
|
case Opcode::ICmpLE:
|
|
case Opcode::ICmpGE:
|
|
case Opcode::FCmpEQ:
|
|
case Opcode::FCmpNE:
|
|
case Opcode::FCmpLT:
|
|
case Opcode::FCmpGT:
|
|
case Opcode::FCmpLE:
|
|
case Opcode::FCmpGE:
|
|
case Opcode::Neg:
|
|
case Opcode::Not:
|
|
case Opcode::FNeg:
|
|
case Opcode::FtoI:
|
|
case Opcode::IToF:
|
|
case Opcode::GetElementPtr:
|
|
case Opcode::Zext:
|
|
return true;
|
|
case Opcode::Call:
|
|
return memutils::IsPureCall(dyncast<CallInst>(inst));
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ExprKey BuildExprKey(Instruction* inst) {
|
|
ExprKey key;
|
|
key.opcode = inst->GetOpcode();
|
|
key.result_type =
|
|
reinterpret_cast<std::uintptr_t>(inst->GetType().get());
|
|
if (auto* gep = dyncast<GetElementPtrInst>(inst)) {
|
|
key.aux_type = reinterpret_cast<std::uintptr_t>(gep->GetSourceType().get());
|
|
}
|
|
key.operands.reserve(inst->GetNumOperands());
|
|
for (std::size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
|
key.operands.push_back(
|
|
reinterpret_cast<std::uintptr_t>(inst->GetOperand(i)));
|
|
}
|
|
if (inst->GetNumOperands() == 2 &&
|
|
passutils::IsCommutativeOpcode(inst->GetOpcode()) &&
|
|
key.operands[1] < key.operands[0]) {
|
|
std::swap(key.operands[0], key.operands[1]);
|
|
}
|
|
return key;
|
|
}
|
|
|
|
bool RunGVNInDomSubtree(
|
|
BasicBlock* block, const DominatorTree& dom_tree,
|
|
std::unordered_map<ExprKey, Value*, ExprKeyHash>& available) {
|
|
if (!block) {
|
|
return false;
|
|
}
|
|
|
|
bool changed = false;
|
|
std::vector<ScopedExpr> scope;
|
|
std::vector<Instruction*> to_remove;
|
|
|
|
for (const auto& inst_ptr : block->GetInstructions()) {
|
|
auto* inst = inst_ptr.get();
|
|
if (!IsSupportedGVNInstruction(inst)) {
|
|
continue;
|
|
}
|
|
|
|
const auto key = BuildExprKey(inst);
|
|
auto it = available.find(key);
|
|
if (it != available.end()) {
|
|
inst->ReplaceAllUsesWith(it->second);
|
|
to_remove.push_back(inst);
|
|
changed = true;
|
|
continue;
|
|
}
|
|
|
|
ScopedExpr scoped{key, nullptr, false};
|
|
auto existing = available.find(key);
|
|
if (existing != available.end()) {
|
|
scoped.previous = existing->second;
|
|
scoped.had_previous = true;
|
|
existing->second = inst;
|
|
} else {
|
|
available.emplace(key, inst);
|
|
}
|
|
scope.push_back(std::move(scoped));
|
|
}
|
|
|
|
for (auto* inst : to_remove) {
|
|
block->EraseInstruction(inst);
|
|
}
|
|
|
|
for (auto* child : dom_tree.GetChildren(block)) {
|
|
changed |= RunGVNInDomSubtree(child, dom_tree, available);
|
|
}
|
|
|
|
for (auto it = scope.rbegin(); it != scope.rend(); ++it) {
|
|
if (it->had_previous) {
|
|
available[it->key] = it->previous;
|
|
} else {
|
|
available.erase(it->key);
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
bool RunGVNOnFunction(Function& function) {
|
|
if (function.IsExternal() || function.GetEntryBlock() == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
DominatorTree dom_tree(function);
|
|
std::unordered_map<ExprKey, Value*, ExprKeyHash> available;
|
|
return RunGVNInDomSubtree(function.GetEntryBlock(), dom_tree, available);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool RunGVN(Module& module) {
|
|
bool changed = false;
|
|
for (const auto& function : module.GetFunctions()) {
|
|
if (function) {
|
|
changed |= RunGVNOnFunction(*function);
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
} // namespace ir
|