forked from KPGPMC/furlang
@@ -20,6 +20,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
static void optimize(const std::unique_ptr<furlang::ir::function>& func);
|
static void optimize(const std::unique_ptr<furlang::ir::function>& func);
|
||||||
|
|
||||||
|
static void constant_propagation(const std::unique_ptr<furlang::ir::function>& func);
|
||||||
|
|
||||||
static void dfs_rpo(furlang::ir::block_index block,
|
static void dfs_rpo(furlang::ir::block_index block,
|
||||||
const block_map_t& successors,
|
const block_map_t& successors,
|
||||||
std::unordered_set<furlang::ir::block_index>& visited,
|
std::unordered_set<furlang::ir::block_index>& visited,
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@@ -17,6 +19,7 @@ namespace furc::front {
|
|||||||
void ssa::optimize(furlang::ir::module& mod) {
|
void ssa::optimize(furlang::ir::module& mod) {
|
||||||
for (const auto& func : mod.functions()) {
|
for (const auto& func : mod.functions()) {
|
||||||
ssa::optimize(func);
|
ssa::optimize(func);
|
||||||
|
ssa::constant_propagation(func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +263,224 @@ void ssa::optimize(const std::unique_ptr<furlang::ir::function>& func) {
|
|||||||
renameBlock(entry);
|
renameBlock(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class lattice_t {
|
||||||
|
Top,
|
||||||
|
Constant,
|
||||||
|
Bottom,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lattice {
|
||||||
|
lattice_t type = lattice_t::Top;
|
||||||
|
std::uint64_t value = 0;
|
||||||
|
|
||||||
|
bool operator==(const lattice& rhs) const {
|
||||||
|
if (type != rhs.type) return false;
|
||||||
|
return type != lattice_t::Constant || value == rhs.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void ssa::constant_propagation(const std::unique_ptr<furlang::ir::function>& func) {
|
||||||
|
using block_idx = furlang::ir::block_index;
|
||||||
|
using reg_t = furlang::ir::register_operand;
|
||||||
|
using reg2_t = furlang::ir::register_t;
|
||||||
|
|
||||||
|
std::unordered_map<reg_t, lattice> latVals;
|
||||||
|
std::unordered_map<reg2_t, std::vector<furlang::ir::instruction*>> edges;
|
||||||
|
std::unordered_set<block_idx> executableBlocks;
|
||||||
|
std::set<std::pair<block_idx, block_idx>> executedEdges;
|
||||||
|
|
||||||
|
std::queue<std::pair<block_idx, block_idx>> cfgWorklist;
|
||||||
|
std::queue<furlang::ir::instruction*> ssaWorklist;
|
||||||
|
|
||||||
|
std::unordered_map<furlang::ir::instruction*, block_idx> blockMap;
|
||||||
|
|
||||||
|
auto getOperandLattice = [&](const furlang::ir::operand& op) -> lattice {
|
||||||
|
if (op.type() == furlang::ir::operand_t::Integer) {
|
||||||
|
lattice lat;
|
||||||
|
lat.type = lattice_t::Constant;
|
||||||
|
lat.value = op.integer();
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
if (op.type() == furlang::ir::operand_t::Register) {
|
||||||
|
auto reg = op.reg();
|
||||||
|
if (latVals.find(reg) == latVals.end()) return { lattice_t::Top };
|
||||||
|
return latVals[reg];
|
||||||
|
}
|
||||||
|
return { lattice_t::Bottom };
|
||||||
|
};
|
||||||
|
|
||||||
|
for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) {
|
||||||
|
const auto& block = func->blocks()[blockIdx];
|
||||||
|
auto& instrs = block->instructions();
|
||||||
|
for (auto it = instrs.begin(); it != instrs.end(); ++it) {
|
||||||
|
const auto& instr = *it;
|
||||||
|
blockMap[instr.get()] = blockIdx;
|
||||||
|
for (const auto& op : instr->sources()) {
|
||||||
|
if (op->type() != furlang::ir::operand_t::Register) continue;
|
||||||
|
edges[op->reg()].push_back(instr.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockMap[block->exit().get()] = blockIdx;
|
||||||
|
for (const auto& op : block->exit()->sources()) {
|
||||||
|
if (op->type() != furlang::ir::operand_t::Register) continue;
|
||||||
|
edges[op->reg()].push_back(block->exit().get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgWorklist.push({ 0, 0 });
|
||||||
|
while (!cfgWorklist.empty() || !ssaWorklist.empty()) {
|
||||||
|
if (!cfgWorklist.empty()) {
|
||||||
|
auto edge = cfgWorklist.front();
|
||||||
|
cfgWorklist.pop();
|
||||||
|
block_idx from = edge.first;
|
||||||
|
block_idx to = edge.second;
|
||||||
|
|
||||||
|
if (executedEdges.count(edge) != 0) continue;
|
||||||
|
executedEdges.insert(edge);
|
||||||
|
|
||||||
|
bool firstVisit = (executableBlocks.find(to) == executableBlocks.end());
|
||||||
|
executableBlocks.insert(to);
|
||||||
|
|
||||||
|
const auto& block = func->blocks()[to];
|
||||||
|
if (firstVisit) {
|
||||||
|
for (auto& instr : block->instructions()) {
|
||||||
|
ssaWorklist.push(instr.get());
|
||||||
|
}
|
||||||
|
ssaWorklist.push(block->exit().get());
|
||||||
|
} else {
|
||||||
|
for (auto& instr : block->instructions()) {
|
||||||
|
if (instr->type() != furlang::ir::instruction_t::Phi) break;
|
||||||
|
ssaWorklist.push(instr.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ssaWorklist.empty()) {
|
||||||
|
auto* instr = ssaWorklist.front();
|
||||||
|
ssaWorklist.pop();
|
||||||
|
|
||||||
|
block_idx blockIdx = blockMap[instr];
|
||||||
|
if (executableBlocks.find(blockIdx) == executableBlocks.end()) continue;
|
||||||
|
|
||||||
|
lattice newLat = { lattice_t::Top };
|
||||||
|
|
||||||
|
switch (instr->type()) {
|
||||||
|
case furlang::ir::instruction_t::Phi: {
|
||||||
|
auto& phi = dynamic_cast<furlang::ir::phi_instruction&>(*instr);
|
||||||
|
for (const auto& [op, label] : phi.labels()) {
|
||||||
|
if (executedEdges.count({ label, blockIdx }) == 0) continue;
|
||||||
|
lattice opLat = getOperandLattice(op);
|
||||||
|
if (opLat.type == lattice_t::Bottom) newLat.type = lattice_t::Bottom;
|
||||||
|
if (opLat.type == lattice_t::Constant) {
|
||||||
|
if (newLat.type == lattice_t::Top) {
|
||||||
|
newLat = opLat;
|
||||||
|
} else if (newLat.type == lattice_t::Constant && newLat.value != opLat.value) {
|
||||||
|
newLat.type = lattice_t::Bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case furlang::ir::instruction_t::Assign: {
|
||||||
|
newLat = getOperandLattice(*instr->sources().front());
|
||||||
|
} break;
|
||||||
|
case furlang::ir::instruction_t::BinaryOp: {
|
||||||
|
lattice lhs = getOperandLattice(*instr->sources()[0]);
|
||||||
|
lattice rhs = getOperandLattice(*instr->sources()[1]);
|
||||||
|
|
||||||
|
if (lhs.type == lattice_t::Bottom || rhs.type == lattice_t::Bottom) {
|
||||||
|
newLat.type = lattice_t::Bottom;
|
||||||
|
} else if (lhs.type == lattice_t::Constant && rhs.type == lattice_t::Constant) {
|
||||||
|
newLat.type = lattice_t::Constant;
|
||||||
|
switch (dynamic_cast<const furlang::ir::binary_op_instruction&>(*instr).op_type()) {
|
||||||
|
case furlang::ir::binary_op_instruction_t::Add: newLat.value = lhs.value + rhs.value; break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::Sub: newLat.value = lhs.value - rhs.value; break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::Mul: newLat.value = lhs.value * rhs.value; break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::Div: newLat.value = lhs.value / rhs.value; break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::Mod: newLat.value = lhs.value % rhs.value; break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::Eq:
|
||||||
|
newLat.value = (lhs.value == rhs.value) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::NotEq:
|
||||||
|
newLat.value = (lhs.value != rhs.value) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::LessThan:
|
||||||
|
newLat.value = (lhs.value < rhs.value) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::GreaterThan:
|
||||||
|
newLat.value = (lhs.value > rhs.value) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::LessEq:
|
||||||
|
newLat.value = (lhs.value <= rhs.value) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
case furlang::ir::binary_op_instruction_t::GreaterEq:
|
||||||
|
newLat.value = (lhs.value >= rhs.value) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) {
|
||||||
|
auto dst = instr->destination().reg();
|
||||||
|
if (!(latVals[dst] == newLat)) {
|
||||||
|
latVals[dst] = newLat;
|
||||||
|
for (auto* uInstr : edges[dst])
|
||||||
|
ssaWorklist.push(uInstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instr == func->blocks()[blockIdx]->exit().get()) {
|
||||||
|
auto* exit = func->blocks()[blockIdx]->exit().get();
|
||||||
|
if (exit->type() == furlang::ir::instruction_t::Branch) {
|
||||||
|
auto& br = dynamic_cast<furlang::ir::branch_instruction&>(*exit);
|
||||||
|
cfgWorklist.push({ blockIdx, br.block() });
|
||||||
|
} else if (exit->type() == furlang::ir::instruction_t::BranchCond) {
|
||||||
|
auto& br = dynamic_cast<furlang::ir::branch_cond_instruction&>(*exit);
|
||||||
|
lattice cond = getOperandLattice(*exit->sources()[0]);
|
||||||
|
|
||||||
|
if (cond.type == lattice_t::Constant) {
|
||||||
|
if (cond.value != 0)
|
||||||
|
cfgWorklist.push({ blockIdx, br.if_block() });
|
||||||
|
else
|
||||||
|
cfgWorklist.push({ blockIdx, br.else_block() });
|
||||||
|
} else {
|
||||||
|
cfgWorklist.push({ blockIdx, br.if_block() });
|
||||||
|
cfgWorklist.push({ blockIdx, br.else_block() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (block_idx i = 0; i < func->blocks().size(); ++i) {
|
||||||
|
if (executableBlocks.find(i) == executableBlocks.end()) {
|
||||||
|
func->blocks()[i]->instructions().clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& block = func->blocks()[i];
|
||||||
|
|
||||||
|
for (auto& instr : block->instructions()) {
|
||||||
|
for (auto& op : instr->sources()) {
|
||||||
|
if (op->type() != furlang::ir::operand_t::Register) continue;
|
||||||
|
auto reg = op->reg();
|
||||||
|
if (latVals[reg].type != lattice_t::Constant) continue;
|
||||||
|
*op = furlang::ir::operand::new_integer(latVals[reg].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* exit = block->exit().get();
|
||||||
|
if (exit->type() != furlang::ir::instruction_t::BranchCond) continue;
|
||||||
|
auto& br = dynamic_cast<furlang::ir::branch_cond_instruction&>(*exit);
|
||||||
|
lattice cond = getOperandLattice(*exit->sources()[0]);
|
||||||
|
if (cond.type != lattice_t::Constant) continue;
|
||||||
|
block_idx target = (cond.value != 0) ? br.if_block() : br.else_block();
|
||||||
|
block->exit() = std::make_unique<furlang::ir::branch_instruction>(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ssa::dfs_rpo(furlang::ir::block_index block,
|
void ssa::dfs_rpo(furlang::ir::block_index block,
|
||||||
const block_map_t& successors,
|
const block_map_t& successors,
|
||||||
std::unordered_set<furlang::ir::block_index>& visited,
|
std::unordered_set<furlang::ir::block_index>& visited,
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ struct register_operand {
|
|||||||
friend std::ostream& operator<<(std::ostream& os, const register_operand& op) {
|
friend std::ostream& operator<<(std::ostream& os, const register_operand& op) {
|
||||||
return os << op.reg << '_' << op.ver;
|
return os << op.reg << '_' << op.ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==(const register_operand& rhs) const { return reg == rhs.reg && ver == rhs.ver; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,4 +272,17 @@ private:
|
|||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace furlang
|
} // namespace furlang
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<furlang::ir::register_operand> {
|
||||||
|
std::size_t operator()(const furlang::ir::register_operand& op) const noexcept {
|
||||||
|
std::size_t h1 = std::hash<decltype(op.reg)>{}(op.reg);
|
||||||
|
std::size_t h2 = std::hash<std::uint32_t>{}(op.ver);
|
||||||
|
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
#endif // FURLANG_IR_OPERAND_HPP
|
#endif // FURLANG_IR_OPERAND_HPP
|
||||||
|
|||||||
Reference in New Issue
Block a user