Oh my. Basic CFA/DFA, local variable support, misc fixes, etc.
This commit is contained in:
203
src/alloy/compiler/passes/data_flow_analysis_pass.cc
Normal file
203
src/alloy/compiler/passes/data_flow_analysis_pass.cc
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#pragma warning(disable : 4267)
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::compiler::passes;
|
||||
using namespace alloy::frontend;
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
|
||||
DataFlowAnalysisPass::DataFlowAnalysisPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
|
||||
DataFlowAnalysisPass::~DataFlowAnalysisPass() {
|
||||
}
|
||||
|
||||
int DataFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||
auto arena = builder->arena();
|
||||
|
||||
// Linearize blocks so that we can detect cycles and propagate dependencies.
|
||||
uint32_t block_count = LinearizeBlocks(builder);
|
||||
|
||||
// Analyze value flow and add locals as needed.
|
||||
AnalyzeFlow(builder, block_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t DataFlowAnalysisPass::LinearizeBlocks(HIRBuilder* builder) {
|
||||
// TODO(benvanik): actually do this - we cheat now knowing that they are in
|
||||
// sequential order.
|
||||
uint32_t block_ordinal = 0;
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
block->ordinal = block_ordinal++;
|
||||
block = block->next;
|
||||
}
|
||||
return block_ordinal;
|
||||
}
|
||||
|
||||
void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
||||
uint32_t block_count) {
|
||||
uint32_t max_value_estimate =
|
||||
builder->max_value_ordinal() + 1 + block_count * 4;
|
||||
|
||||
// Stash for value map. We may want to maintain this during building.
|
||||
auto arena = builder->arena();
|
||||
Value** value_map = (Value**)arena->Alloc(
|
||||
sizeof(Value*) * max_value_estimate);
|
||||
|
||||
// Allocate incoming bitvectors for use by blocks. We don't need outgoing
|
||||
// because they are only used during the block iteration.
|
||||
// Mapped by block ordinal.
|
||||
// TODO(benvanik): cache this list, grow as needed, etc.
|
||||
auto incoming_bitvectors = (llvm::BitVector**)arena->Alloc(
|
||||
sizeof(llvm::BitVector*) * block_count);
|
||||
for (auto n = 0u; n < block_count; n++) {
|
||||
incoming_bitvectors[n] = new llvm::BitVector(max_value_estimate);
|
||||
}
|
||||
|
||||
// Walk blocks in reverse and calculate incoming/outgoing values.
|
||||
auto block = builder->last_block();
|
||||
while (block) {
|
||||
// allocate bitsets based on max value number
|
||||
block->incoming_values = incoming_bitvectors[block->ordinal];
|
||||
auto& incoming_values = *block->incoming_values;
|
||||
|
||||
// Walk instructions and gather up incoming values.
|
||||
auto instr = block->instr_head;
|
||||
while (instr) {
|
||||
uint32_t signature = instr->opcode->signature;
|
||||
#define SET_INCOMING_VALUE(v) \
|
||||
if (v->def && v->def->block != block) { \
|
||||
incoming_values.set(v->ordinal); \
|
||||
} \
|
||||
XEASSERT(v->ordinal < max_value_estimate); \
|
||||
value_map[v->ordinal] = v;
|
||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||
SET_INCOMING_VALUE(instr->src1.value);
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC2(signature) == OPCODE_SIG_TYPE_V) {
|
||||
SET_INCOMING_VALUE(instr->src2.value);
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC3(signature) == OPCODE_SIG_TYPE_V) {
|
||||
SET_INCOMING_VALUE(instr->src3.value);
|
||||
}
|
||||
#undef SET_INCOMING_VALUE
|
||||
instr = instr->next;
|
||||
}
|
||||
|
||||
// Add all successor incoming values to our outgoing, as we need to
|
||||
// pass them through.
|
||||
llvm::BitVector outgoing_values(max_value_estimate);
|
||||
auto outgoing_edge = block->outgoing_edge_head;
|
||||
while (outgoing_edge) {
|
||||
if (outgoing_edge->dest->ordinal > block->ordinal) {
|
||||
outgoing_values |= *outgoing_edge->dest->incoming_values;
|
||||
}
|
||||
outgoing_edge = outgoing_edge->outgoing_next;
|
||||
}
|
||||
incoming_values |= outgoing_values;
|
||||
|
||||
// Add stores for all outgoing values.
|
||||
auto outgoing_ordinal = outgoing_values.find_first();
|
||||
while (outgoing_ordinal != -1) {
|
||||
Value* src_value = value_map[outgoing_ordinal];
|
||||
XEASSERTNOTNULL(src_value);
|
||||
if (!src_value->local_slot) {
|
||||
src_value->local_slot = builder->AllocLocal(src_value->type);
|
||||
}
|
||||
builder->StoreLocal(src_value->local_slot, src_value);
|
||||
|
||||
// If we are in the block the value was defined in:
|
||||
if (src_value->def->block == block) {
|
||||
// Move the store to right after the def, or as soon after
|
||||
// as we can (respecting PAIRED flags).
|
||||
auto def_next = src_value->def->next;
|
||||
while (def_next && def_next->opcode->flags & OPCODE_FLAG_PAIRED_PREV) {
|
||||
def_next = def_next->next;
|
||||
}
|
||||
XEASSERTNOTNULL(def_next);
|
||||
builder->last_instr()->MoveBefore(def_next);
|
||||
|
||||
// We don't need it in the incoming list.
|
||||
incoming_values.reset(outgoing_ordinal);
|
||||
} else {
|
||||
// Eh, just throw at the end, before the first branch.
|
||||
auto tail = block->instr_tail;
|
||||
while (tail && tail->opcode->flags & OPCODE_FLAG_BRANCH) {
|
||||
tail = tail->prev;
|
||||
}
|
||||
XEASSERTNOTZERO(tail);
|
||||
builder->last_instr()->MoveBefore(tail->next);
|
||||
}
|
||||
|
||||
outgoing_ordinal = outgoing_values.find_next(outgoing_ordinal);
|
||||
}
|
||||
|
||||
// Add loads for all incoming values and rename them in the block.
|
||||
auto incoming_ordinal = incoming_values.find_first();
|
||||
while (incoming_ordinal != -1) {
|
||||
Value* src_value = value_map[incoming_ordinal];
|
||||
XEASSERTNOTNULL(src_value);
|
||||
if (!src_value->local_slot) {
|
||||
src_value->local_slot = builder->AllocLocal(src_value->type);
|
||||
}
|
||||
Value* local_value = builder->LoadLocal(src_value->local_slot);
|
||||
builder->last_instr()->MoveBefore(block->instr_head);
|
||||
|
||||
// Swap uses of original value with the local value.
|
||||
auto instr = block->instr_head;
|
||||
while (instr) {
|
||||
uint32_t signature = instr->opcode->signature;
|
||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||
if (instr->src1.value == src_value) {
|
||||
instr->set_src1(local_value);
|
||||
}
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC2(signature) == OPCODE_SIG_TYPE_V) {
|
||||
if (instr->src2.value == src_value) {
|
||||
instr->set_src2(local_value);
|
||||
}
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC3(signature) == OPCODE_SIG_TYPE_V) {
|
||||
if (instr->src3.value == src_value) {
|
||||
instr->set_src3(local_value);
|
||||
}
|
||||
}
|
||||
instr = instr->next;
|
||||
}
|
||||
|
||||
incoming_ordinal = incoming_values.find_next(incoming_ordinal);
|
||||
}
|
||||
|
||||
block = block->prev;
|
||||
}
|
||||
|
||||
// Cleanup bitvectors.
|
||||
for (auto n = 0u; n < block_count; n++) {
|
||||
delete incoming_bitvectors[n];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user