Oh my. Basic CFA/DFA, local variable support, misc fixes, etc.

This commit is contained in:
Ben Vanik
2014-02-02 00:33:57 -08:00
parent b29276e167
commit bca349b302
37 changed files with 3048 additions and 28 deletions

View File

@@ -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);

View File

@@ -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.");

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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_;
};

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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:

View 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;
}

View 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_

View 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];
}
}

View 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_

View File

@@ -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;
}

View File

@@ -28,6 +28,7 @@ public:
private:
void MakeNopRecursive(hir::Instr* i);
void ReplaceAssignment(hir::Instr* i);
bool CheckLocalUse(hir::Instr* i);
};

View File

@@ -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',
],

View 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;
}

View 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_

View File

@@ -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;

View File

@@ -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());

View File

@@ -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;

View File

@@ -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,

View File

@@ -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_;

View File

@@ -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;

View File

@@ -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();
};

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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;