JIT hackery. Not quite working.
This commit is contained in:
parent
a84ce633c9
commit
4609339c5a
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
* [LLVM R600 Tables](https://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/R600/R600Instructions.td)
|
* [LLVM R600 Tables](https://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/R600/R600Instructions.td)
|
||||||
** The opcode formats don't match, but the name->psuedo code is correct.
|
** The opcode formats don't match, but the name->psuedo code is correct.
|
||||||
|
* [xemit](https://github.com/gligli/libxemit/blob/master/xemitops.c)
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -39,6 +39,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) :
|
||||||
code_cache_(backend->code_cache()),
|
code_cache_(backend->code_cache()),
|
||||||
allocator_(allocator),
|
allocator_(allocator),
|
||||||
CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator) {
|
CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator) {
|
||||||
|
xe_zero_struct(®_state_, sizeof(reg_state_));
|
||||||
}
|
}
|
||||||
|
|
||||||
X64Emitter::~X64Emitter() {
|
X64Emitter::~X64Emitter() {
|
||||||
|
@ -79,6 +80,17 @@ void* X64Emitter::Emplace(X64CodeCache* code_cache) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64Emitter::Emit(HIRBuilder* builder) {
|
int X64Emitter::Emit(HIRBuilder* builder) {
|
||||||
|
// These are the registers we will not be using. All others are fare game.
|
||||||
|
const uint32_t reserved_regs =
|
||||||
|
GetRegBit(rax) |
|
||||||
|
GetRegBit(rcx) |
|
||||||
|
GetRegBit(rdx) |
|
||||||
|
GetRegBit(rsp) |
|
||||||
|
GetRegBit(rbp) |
|
||||||
|
GetRegBit(rsi) |
|
||||||
|
GetRegBit(rdi) |
|
||||||
|
GetRegBit(xmm0);
|
||||||
|
|
||||||
// Function prolog.
|
// Function prolog.
|
||||||
// Must be 16b aligned.
|
// Must be 16b aligned.
|
||||||
// Windows is very strict about the form of this and the epilog:
|
// Windows is very strict about the form of this and the epilog:
|
||||||
|
@ -109,6 +121,11 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||||
label = label->next;
|
label = label->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset reg allocation state.
|
||||||
|
// If we start keeping regs across blocks this needs to change.
|
||||||
|
// We mark a few active so that the allocator doesn't use them.
|
||||||
|
reg_state_.active_regs = reg_state_.live_regs = reserved_regs;
|
||||||
|
|
||||||
// Add instructions.
|
// Add instructions.
|
||||||
// The table will process sequences of instructions to (try to)
|
// The table will process sequences of instructions to (try to)
|
||||||
// generate optimal code.
|
// generate optimal code.
|
||||||
|
@ -129,3 +146,68 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void X64Emitter::FindFreeRegs(
|
||||||
|
Value* v0, uint32_t& v0_idx, uint32_t v0_flags) {
|
||||||
|
// If the value is already in a register, use it.
|
||||||
|
if (v0->reg != -1) {
|
||||||
|
// Already in a register. Mark active and return.
|
||||||
|
v0_idx = v0->reg;
|
||||||
|
reg_state_.active_regs |= 1 << v0_idx;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t avail_regs = 0;
|
||||||
|
if (IsIntType(v0->type)) {
|
||||||
|
if (v0_flags & REG_ABCD) {
|
||||||
|
avail_regs = B00001111;
|
||||||
|
} else {
|
||||||
|
avail_regs = 0xFFFF;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
avail_regs = 0xFFFF0000;
|
||||||
|
}
|
||||||
|
uint32_t free_regs = avail_regs & ~reg_state_.active_regs;
|
||||||
|
if (free_regs) {
|
||||||
|
// Just take one.
|
||||||
|
_BitScanReverse((DWORD*)&v0_idx, free_regs);
|
||||||
|
} else {
|
||||||
|
// Need to evict something.
|
||||||
|
XEASSERTALWAYS();
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_state_.active_regs |= 1 << v0_idx;
|
||||||
|
reg_state_.live_regs |= 1 << v0_idx;
|
||||||
|
v0->reg = v0_idx;
|
||||||
|
reg_state_.reg_values[v0_idx] = v0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void X64Emitter::FindFreeRegs(
|
||||||
|
Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
|
Value* v1, uint32_t& v1_idx, uint32_t v1_flags) {
|
||||||
|
// TODO(benvanik): support REG_DEST reuse/etc.
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void X64Emitter::FindFreeRegs(
|
||||||
|
Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
|
Value* v1, uint32_t& v1_idx, uint32_t v1_flags,
|
||||||
|
Value* v2, uint32_t& v2_idx, uint32_t v2_flags) {
|
||||||
|
// TODO(benvanik): support REG_DEST reuse/etc.
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
FindFreeRegs(v2, v2_idx, v2_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void X64Emitter::FindFreeRegs(
|
||||||
|
Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
|
Value* v1, uint32_t& v1_idx, uint32_t v1_flags,
|
||||||
|
Value* v2, uint32_t& v2_idx, uint32_t v2_flags,
|
||||||
|
Value* v3, uint32_t& v3_idx, uint32_t v3_flags) {
|
||||||
|
// TODO(benvanik): support REG_DEST reuse/etc.
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
FindFreeRegs(v2, v2_idx, v2_flags);
|
||||||
|
FindFreeRegs(v3, v3_idx, v3_flags);
|
||||||
|
}
|
||||||
|
|
|
@ -12,9 +12,12 @@
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <alloy/core.h>
|
||||||
|
|
||||||
|
#include <alloy/hir/value.h>
|
||||||
|
|
||||||
#include <third_party/xbyak/xbyak/xbyak.h>
|
#include <third_party/xbyak/xbyak/xbyak.h>
|
||||||
|
|
||||||
XEDECLARECLASS2(alloy, hir, HIRBuilder);
|
XEDECLARECLASS2(alloy, hir, HIRBuilder);
|
||||||
|
XEDECLARECLASS2(alloy, hir, Instr);
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -23,6 +26,11 @@ namespace x64 {
|
||||||
class X64Backend;
|
class X64Backend;
|
||||||
class X64CodeCache;
|
class X64CodeCache;
|
||||||
|
|
||||||
|
enum RegisterFlags {
|
||||||
|
REG_DEST = (1 << 0),
|
||||||
|
REG_ABCD = (1 << 1),
|
||||||
|
};
|
||||||
|
|
||||||
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
|
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
|
||||||
class XbyakAllocator : public Xbyak::Allocator {
|
class XbyakAllocator : public Xbyak::Allocator {
|
||||||
public:
|
public:
|
||||||
|
@ -39,6 +47,91 @@ public:
|
||||||
int Emit(hir::HIRBuilder* builder,
|
int Emit(hir::HIRBuilder* builder,
|
||||||
void*& out_code_address, size_t& out_code_size);
|
void*& out_code_address, size_t& out_code_size);
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename V0>
|
||||||
|
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags) {
|
||||||
|
uint32_t v0_idx;
|
||||||
|
FindFreeRegs(v0, v0_idx, r0_flags);
|
||||||
|
SetupReg(v0_idx, r0);
|
||||||
|
}
|
||||||
|
template<typename V0, typename V1>
|
||||||
|
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags,
|
||||||
|
hir::Value* v1, V1& r1, uint32_t r1_flags) {
|
||||||
|
uint32_t v0_idx, v1_idx;
|
||||||
|
FindFreeRegs(v0, v0_idx, r0_flags,
|
||||||
|
v1, v1_idx, r1_flags);
|
||||||
|
SetupReg(v0_idx, r0);
|
||||||
|
SetupReg(v1_idx, r1);
|
||||||
|
}
|
||||||
|
template<typename V0, typename V1, typename V2>
|
||||||
|
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags,
|
||||||
|
hir::Value* v1, V1& r1, uint32_t r1_flags,
|
||||||
|
hir::Value* v2, V2& r2, uint32_t r2_flags) {
|
||||||
|
uint32_t v0_idx, v1_idx, v2_idx;
|
||||||
|
FindFreeRegs(v0, v0_idx, r0_flags,
|
||||||
|
v1, v1_idx, r1_flags,
|
||||||
|
v2, v2_idx, r2_flags);
|
||||||
|
SetupReg(v0_idx, r0);
|
||||||
|
SetupReg(v1_idx, r1);
|
||||||
|
SetupReg(v2_idx, r2);
|
||||||
|
}
|
||||||
|
template<typename V0, typename V1, typename V2, typename V3>
|
||||||
|
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags,
|
||||||
|
hir::Value* v1, V1& r1, uint32_t r1_flags,
|
||||||
|
hir::Value* v2, V2& r2, uint32_t r2_flags,
|
||||||
|
hir::Value* v3, V3& r3, uint32_t r3_flags) {
|
||||||
|
uint32_t v0_idx, v1_idx, v2_idx, v3_idx;
|
||||||
|
FindFreeRegs(v0, v0_idx, r0_flags,
|
||||||
|
v1, v1_idx, r1_flags,
|
||||||
|
v2, v2_idx, r2_flags,
|
||||||
|
v3, v3_idx, r3_flags);
|
||||||
|
SetupReg(v0_idx, r0);
|
||||||
|
SetupReg(v1_idx, r1);
|
||||||
|
SetupReg(v2_idx, r2);
|
||||||
|
SetupReg(v3_idx, r3);
|
||||||
|
}
|
||||||
|
template<typename V0>
|
||||||
|
void EndOp(V0& r0) {
|
||||||
|
reg_state_.active_regs = reg_state_.active_regs ^ GetRegBit(r0);
|
||||||
|
}
|
||||||
|
template<typename V0, typename V1>
|
||||||
|
void EndOp(V0& r0, V1& r1) {
|
||||||
|
reg_state_.active_regs = reg_state_.active_regs ^ (
|
||||||
|
GetRegBit(r0) | GetRegBit(r1));
|
||||||
|
}
|
||||||
|
template<typename V0, typename V1, typename V2>
|
||||||
|
void EndOp(V0& r0, V1& r1, V2& r2) {
|
||||||
|
reg_state_.active_regs = reg_state_.active_regs ^ (
|
||||||
|
GetRegBit(r0) | GetRegBit(r1) | GetRegBit(r2));
|
||||||
|
}
|
||||||
|
template<typename V0, typename V1, typename V2, typename V3>
|
||||||
|
void EndOp(V0& r0, V1& r1, V2& r2, V3& r3) {
|
||||||
|
reg_state_.active_regs = reg_state_.active_regs ^ (
|
||||||
|
GetRegBit(r0) | GetRegBit(r1) | GetRegBit(r2) | GetRegBit(r3));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags);
|
||||||
|
void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
|
hir::Value* v1, uint32_t& v1_idx, uint32_t v1_flags);
|
||||||
|
void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
|
hir::Value* v1, uint32_t& v1_idx, uint32_t v1_flags,
|
||||||
|
hir::Value* v2, uint32_t& v2_idx, uint32_t v2_flags);
|
||||||
|
void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
|
hir::Value* v1, uint32_t& v1_idx, uint32_t v1_flags,
|
||||||
|
hir::Value* v2, uint32_t& v2_idx, uint32_t v2_flags,
|
||||||
|
hir::Value* v3, uint32_t& v3_idx, uint32_t v3_flags);
|
||||||
|
|
||||||
|
static void SetupReg(uint32_t idx, Xbyak::Reg8& r) { r = Xbyak::Reg8(idx); }
|
||||||
|
static void SetupReg(uint32_t idx, Xbyak::Reg16& r) { r = Xbyak::Reg16(idx); }
|
||||||
|
static void SetupReg(uint32_t idx, Xbyak::Reg32& r) { r = Xbyak::Reg32(idx); }
|
||||||
|
static void SetupReg(uint32_t idx, Xbyak::Reg64& r) { r = Xbyak::Reg64(idx); }
|
||||||
|
static void SetupReg(uint32_t idx, Xbyak::Xmm& r) { r = Xbyak::Xmm(idx - 16); }
|
||||||
|
static uint32_t GetRegBit(const Xbyak::Reg8& r) { return 1 << r.getIdx(); }
|
||||||
|
static uint32_t GetRegBit(const Xbyak::Reg16& r) { return 1 << r.getIdx(); }
|
||||||
|
static uint32_t GetRegBit(const Xbyak::Reg32& r) { return 1 << r.getIdx(); }
|
||||||
|
static uint32_t GetRegBit(const Xbyak::Reg64& r) { return 1 << r.getIdx(); }
|
||||||
|
static uint32_t GetRegBit(const Xbyak::Xmm& r) { return 1 << (16 + r.getIdx()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void* Emplace(X64CodeCache* code_cache);
|
void* Emplace(X64CodeCache* code_cache);
|
||||||
int Emit(hir::HIRBuilder* builder);
|
int Emit(hir::HIRBuilder* builder);
|
||||||
|
@ -47,6 +140,16 @@ private:
|
||||||
X64Backend* backend_;
|
X64Backend* backend_;
|
||||||
X64CodeCache* code_cache_;
|
X64CodeCache* code_cache_;
|
||||||
XbyakAllocator* allocator_;
|
XbyakAllocator* allocator_;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
// Registers currently active within a begin/end op block. These
|
||||||
|
// cannot be reused.
|
||||||
|
uint32_t active_regs;
|
||||||
|
// Registers with values in them.
|
||||||
|
uint32_t live_regs;
|
||||||
|
// Current register values.
|
||||||
|
hir::Value* reg_values[32];
|
||||||
|
} reg_state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,9 @@ int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
||||||
typedef void(*call_t)(ThreadState* thread_state, uint64_t return_address);
|
//typedef void(*call_t)(ThreadState* thread_state, uint64_t return_address);
|
||||||
((call_t)machine_code_)(thread_state, return_address);
|
//((call_t)machine_code_)(thread_state, return_address);
|
||||||
|
typedef void(*call_t)(ThreadState* thread_state, uint8_t* membase);
|
||||||
|
((call_t)machine_code_)(thread_state, thread_state->memory()->membase());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <alloy/compiler/passes/context_promotion_pass.h>
|
#include <alloy/compiler/passes/context_promotion_pass.h>
|
||||||
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
|
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
|
||||||
#include <alloy/compiler/passes/finalization_pass.h>
|
#include <alloy/compiler/passes/finalization_pass.h>
|
||||||
#include <alloy/compiler/passes/register_allocation_pass.h>
|
|
||||||
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
||||||
#include <alloy/compiler/passes/simplification_pass.h>
|
#include <alloy/compiler/passes/simplification_pass.h>
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,15 @@ int FinalizationPass::Run(HIRBuilder* builder) {
|
||||||
label = label->next;
|
label = label->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ?
|
// ? remove useless jumps?
|
||||||
|
|
||||||
|
// Renumber all instructions to make liveness tracking easier.
|
||||||
|
uint32_t n = 0;
|
||||||
|
auto instr = block->instr_head;
|
||||||
|
while (instr) {
|
||||||
|
instr->ordinal = n++;
|
||||||
|
instr = instr->next;
|
||||||
|
}
|
||||||
|
|
||||||
block = block->next;
|
block = block->next;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* 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/register_allocation_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;
|
|
||||||
|
|
||||||
|
|
||||||
RegisterAllocationPass::RegisterAllocationPass(Backend* backend) :
|
|
||||||
CompilerPass() {
|
|
||||||
// TODO(benvanik): query backend info (register layout, etc).
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterAllocationPass::~RegisterAllocationPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
int RegisterAllocationPass::Run(HIRBuilder* builder) {
|
|
||||||
// Run through each block and give each dest value a register.
|
|
||||||
// This pass is currently really dumb, and will over spill and other badness.
|
|
||||||
|
|
||||||
auto block = builder->first_block();
|
|
||||||
while (block) {
|
|
||||||
if (ProcessBlock(block)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
block = block->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int RegisterAllocationPass::ProcessBlock(Block* block) {
|
|
||||||
//
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* 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_REGISTER_ALLOCATION_PASS_H_
|
|
||||||
#define ALLOY_COMPILER_PASSES_REGISTER_ALLOCATION_PASS_H_
|
|
||||||
|
|
||||||
#include <alloy/compiler/compiler_pass.h>
|
|
||||||
|
|
||||||
XEDECLARECLASS2(alloy, backend, Backend);
|
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
|
||||||
namespace compiler {
|
|
||||||
namespace passes {
|
|
||||||
|
|
||||||
|
|
||||||
class RegisterAllocationPass : public CompilerPass {
|
|
||||||
public:
|
|
||||||
RegisterAllocationPass(backend::Backend* backend);
|
|
||||||
virtual ~RegisterAllocationPass();
|
|
||||||
|
|
||||||
virtual int Run(hir::HIRBuilder* builder);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int ProcessBlock(hir::Block* block);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace passes
|
|
||||||
} // namespace compiler
|
|
||||||
} // namespace alloy
|
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_COMPILER_PASSES_REGISTER_ALLOCATION_PASS_H_
|
|
|
@ -9,8 +9,6 @@
|
||||||
'dead_code_elimination_pass.h',
|
'dead_code_elimination_pass.h',
|
||||||
'finalization_pass.cc',
|
'finalization_pass.cc',
|
||||||
'finalization_pass.h',
|
'finalization_pass.h',
|
||||||
'register_allocation_pass.cc',
|
|
||||||
'register_allocation_pass.h',
|
|
||||||
#'dead_store_elimination_pass.cc',
|
#'dead_store_elimination_pass.cc',
|
||||||
#'dead_store_elimination_pass.h',
|
#'dead_store_elimination_pass.h',
|
||||||
'simplification_pass.cc',
|
'simplification_pass.cc',
|
||||||
|
|
|
@ -50,9 +50,6 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) :
|
||||||
//compiler_->AddPass(new passes::DeadStoreEliminationPass());
|
//compiler_->AddPass(new passes::DeadStoreEliminationPass());
|
||||||
compiler_->AddPass(new passes::DeadCodeEliminationPass());
|
compiler_->AddPass(new passes::DeadCodeEliminationPass());
|
||||||
|
|
||||||
// After register allocation instructions should not be added/removed.
|
|
||||||
compiler_->AddPass(new passes::RegisterAllocationPass(backend));
|
|
||||||
|
|
||||||
// Must come last. The HIR is not really HIR after this.
|
// Must come last. The HIR is not really HIR after this.
|
||||||
compiler_->AddPass(new passes::FinalizationPass());
|
compiler_->AddPass(new passes::FinalizationPass());
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,6 +420,7 @@ Value* HIRBuilder::AllocValue(TypeName type) {
|
||||||
value->def = NULL;
|
value->def = NULL;
|
||||||
value->use_head = NULL;
|
value->use_head = NULL;
|
||||||
value->tag = NULL;
|
value->tag = NULL;
|
||||||
|
value->reg = -1;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,6 +433,7 @@ Value* HIRBuilder::CloneValue(Value* source) {
|
||||||
value->def = NULL;
|
value->def = NULL;
|
||||||
value->use_head = NULL;
|
value->use_head = NULL;
|
||||||
value->tag = NULL;
|
value->tag = NULL;
|
||||||
|
value->reg = -1;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,19 @@ void Instr::set_src3(Value* value) {
|
||||||
src3_use = value ? value->AddUse(block->arena, this) : NULL;
|
src3_use = value ? value->AddUse(block->arena, this) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Instr::Match(SignatureType dest_req,
|
||||||
|
SignatureType src1_req,
|
||||||
|
SignatureType src2_req,
|
||||||
|
SignatureType src3_req) const {
|
||||||
|
#define TO_SIG_TYPE(v) \
|
||||||
|
(v ? (v->IsConstant() ? SignatureType((v->type + 1) | SIG_TYPE_C) : SignatureType(v->type + 1)) : SIG_TYPE_X)
|
||||||
|
return
|
||||||
|
((dest_req == SIG_TYPE_IGNORE) || (dest_req == TO_SIG_TYPE(dest))) &&
|
||||||
|
((src1_req == SIG_TYPE_IGNORE) || (src1_req == TO_SIG_TYPE(src1.value))) &&
|
||||||
|
((src2_req == SIG_TYPE_IGNORE) || (src2_req == TO_SIG_TYPE(src2.value))) &&
|
||||||
|
((src3_req == SIG_TYPE_IGNORE) || (src3_req == TO_SIG_TYPE(src3.value)));
|
||||||
|
}
|
||||||
|
|
||||||
void Instr::Replace(const OpcodeInfo* opcode, uint16_t flags) {
|
void Instr::Replace(const OpcodeInfo* opcode, uint16_t flags) {
|
||||||
this->opcode = opcode;
|
this->opcode = opcode;
|
||||||
this->flags = flags;
|
this->flags = flags;
|
||||||
|
|
|
@ -24,6 +24,25 @@ namespace hir {
|
||||||
class Block;
|
class Block;
|
||||||
class Label;
|
class Label;
|
||||||
|
|
||||||
|
enum SignatureType {
|
||||||
|
SIG_TYPE_X = 0,
|
||||||
|
SIG_TYPE_I8 = 1,
|
||||||
|
SIG_TYPE_I16 = 2,
|
||||||
|
SIG_TYPE_I32 = 3,
|
||||||
|
SIG_TYPE_I64 = 4,
|
||||||
|
SIG_TYPE_F32 = 5,
|
||||||
|
SIG_TYPE_F64 = 6,
|
||||||
|
SIG_TYPE_V128 = 7,
|
||||||
|
SIG_TYPE_C = (1 << 3),
|
||||||
|
SIG_TYPE_I8C = SIG_TYPE_C | SIG_TYPE_I8,
|
||||||
|
SIG_TYPE_I16C = SIG_TYPE_C | SIG_TYPE_I16,
|
||||||
|
SIG_TYPE_I32C = SIG_TYPE_C | SIG_TYPE_I32,
|
||||||
|
SIG_TYPE_I64C = SIG_TYPE_C | SIG_TYPE_I64,
|
||||||
|
SIG_TYPE_F32C = SIG_TYPE_C | SIG_TYPE_F32,
|
||||||
|
SIG_TYPE_F64C = SIG_TYPE_C | SIG_TYPE_F64,
|
||||||
|
SIG_TYPE_V128C = SIG_TYPE_C | SIG_TYPE_V128,
|
||||||
|
SIG_TYPE_IGNORE = 0xFF,
|
||||||
|
};
|
||||||
|
|
||||||
class Instr {
|
class Instr {
|
||||||
public:
|
public:
|
||||||
|
@ -33,6 +52,7 @@ public:
|
||||||
|
|
||||||
const OpcodeInfo* opcode;
|
const OpcodeInfo* opcode;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
|
uint16_t ordinal;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
runtime::FunctionInfo* symbol_info;
|
runtime::FunctionInfo* symbol_info;
|
||||||
|
@ -54,6 +74,11 @@ public:
|
||||||
void set_src2(Value* value);
|
void set_src2(Value* value);
|
||||||
void set_src3(Value* value);
|
void set_src3(Value* value);
|
||||||
|
|
||||||
|
bool Match(SignatureType dest = SIG_TYPE_X,
|
||||||
|
SignatureType src1 = SIG_TYPE_X,
|
||||||
|
SignatureType src2 = SIG_TYPE_X,
|
||||||
|
SignatureType src3 = SIG_TYPE_X) const;
|
||||||
|
|
||||||
void Replace(const OpcodeInfo* opcode, uint16_t flags);
|
void Replace(const OpcodeInfo* opcode, uint16_t flags);
|
||||||
void Remove();
|
void Remove();
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,23 @@ void Value::RemoveUse(Use* use) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Value::AsUint32() {
|
||||||
|
XEASSERT(IsConstant());
|
||||||
|
switch (type) {
|
||||||
|
case INT8_TYPE:
|
||||||
|
return constant.i8;
|
||||||
|
case INT16_TYPE:
|
||||||
|
return constant.i16;
|
||||||
|
case INT32_TYPE:
|
||||||
|
return constant.i32;
|
||||||
|
case INT64_TYPE:
|
||||||
|
return (uint32_t)constant.i64;
|
||||||
|
default:
|
||||||
|
XEASSERTALWAYS();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t Value::AsUint64() {
|
uint64_t Value::AsUint64() {
|
||||||
XEASSERT(IsConstant());
|
XEASSERT(IsConstant());
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
@ -21,17 +21,22 @@ class Instr;
|
||||||
|
|
||||||
|
|
||||||
enum TypeName {
|
enum TypeName {
|
||||||
INT8_TYPE,
|
// Many tables rely on this ordering.
|
||||||
INT16_TYPE,
|
INT8_TYPE = 0,
|
||||||
INT32_TYPE,
|
INT16_TYPE = 1,
|
||||||
INT64_TYPE,
|
INT32_TYPE = 2,
|
||||||
FLOAT32_TYPE,
|
INT64_TYPE = 3,
|
||||||
FLOAT64_TYPE,
|
FLOAT32_TYPE = 4,
|
||||||
VEC128_TYPE,
|
FLOAT64_TYPE = 5,
|
||||||
|
VEC128_TYPE = 6,
|
||||||
|
|
||||||
MAX_TYPENAME,
|
MAX_TYPENAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool IsIntType(TypeName type_name) {
|
||||||
|
return type_name < 4;
|
||||||
|
}
|
||||||
|
|
||||||
enum ValueFlags {
|
enum ValueFlags {
|
||||||
VALUE_IS_CONSTANT = (1 << 1),
|
VALUE_IS_CONSTANT = (1 << 1),
|
||||||
};
|
};
|
||||||
|
@ -59,7 +64,7 @@ public:
|
||||||
TypeName type;
|
TypeName type;
|
||||||
|
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t reserved;
|
uint32_t reg;
|
||||||
ConstantValue constant;
|
ConstantValue constant;
|
||||||
|
|
||||||
Instr* def;
|
Instr* def;
|
||||||
|
@ -174,6 +179,7 @@ public:
|
||||||
(other->flags & VALUE_IS_CONSTANT) &&
|
(other->flags & VALUE_IS_CONSTANT) &&
|
||||||
constant.i64 != other->constant.i64;
|
constant.i64 != other->constant.i64;
|
||||||
}
|
}
|
||||||
|
uint32_t AsUint32();
|
||||||
uint64_t AsUint64();
|
uint64_t AsUint64();
|
||||||
|
|
||||||
void Cast(TypeName target_type);
|
void Cast(TypeName target_type);
|
||||||
|
|
Loading…
Reference in New Issue