Oh my. Basic CFA/DFA, local variable support, misc fixes, etc.
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
DECLARE_bool(debug);
|
||||
DECLARE_bool(always_disasm);
|
||||
|
||||
DECLARE_bool(validate_hir);
|
||||
|
||||
DECLARE_uint64(break_on_instruction);
|
||||
DECLARE_uint64(break_on_memory);
|
||||
|
||||
|
||||
@@ -21,10 +21,12 @@ using namespace alloy;
|
||||
|
||||
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
|
||||
"Allow debugging and retain debug information.");
|
||||
|
||||
DEFINE_bool(always_disasm, false,
|
||||
"Always add debug info to functions, even when no debugger is attached.");
|
||||
|
||||
DEFINE_bool(validate_hir, false,
|
||||
"Perform validation checks on the HIR during compilation.");
|
||||
|
||||
// Breakpoints:
|
||||
DEFINE_uint64(break_on_instruction, 0,
|
||||
"int3 before the given guest address is executed.");
|
||||
|
||||
@@ -74,6 +74,15 @@ int IVMAssembler::Assemble(
|
||||
builder->ResetLabelTags();
|
||||
|
||||
// Function prologue.
|
||||
size_t stack_size = 0;
|
||||
auto locals = builder->locals();
|
||||
for (auto it = locals.begin(); it != locals.end(); ++it) {
|
||||
auto slot = *it;
|
||||
size_t stack_offset = stack_size;
|
||||
slot->set_constant(stack_offset);
|
||||
stack_size += GetTypeSize(slot->type);
|
||||
}
|
||||
ctx.stack_size = stack_size;
|
||||
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
|
||||
@@ -33,6 +33,7 @@ IVMFunction::~IVMFunction() {
|
||||
|
||||
void IVMFunction::Setup(TranslationContext& ctx) {
|
||||
register_count_ = ctx.register_count;
|
||||
stack_size_ = ctx.stack_size;
|
||||
intcode_count_ = ctx.intcode_count;
|
||||
intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents();
|
||||
source_map_count_ = ctx.source_map_count;
|
||||
@@ -108,11 +109,13 @@ int IVMFunction::CallImpl(ThreadState* thread_state) {
|
||||
// Setup register file on stack.
|
||||
auto stack = (IVMStack*)thread_state->backend_data();
|
||||
auto register_file = (Register*)stack->Alloc(register_count_);
|
||||
auto local_stack = (uint8_t*)alloca(stack_size_);
|
||||
|
||||
Memory* memory = thread_state->memory();
|
||||
|
||||
IntCodeState ics;
|
||||
ics.rf = register_file;
|
||||
ics.locals = local_stack;
|
||||
ics.context = (uint8_t*)thread_state->raw_context();
|
||||
ics.membase = memory->membase();
|
||||
ics.did_carry = 0;
|
||||
|
||||
@@ -38,9 +38,10 @@ private:
|
||||
void OnBreakpointHit(runtime::ThreadState* thread_state, IntCode* i);
|
||||
|
||||
private:
|
||||
size_t register_count_;
|
||||
size_t intcode_count_;
|
||||
IntCode* intcodes_;
|
||||
size_t register_count_;
|
||||
size_t stack_size_;
|
||||
size_t intcode_count_;
|
||||
IntCode* intcodes_;
|
||||
size_t source_map_count_;
|
||||
SourceMapEntry* source_map_;
|
||||
};
|
||||
|
||||
@@ -1342,6 +1342,88 @@ int Translate_LOAD_CLOCK(TranslationContext& ctx, Instr* i) {
|
||||
return DispatchToC(ctx, i, IntCode_LOAD_CLOCK);
|
||||
}
|
||||
|
||||
uint32_t IntCode_LOAD_LOCAL_I8(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].i8 = *((int8_t*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_LOAD_LOCAL_I16(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].i16 = *((int16_t*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_LOAD_LOCAL_I32(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].i32 = *((int32_t*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_LOAD_LOCAL_I64(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].i64 = *((int64_t*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_LOAD_LOCAL_F32(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].f32 = *((float*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_LOAD_LOCAL_F64(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].f64 = *((double*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_LOAD_LOCAL_V128(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].v128 = *((vec128_t*)(ics.locals + ics.rf[i->src1_reg].u64));
|
||||
return IA_NEXT;
|
||||
}
|
||||
int Translate_LOAD_LOCAL(TranslationContext& ctx, Instr* i) {
|
||||
static IntCodeFn fns[] = {
|
||||
IntCode_LOAD_LOCAL_I8,
|
||||
IntCode_LOAD_LOCAL_I16,
|
||||
IntCode_LOAD_LOCAL_I32,
|
||||
IntCode_LOAD_LOCAL_I64,
|
||||
IntCode_LOAD_LOCAL_F32,
|
||||
IntCode_LOAD_LOCAL_F64,
|
||||
IntCode_LOAD_LOCAL_V128,
|
||||
};
|
||||
return DispatchToC(ctx, i, fns[i->dest->type]);
|
||||
}
|
||||
|
||||
uint32_t IntCode_STORE_LOCAL_I8(IntCodeState& ics, const IntCode* i) {
|
||||
*((int8_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i8;
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_STORE_LOCAL_I16(IntCodeState& ics, const IntCode* i) {
|
||||
*((int16_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i16;
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_STORE_LOCAL_I32(IntCodeState& ics, const IntCode* i) {
|
||||
*((int32_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i32;
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_STORE_LOCAL_I64(IntCodeState& ics, const IntCode* i) {
|
||||
*((int64_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i64;
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_STORE_LOCAL_F32(IntCodeState& ics, const IntCode* i) {
|
||||
*((float*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].f32;
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_STORE_LOCAL_F64(IntCodeState& ics, const IntCode* i) {
|
||||
*((double*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].f64;
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_STORE_LOCAL_V128(IntCodeState& ics, const IntCode* i) {
|
||||
*((vec128_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].v128;
|
||||
return IA_NEXT;
|
||||
}
|
||||
int Translate_STORE_LOCAL(TranslationContext& ctx, Instr* i) {
|
||||
static IntCodeFn fns[] = {
|
||||
IntCode_STORE_LOCAL_I8,
|
||||
IntCode_STORE_LOCAL_I16,
|
||||
IntCode_STORE_LOCAL_I32,
|
||||
IntCode_STORE_LOCAL_I64,
|
||||
IntCode_STORE_LOCAL_F32,
|
||||
IntCode_STORE_LOCAL_F64,
|
||||
IntCode_STORE_LOCAL_V128,
|
||||
};
|
||||
return DispatchToC(ctx, i, fns[i->src2.value->type]);
|
||||
}
|
||||
|
||||
uint32_t IntCode_LOAD_CONTEXT_I8(IntCodeState& ics, const IntCode* i) {
|
||||
ics.rf[i->dest_reg].i8 = *((int8_t*)(ics.context + ics.rf[i->src1_reg].u64));
|
||||
DPRINT("%d (%X) = ctx i8 +%d\n", ics.rf[i->dest_reg].i8, ics.rf[i->dest_reg].u8, ics.rf[i->src1_reg].u64);
|
||||
@@ -4039,6 +4121,9 @@ static const TranslateFn dispatch_table[] = {
|
||||
|
||||
Translate_LOAD_CLOCK,
|
||||
|
||||
Translate_LOAD_LOCAL,
|
||||
Translate_STORE_LOCAL,
|
||||
|
||||
Translate_LOAD_CONTEXT,
|
||||
Translate_STORE_CONTEXT,
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef union {
|
||||
|
||||
typedef struct {
|
||||
Register* rf;
|
||||
uint8_t* locals;
|
||||
uint8_t* context;
|
||||
uint8_t* membase;
|
||||
int8_t did_carry;
|
||||
@@ -103,6 +104,7 @@ typedef struct {
|
||||
Arena* source_map_arena;
|
||||
Arena* scratch_arena;
|
||||
LabelRef* label_ref_head;
|
||||
size_t stack_size;
|
||||
} TranslationContext;
|
||||
|
||||
|
||||
|
||||
@@ -815,6 +815,117 @@ table->AddSequence(OPCODE_LOAD_CLOCK, [](X64Emitter& e, Instr*& i) {
|
||||
return true;
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Stack Locals
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
table->AddSequence(OPCODE_LOAD_LOCAL, [](X64Emitter& e, Instr*& i) {
|
||||
auto addr = e.rsp + i->src1.value->AsUint32();
|
||||
if (i->Match(SIG_TYPE_I8, SIG_TYPE_IGNORE)) {
|
||||
Reg8 dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
e.mov(dest, e.byte[addr]);
|
||||
e.EndOp(dest);
|
||||
} else if (i->Match(SIG_TYPE_I16, SIG_TYPE_IGNORE)) {
|
||||
Reg16 dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
e.mov(dest, e.word[addr]);
|
||||
e.EndOp(dest);
|
||||
} else if (i->Match(SIG_TYPE_I32, SIG_TYPE_IGNORE)) {
|
||||
Reg32 dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
e.mov(dest, e.dword[addr]);
|
||||
e.EndOp(dest);
|
||||
} else if (i->Match(SIG_TYPE_I64, SIG_TYPE_IGNORE)) {
|
||||
Reg64 dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
e.mov(dest, e.qword[addr]);
|
||||
e.EndOp(dest);
|
||||
} else if (i->Match(SIG_TYPE_F32, SIG_TYPE_IGNORE)) {
|
||||
Xmm dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
e.movss(dest, e.dword[addr]);
|
||||
e.EndOp(dest);
|
||||
} else if (i->Match(SIG_TYPE_F64, SIG_TYPE_IGNORE)) {
|
||||
Xmm dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
e.movsd(dest, e.qword[addr]);
|
||||
e.EndOp(dest);
|
||||
} else if (i->Match(SIG_TYPE_V128, SIG_TYPE_IGNORE)) {
|
||||
Xmm dest;
|
||||
e.BeginOp(i->dest, dest, REG_DEST);
|
||||
// NOTE: we always know we are aligned.
|
||||
e.movaps(dest, e.ptr[addr]);
|
||||
e.EndOp(dest);
|
||||
} else {
|
||||
ASSERT_INVALID_TYPE();
|
||||
}
|
||||
i = e.Advance(i);
|
||||
return true;
|
||||
});
|
||||
|
||||
table->AddSequence(OPCODE_STORE_LOCAL, [](X64Emitter& e, Instr*& i) {
|
||||
auto addr = e.rsp + i->src1.value->AsUint32();
|
||||
if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I8)) {
|
||||
Reg8 src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
e.mov(e.byte[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I8C)) {
|
||||
e.mov(e.byte[addr], i->src2.value->constant.i8);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I16)) {
|
||||
Reg16 src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
e.mov(e.word[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I16C)) {
|
||||
e.mov(e.word[addr], i->src2.value->constant.i16);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I32)) {
|
||||
Reg32 src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
e.mov(e.dword[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I32C)) {
|
||||
e.mov(e.dword[addr], i->src2.value->constant.i32);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I64)) {
|
||||
Reg64 src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
e.mov(e.qword[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_I64C)) {
|
||||
MovMem64(e, addr, i->src2.value->constant.i64);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_F32)) {
|
||||
Xmm src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
e.movss(e.dword[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_F32C)) {
|
||||
e.mov(e.dword[addr], i->src2.value->constant.i32);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_F64)) {
|
||||
Xmm src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
e.movsd(e.qword[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_F64C)) {
|
||||
MovMem64(e, addr, i->src2.value->constant.i64);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_V128)) {
|
||||
Xmm src;
|
||||
e.BeginOp(i->src2.value, src, 0);
|
||||
// NOTE: we always know we are aligned.
|
||||
e.movaps(e.ptr[addr], src);
|
||||
e.EndOp(src);
|
||||
} else if (i->Match(SIG_TYPE_X, SIG_TYPE_IGNORE, SIG_TYPE_V128C)) {
|
||||
// TODO(benvanik): check zero
|
||||
// TODO(benvanik): correct order?
|
||||
MovMem64(e, addr, i->src2.value->constant.v128.low);
|
||||
MovMem64(e, addr + 8, i->src2.value->constant.v128.high);
|
||||
} else {
|
||||
ASSERT_INVALID_TYPE();
|
||||
}
|
||||
i = e.Advance(i);
|
||||
return true;
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Context
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -2892,6 +3003,7 @@ table->AddSequence(OPCODE_ATOMIC_EXCHANGE, [](X64Emitter& e, Instr*& i) {
|
||||
i->src1.value, src1, 0,
|
||||
i->src2.value, src2, 0);
|
||||
e.mov(dest, src2);
|
||||
e.lock();
|
||||
e.xchg(e.dword[src1], dest);
|
||||
e.EndOp(dest, src1, src2);
|
||||
} else {
|
||||
|
||||
@@ -97,6 +97,8 @@ void* X64Emitter::Emplace(size_t stack_size) {
|
||||
return new_address;
|
||||
}
|
||||
|
||||
#define XEALIGN(value, align) ((value + align - 1) & ~(align - 1))
|
||||
|
||||
int X64Emitter::Emit(HIRBuilder* builder) {
|
||||
// These are the registers we will not be using. All others are fare game.
|
||||
const uint32_t reserved_regs =
|
||||
@@ -120,6 +122,19 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||
GetRegBit(xmm4) |
|
||||
GetRegBit(xmm5);
|
||||
|
||||
// Calculate stack size. We need to align things to their natural sizes.
|
||||
// This could be much better (sort by type/etc).
|
||||
auto locals = builder->locals();
|
||||
size_t stack_offset = 0;
|
||||
for (auto it = locals.begin(); it != locals.end(); ++it) {
|
||||
auto slot = *it;
|
||||
size_t type_size = GetTypeSize(slot->type);
|
||||
// Align to natural size.
|
||||
stack_offset = XEALIGN(stack_offset, type_size);
|
||||
slot->set_constant(stack_offset);
|
||||
stack_offset += type_size;
|
||||
}
|
||||
|
||||
// Function prolog.
|
||||
// Must be 16b aligned.
|
||||
// Windows is very strict about the form of this and the epilog:
|
||||
|
||||
@@ -11,11 +11,14 @@
|
||||
#define ALLOY_COMPILER_COMPILER_PASSES_H_
|
||||
|
||||
#include <alloy/compiler/passes/constant_propagation_pass.h>
|
||||
#include <alloy/compiler/passes/control_flow_analysis_pass.h>
|
||||
#include <alloy/compiler/passes/context_promotion_pass.h>
|
||||
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
|
||||
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
|
||||
#include <alloy/compiler/passes/finalization_pass.h>
|
||||
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
||||
#include <alloy/compiler/passes/simplification_pass.h>
|
||||
#include <alloy/compiler/passes/validation_pass.h>
|
||||
#include <alloy/compiler/passes/value_reduction_pass.h>
|
||||
|
||||
// TODO:
|
||||
|
||||
72
src/alloy/compiler/passes/control_flow_analysis_pass.cc
Normal file
72
src/alloy/compiler/passes/control_flow_analysis_pass.cc
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/control_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;
|
||||
|
||||
|
||||
ControlFlowAnalysisPass::ControlFlowAnalysisPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
|
||||
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {
|
||||
}
|
||||
|
||||
int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||
// TODO(benvanik): reset edges for all blocks? Needed to be re-runnable.
|
||||
|
||||
// Add edges.
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
auto instr = block->instr_head;
|
||||
while (instr) {
|
||||
if (instr->opcode->flags & OPCODE_FLAG_BRANCH) {
|
||||
if (instr->opcode == &OPCODE_BRANCH_info) {
|
||||
auto label = instr->src1.label;
|
||||
builder->AddEdge(block, label->block, Edge::UNCONDITIONAL);
|
||||
} else if (instr->opcode == &OPCODE_BRANCH_TRUE_info ||
|
||||
instr->opcode == &OPCODE_BRANCH_FALSE_info) {
|
||||
auto label = instr->src2.label;
|
||||
builder->AddEdge(block, label->block, 0);
|
||||
}
|
||||
}
|
||||
instr = instr->next;
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
// Mark dominators.
|
||||
block = builder->first_block();
|
||||
while (block) {
|
||||
if (block->incoming_edge_head &&
|
||||
!block->incoming_edge_head->incoming_next) {
|
||||
block->incoming_edge_head->flags |= Edge::DOMINATES;
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
37
src/alloy/compiler/passes/control_flow_analysis_pass.h
Normal file
37
src/alloy/compiler/passes/control_flow_analysis_pass.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
|
||||
class ControlFlowAnalysisPass : public CompilerPass {
|
||||
public:
|
||||
ControlFlowAnalysisPass();
|
||||
virtual ~ControlFlowAnalysisPass();
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||
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];
|
||||
}
|
||||
}
|
||||
39
src/alloy/compiler/passes/data_flow_analysis_pass.h
Normal file
39
src/alloy/compiler/passes/data_flow_analysis_pass.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
|
||||
class DataFlowAnalysisPass : public CompilerPass {
|
||||
public:
|
||||
DataFlowAnalysisPass();
|
||||
virtual ~DataFlowAnalysisPass();
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
|
||||
private:
|
||||
uint32_t LinearizeBlocks(hir::HIRBuilder* builder);
|
||||
void AnalyzeFlow(hir::HIRBuilder* builder, uint32_t block_count);
|
||||
};
|
||||
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
||||
@@ -59,20 +59,21 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||
// all removed ops with NOP and then do a single pass that removes them
|
||||
// all.
|
||||
|
||||
bool any_removed = false;
|
||||
bool any_instr_removed = false;
|
||||
bool any_locals_removed = false;
|
||||
Block* block = builder->first_block();
|
||||
while (block) {
|
||||
// Walk instructions in reverse.
|
||||
Instr* i = block->instr_tail;
|
||||
while (i) {
|
||||
Instr* prev = i->prev;
|
||||
auto prev = i->prev;
|
||||
|
||||
const OpcodeInfo* opcode = i->opcode;
|
||||
uint32_t signature = opcode->signature;
|
||||
auto opcode = i->opcode;
|
||||
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) &&
|
||||
i->dest && !i->dest->use_head) {
|
||||
// Has no uses and is not volatile. This instruction can die!
|
||||
MakeNopRecursive(i);
|
||||
any_removed = true;
|
||||
any_instr_removed = true;
|
||||
} else if (opcode == &OPCODE_ASSIGN_info) {
|
||||
// Assignment. These are useless, so just try to remove by completely
|
||||
// replacing the value.
|
||||
@@ -82,11 +83,31 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||
i = prev;
|
||||
}
|
||||
|
||||
// Walk instructions forward.
|
||||
i = block->instr_head;
|
||||
while (i) {
|
||||
auto next = i->next;
|
||||
|
||||
auto opcode = i->opcode;
|
||||
if (opcode == &OPCODE_STORE_LOCAL_info) {
|
||||
// Check to see if the store has any interceeding uses after the load.
|
||||
// If not, it can be removed (as the local is just passing through the
|
||||
// function).
|
||||
// We do this after the previous pass so that removed code doesn't keep
|
||||
// the local alive.
|
||||
if (!CheckLocalUse(i)) {
|
||||
any_locals_removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
i = next;
|
||||
}
|
||||
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
// Remove all nops.
|
||||
if (any_removed) {
|
||||
if (any_instr_removed) {
|
||||
Block* block = builder->first_block();
|
||||
while (block) {
|
||||
Instr* i = block->instr_head;
|
||||
@@ -102,6 +123,21 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any locals that no longer have uses.
|
||||
if (any_locals_removed) {
|
||||
// TODO(benvanik): local removal/dealloc.
|
||||
auto locals = builder->locals();
|
||||
for (auto it = locals.begin(); it != locals.end();) {
|
||||
auto next = ++it;
|
||||
auto value = *it;
|
||||
if (!value->use_head) {
|
||||
// Unused, can be removed.
|
||||
locals.erase(it);
|
||||
}
|
||||
it = next;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -150,3 +186,24 @@ void DeadCodeEliminationPass::ReplaceAssignment(Instr* i) {
|
||||
|
||||
i->Remove();
|
||||
}
|
||||
|
||||
bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
|
||||
auto slot = i->src1.value;
|
||||
auto src = i->src2.value;
|
||||
|
||||
auto use = src->use_head;
|
||||
if (use) {
|
||||
auto use_instr = use->instr;
|
||||
if (use_instr->opcode != &OPCODE_LOAD_LOCAL_info) {
|
||||
// A valid use (probably). Keep it.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load/store are paired. They can both be removed.
|
||||
use_instr->Remove();
|
||||
}
|
||||
|
||||
i->Remove();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
private:
|
||||
void MakeNopRecursive(hir::Instr* i);
|
||||
void ReplaceAssignment(hir::Instr* i);
|
||||
bool CheckLocalUse(hir::Instr* i);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
'constant_propagation_pass.h',
|
||||
'context_promotion_pass.cc',
|
||||
'context_promotion_pass.h',
|
||||
'control_flow_analysis_pass.cc',
|
||||
'control_flow_analysis_pass.h',
|
||||
'data_flow_analysis_pass.cc',
|
||||
'data_flow_analysis_pass.h',
|
||||
'dead_code_elimination_pass.cc',
|
||||
'dead_code_elimination_pass.h',
|
||||
'finalization_pass.cc',
|
||||
@@ -13,6 +17,8 @@
|
||||
#'dead_store_elimination_pass.h',
|
||||
'simplification_pass.cc',
|
||||
'simplification_pass.h',
|
||||
'validation_pass.cc',
|
||||
'validation_pass.h',
|
||||
'value_reduction_pass.cc',
|
||||
'value_reduction_pass.h',
|
||||
],
|
||||
|
||||
99
src/alloy/compiler/passes/validation_pass.cc
Normal file
99
src/alloy/compiler/passes/validation_pass.cc
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/validation_pass.h>
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
ValidationPass::ValidationPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
|
||||
ValidationPass::~ValidationPass() {
|
||||
}
|
||||
|
||||
int ValidationPass::Run(HIRBuilder* builder) {
|
||||
StringBuffer str;
|
||||
builder->Dump(&str);
|
||||
printf(str.GetString());
|
||||
fflush(stdout);
|
||||
str.Reset();
|
||||
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
auto label = block->label_head;
|
||||
while (label) {
|
||||
XEASSERT(label->block == block);
|
||||
if (label->block != block) {
|
||||
return 1;
|
||||
}
|
||||
label = label->next;
|
||||
}
|
||||
|
||||
auto instr = block->instr_head;
|
||||
while (instr) {
|
||||
if (ValidateInstruction(block, instr)) {
|
||||
return 1;
|
||||
}
|
||||
instr = instr->next;
|
||||
}
|
||||
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ValidationPass::ValidateInstruction(Block* block, Instr* instr) {
|
||||
XEASSERT(instr->block == block);
|
||||
if (instr->block != block) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t signature = instr->opcode->signature;
|
||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||
if (ValidateValue(block, instr, instr->src1.value)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC2(signature) == OPCODE_SIG_TYPE_V) {
|
||||
if (ValidateValue(block, instr, instr->src2.value)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC3(signature) == OPCODE_SIG_TYPE_V) {
|
||||
if (ValidateValue(block, instr, instr->src3.value)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ValidationPass::ValidateValue(Block* block, Instr* instr, Value* value) {
|
||||
if (value->def) {
|
||||
/*auto def = value->def;
|
||||
XEASSERT(def->block == block);
|
||||
if (def->block != block) {
|
||||
return 1;
|
||||
}*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
39
src/alloy/compiler/passes/validation_pass.h
Normal file
39
src/alloy/compiler/passes/validation_pass.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef ALLOY_COMPILER_PASSES_VALIDATION_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_VALIDATION_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
|
||||
class ValidationPass : public CompilerPass {
|
||||
public:
|
||||
ValidationPass();
|
||||
virtual ~ValidationPass();
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
|
||||
private:
|
||||
int ValidateInstruction(hir::Block* block, hir::Instr* instr);
|
||||
int ValidateValue(hir::Block* block, hir::Instr* instr, hir::Value* value);
|
||||
};
|
||||
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_VALIDATION_PASS_H_
|
||||
@@ -13,7 +13,11 @@
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
#include <bitset>
|
||||
#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;
|
||||
@@ -51,8 +55,7 @@ void ValueReductionPass::ComputeLastUse(Value* value) {
|
||||
int ValueReductionPass::Run(HIRBuilder* builder) {
|
||||
// Walk each block and reuse variable ordinals as much as possible.
|
||||
|
||||
// Let's hope this is enough.
|
||||
std::bitset<1024> ordinals;
|
||||
llvm::BitVector ordinals(builder->max_value_ordinal());
|
||||
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
@@ -82,7 +85,7 @@ int ValueReductionPass::Run(HIRBuilder* builder) {
|
||||
if (v->last_use == instr) {
|
||||
// Available.
|
||||
if (!instr->src1.value->IsConstant()) {
|
||||
ordinals.set(v->ordinal, false);
|
||||
ordinals.reset(v->ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,7 +97,7 @@ int ValueReductionPass::Run(HIRBuilder* builder) {
|
||||
if (v->last_use == instr) {
|
||||
// Available.
|
||||
if (!instr->src2.value->IsConstant()) {
|
||||
ordinals.set(v->ordinal, false);
|
||||
ordinals.reset(v->ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +109,7 @@ int ValueReductionPass::Run(HIRBuilder* builder) {
|
||||
if (v->last_use == instr) {
|
||||
// Available.
|
||||
if (!instr->src3.value->IsConstant()) {
|
||||
ordinals.set(v->ordinal, false);
|
||||
ordinals.reset(v->ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +118,7 @@ int ValueReductionPass::Run(HIRBuilder* builder) {
|
||||
// source value ordinal.
|
||||
auto v = instr->dest;
|
||||
// Find a lower ordinal.
|
||||
for (auto n = 0; n < ordinals.size(); n++) {
|
||||
for (auto n = 0u; n < ordinals.size(); n++) {
|
||||
if (!ordinals.test(n)) {
|
||||
ordinals.set(n);
|
||||
v->ordinal = n;
|
||||
|
||||
@@ -38,20 +38,40 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) :
|
||||
assembler_ = backend->CreateAssembler();
|
||||
assembler_->Initialize();
|
||||
|
||||
bool validate = FLAGS_validate_hir;
|
||||
|
||||
// Build the CFG first.
|
||||
compiler_->AddPass(new passes::ControlFlowAnalysisPass());
|
||||
|
||||
// Passes are executed in the order they are added. Multiple of the same
|
||||
// pass type may be used.
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
//compiler_->AddPass(new passes::ContextPromotionPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
compiler_->AddPass(new passes::SimplificationPass());
|
||||
// TODO(benvanik): run repeatedly?
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
compiler_->AddPass(new passes::ConstantPropagationPass());
|
||||
//compiler_->AddPass(new passes::TypePropagationPass());
|
||||
//compiler_->AddPass(new passes::ByteSwapEliminationPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
compiler_->AddPass(new passes::SimplificationPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
//compiler_->AddPass(new passes::DeadStoreEliminationPass());
|
||||
//if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
compiler_->AddPass(new passes::DeadCodeEliminationPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
|
||||
// Adds local load/stores.
|
||||
compiler_->AddPass(new passes::DataFlowAnalysisPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
compiler_->AddPass(new passes::SimplificationPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
|
||||
// Run DCE one more time to cleanup any local manipulation.
|
||||
compiler_->AddPass(new passes::DeadCodeEliminationPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
|
||||
// Removes all unneeded variables. Try not to add new ones after this.
|
||||
compiler_->AddPass(new passes::ValueReductionPass());
|
||||
if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
|
||||
// Must come last. The HIR is not really HIR after this.
|
||||
compiler_->AddPass(new passes::FinalizationPass());
|
||||
|
||||
@@ -12,15 +12,37 @@
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
XEDECLARECLASS1(llvm, BitVector);
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace hir {
|
||||
|
||||
class Block;
|
||||
class HIRBuilder;
|
||||
class Instr;
|
||||
class Label;
|
||||
|
||||
|
||||
class Edge {
|
||||
public:
|
||||
enum EdgeFlags {
|
||||
UNCONDITIONAL = (1 << 0),
|
||||
DOMINATES = (1 << 1),
|
||||
};
|
||||
public:
|
||||
Edge* outgoing_next;
|
||||
Edge* outgoing_prev;
|
||||
Edge* incoming_next;
|
||||
Edge* incoming_prev;
|
||||
|
||||
Block* src;
|
||||
Block* dest;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
|
||||
class Block {
|
||||
public:
|
||||
Arena* arena;
|
||||
@@ -28,6 +50,10 @@ public:
|
||||
Block* next;
|
||||
Block* prev;
|
||||
|
||||
Edge* incoming_edge_head;
|
||||
Edge* outgoing_edge_head;
|
||||
llvm::BitVector* incoming_values;
|
||||
|
||||
Label* label_head;
|
||||
Label* label_tail;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ void HIRBuilder::Reset() {
|
||||
attributes_ = 0;
|
||||
next_label_id_ = 0;
|
||||
next_value_ordinal_ = 0;
|
||||
locals_.clear();
|
||||
block_head_ = block_tail_ = NULL;
|
||||
current_block_ = NULL;
|
||||
#if XE_DEBUG
|
||||
@@ -141,6 +142,13 @@ void HIRBuilder::Dump(StringBuffer* str) {
|
||||
str->Append("; attributes = %.8X\n", attributes_);
|
||||
}
|
||||
|
||||
for (auto it = locals_.begin(); it != locals_.end(); ++it) {
|
||||
auto local = *it;
|
||||
str->Append(" ; local ");
|
||||
DumpValue(str, local);
|
||||
str->Append("\n");
|
||||
}
|
||||
|
||||
uint32_t block_ordinal = 0;
|
||||
Block* block = block_head_;
|
||||
while (block) {
|
||||
@@ -161,6 +169,39 @@ void HIRBuilder::Dump(StringBuffer* str) {
|
||||
label = label->next;
|
||||
}
|
||||
|
||||
Edge* incoming_edge = block->incoming_edge_head;
|
||||
while (incoming_edge) {
|
||||
auto src_label = incoming_edge->src->label_head;
|
||||
if (src_label && src_label->name) {
|
||||
str->Append(" ; in: %s", src_label->name);
|
||||
} else if (src_label) {
|
||||
str->Append(" ; in: label%d", src_label->id);
|
||||
} else {
|
||||
str->Append(" ; in: <block%d>",
|
||||
incoming_edge->src->ordinal);
|
||||
}
|
||||
str->Append(", dom:%d, uncond:%d\n",
|
||||
(incoming_edge->flags & Edge::DOMINATES) ? 1 : 0,
|
||||
(incoming_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0);
|
||||
incoming_edge = incoming_edge->incoming_next;
|
||||
}
|
||||
Edge* outgoing_edge = block->outgoing_edge_head;
|
||||
while (outgoing_edge) {
|
||||
auto dest_label = outgoing_edge->dest->label_head;
|
||||
if (dest_label && dest_label->name) {
|
||||
str->Append(" ; out: %s", dest_label->name);
|
||||
} else if (dest_label) {
|
||||
str->Append(" ; out: label%d", dest_label->id);
|
||||
} else {
|
||||
str->Append(" ; out: <block%d>",
|
||||
outgoing_edge->dest->ordinal);
|
||||
}
|
||||
str->Append(", dom:%d, uncond:%d\n",
|
||||
(outgoing_edge->flags & Edge::DOMINATES) ? 1 : 0,
|
||||
(outgoing_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0);
|
||||
outgoing_edge = outgoing_edge->outgoing_next;
|
||||
}
|
||||
|
||||
Instr* i = block->instr_head;
|
||||
while (i) {
|
||||
if (i->opcode->flags & OPCODE_FLAG_HIDE) {
|
||||
@@ -303,6 +344,7 @@ void HIRBuilder::InsertLabel(Label* label, Instr* prev_instr) {
|
||||
block_tail_ = new_block;
|
||||
}
|
||||
new_block->label_head = new_block->label_tail = label;
|
||||
new_block->incoming_edge_head = new_block->outgoing_edge_head = NULL;
|
||||
label->block = new_block;
|
||||
label->prev = label->next = NULL;
|
||||
|
||||
@@ -319,8 +361,7 @@ void HIRBuilder::InsertLabel(Label* label, Instr* prev_instr) {
|
||||
new_block->instr_tail = old_prev_tail;
|
||||
}
|
||||
|
||||
for (auto instr = new_block->instr_head; instr != new_block->instr_tail;
|
||||
instr = instr->next) {
|
||||
for (auto instr = new_block->instr_head; instr; instr = instr->next) {
|
||||
instr->block = new_block;
|
||||
}
|
||||
|
||||
@@ -342,6 +383,19 @@ void HIRBuilder::ResetLabelTags() {
|
||||
}
|
||||
}
|
||||
|
||||
void HIRBuilder::AddEdge(Block* src, Block* dest, uint32_t flags) {
|
||||
Edge* edge = arena_->Alloc<Edge>();
|
||||
edge->src = src;
|
||||
edge->dest = dest;
|
||||
edge->flags = flags;
|
||||
edge->outgoing_prev = NULL;
|
||||
edge->outgoing_next = src->outgoing_edge_head;
|
||||
src->outgoing_edge_head = edge;
|
||||
edge->incoming_prev = NULL;
|
||||
edge->incoming_next = dest->incoming_edge_head;
|
||||
dest->incoming_edge_head = edge;
|
||||
}
|
||||
|
||||
Block* HIRBuilder::AppendBlock() {
|
||||
Block* block = arena_->Alloc<Block>();
|
||||
block->arena = arena_;
|
||||
@@ -356,6 +410,7 @@ Block* HIRBuilder::AppendBlock() {
|
||||
}
|
||||
current_block_ = block;
|
||||
block->label_head = block->label_tail = NULL;
|
||||
block->incoming_edge_head = block->outgoing_edge_head = NULL;
|
||||
block->instr_head = block->instr_tail = NULL;
|
||||
return block;
|
||||
}
|
||||
@@ -420,6 +475,7 @@ Value* HIRBuilder::AllocValue(TypeName type) {
|
||||
value->def = NULL;
|
||||
value->use_head = NULL;
|
||||
value->last_use = NULL;
|
||||
value->local_slot = NULL;
|
||||
value->tag = NULL;
|
||||
value->reg = -1;
|
||||
return value;
|
||||
@@ -434,6 +490,7 @@ Value* HIRBuilder::CloneValue(Value* source) {
|
||||
value->def = NULL;
|
||||
value->use_head = NULL;
|
||||
value->last_use = NULL;
|
||||
value->local_slot = NULL;
|
||||
value->tag = NULL;
|
||||
value->reg = -1;
|
||||
return value;
|
||||
@@ -877,6 +934,28 @@ Value* HIRBuilder::LoadClock() {
|
||||
return i->dest;
|
||||
}
|
||||
|
||||
Value* HIRBuilder::AllocLocal(TypeName type) {
|
||||
Value* slot = AllocValue(type);
|
||||
locals_.push_back(slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
Value* HIRBuilder::LoadLocal(Value* slot) {
|
||||
Instr* i = AppendInstr(
|
||||
OPCODE_LOAD_LOCAL_info, 0,
|
||||
AllocValue(slot->type));
|
||||
i->set_src1(slot);
|
||||
i->src2.value = i->src3.value = NULL;
|
||||
return i->dest;
|
||||
}
|
||||
|
||||
void HIRBuilder::StoreLocal(Value* slot, Value* value) {
|
||||
Instr* i = AppendInstr(OPCODE_STORE_LOCAL_info, 0);
|
||||
i->set_src1(slot);
|
||||
i->set_src2(value);
|
||||
i->src3.value = NULL;
|
||||
}
|
||||
|
||||
Value* HIRBuilder::LoadContext(size_t offset, TypeName type) {
|
||||
Instr* i = AppendInstr(
|
||||
OPCODE_LOAD_CONTEXT_info, 0,
|
||||
|
||||
@@ -41,7 +41,12 @@ public:
|
||||
uint32_t attributes() const { return attributes_; }
|
||||
void set_attributes(uint32_t value) { attributes_ = value; }
|
||||
|
||||
std::vector<Value*>& locals() { return locals_; }
|
||||
|
||||
uint32_t max_value_ordinal() const { return next_value_ordinal_; }
|
||||
|
||||
Block* first_block() const { return block_head_; }
|
||||
Block* last_block() const { return block_tail_; }
|
||||
Block* current_block() const;
|
||||
Instr* last_instr() const;
|
||||
|
||||
@@ -50,12 +55,11 @@ public:
|
||||
void InsertLabel(Label* label, Instr* prev_instr);
|
||||
void ResetLabelTags();
|
||||
|
||||
void AddEdge(Block* src, Block* dest, uint32_t flags);
|
||||
|
||||
// static allocations:
|
||||
// Value* AllocStatic(size_t length);
|
||||
|
||||
// stack allocations:
|
||||
// Value* AllocLocal(TypeName type);
|
||||
|
||||
void Comment(const char* format, ...);
|
||||
|
||||
void Nop();
|
||||
@@ -116,6 +120,10 @@ public:
|
||||
|
||||
Value* LoadClock();
|
||||
|
||||
Value* AllocLocal(TypeName type);
|
||||
Value* LoadLocal(Value* slot);
|
||||
void StoreLocal(Value* slot, Value* value);
|
||||
|
||||
Value* LoadContext(size_t offset, TypeName type);
|
||||
void StoreContext(size_t offset, Value* value);
|
||||
|
||||
@@ -230,6 +238,8 @@ protected:
|
||||
uint32_t next_label_id_;
|
||||
uint32_t next_value_ordinal_;
|
||||
|
||||
std::vector<Value*> locals_;
|
||||
|
||||
Block* block_head_;
|
||||
Block* block_tail_;
|
||||
Block* current_block_;
|
||||
|
||||
@@ -61,6 +61,36 @@ bool Instr::Match(SignatureType dest_req,
|
||||
((src3_req == SIG_TYPE_IGNORE) || (src3_req == TO_SIG_TYPE(src3.value)));
|
||||
}
|
||||
|
||||
void Instr::MoveBefore(Instr* other) {
|
||||
if (next == other) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove from current location.
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
block->instr_head = next;
|
||||
}
|
||||
if (next) {
|
||||
next->prev = prev;
|
||||
} else {
|
||||
block->instr_tail = prev;
|
||||
}
|
||||
|
||||
// Insert into new location.
|
||||
block = other->block;
|
||||
next = other;
|
||||
prev = other->prev;
|
||||
other->prev = this;
|
||||
if (prev) {
|
||||
prev->next = this;
|
||||
}
|
||||
if (other == block->instr_head) {
|
||||
block->instr_head = this;
|
||||
}
|
||||
}
|
||||
|
||||
void Instr::Replace(const OpcodeInfo* opcode, uint16_t flags) {
|
||||
this->opcode = opcode;
|
||||
this->flags = flags;
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
SignatureType src2 = SIG_TYPE_X,
|
||||
SignatureType src3 = SIG_TYPE_X) const;
|
||||
|
||||
void MoveBefore(Instr* other);
|
||||
void Replace(const OpcodeInfo* opcode, uint16_t flags);
|
||||
void Remove();
|
||||
};
|
||||
|
||||
@@ -117,6 +117,9 @@ enum Opcode {
|
||||
|
||||
OPCODE_LOAD_CLOCK,
|
||||
|
||||
OPCODE_LOAD_LOCAL,
|
||||
OPCODE_STORE_LOCAL,
|
||||
|
||||
OPCODE_LOAD_CONTEXT,
|
||||
OPCODE_STORE_CONTEXT,
|
||||
|
||||
@@ -202,6 +205,7 @@ enum OpcodeFlags {
|
||||
OPCODE_FLAG_VOLATILE = (1 << 4),
|
||||
OPCODE_FLAG_IGNORE = (1 << 5),
|
||||
OPCODE_FLAG_HIDE = (1 << 6),
|
||||
OPCODE_FLAG_PAIRED_PREV = (1 << 7),
|
||||
};
|
||||
|
||||
enum OpcodeSignatureType {
|
||||
|
||||
@@ -182,6 +182,18 @@ DEFINE_OPCODE(
|
||||
OPCODE_SIG_V,
|
||||
0);
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_LOAD_LOCAL,
|
||||
"load_local",
|
||||
OPCODE_SIG_V_V,
|
||||
0);
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_STORE_LOCAL,
|
||||
"store_local",
|
||||
OPCODE_SIG_X_V_V,
|
||||
0);
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_LOAD_CONTEXT,
|
||||
"load_context",
|
||||
@@ -297,17 +309,17 @@ DEFINE_OPCODE(
|
||||
OPCODE_DID_CARRY,
|
||||
"did_carry",
|
||||
OPCODE_SIG_V_V,
|
||||
0);
|
||||
OPCODE_FLAG_PAIRED_PREV);
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_DID_OVERFLOW,
|
||||
"did_overflow",
|
||||
OPCODE_SIG_V_V,
|
||||
0);
|
||||
OPCODE_FLAG_PAIRED_PREV);
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_DID_SATURATE,
|
||||
"did_saturate",
|
||||
OPCODE_SIG_V_V,
|
||||
0);
|
||||
OPCODE_FLAG_PAIRED_PREV);
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_VECTOR_COMPARE_EQ,
|
||||
|
||||
@@ -42,6 +42,25 @@ static bool IsFloatType(TypeName type_name) {
|
||||
static bool IsVecType(TypeName type_name) {
|
||||
return type_name == VEC128_TYPE;
|
||||
}
|
||||
static size_t GetTypeSize(TypeName type_name) {
|
||||
switch (type_name) {
|
||||
case INT8_TYPE:
|
||||
return 1;
|
||||
case INT16_TYPE:
|
||||
return 2;
|
||||
case INT32_TYPE:
|
||||
return 4;
|
||||
case INT64_TYPE:
|
||||
return 8;
|
||||
case FLOAT32_TYPE:
|
||||
return 4;
|
||||
case FLOAT64_TYPE:
|
||||
return 8;
|
||||
default:
|
||||
case VEC128_TYPE:
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
enum ValueFlags {
|
||||
VALUE_IS_CONSTANT = (1 << 1),
|
||||
@@ -78,6 +97,7 @@ public:
|
||||
Use* use_head;
|
||||
// NOTE: for performance reasons this is not maintained during construction.
|
||||
Instr* last_use;
|
||||
Value* local_slot;
|
||||
|
||||
// TODO(benvanik): remove to shrink size.
|
||||
void* tag;
|
||||
|
||||
Reference in New Issue
Block a user