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

#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