JIT hackery. Not quite working.
This commit is contained in:
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()),
|
||||
allocator_(allocator),
|
||||
CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator) {
|
||||
xe_zero_struct(®_state_, sizeof(reg_state_));
|
||||
}
|
||||
|
||||
X64Emitter::~X64Emitter() {
|
||||
@@ -79,6 +80,17 @@ void* X64Emitter::Emplace(X64CodeCache* code_cache) {
|
||||
}
|
||||
|
||||
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.
|
||||
// Must be 16b aligned.
|
||||
// Windows is very strict about the form of this and the epilog:
|
||||
@@ -109,6 +121,11 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||
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.
|
||||
// The table will process sequences of instructions to (try to)
|
||||
// generate optimal code.
|
||||
@@ -129,3 +146,68 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||
|
||||
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/hir/value.h>
|
||||
|
||||
#include <third_party/xbyak/xbyak/xbyak.h>
|
||||
|
||||
XEDECLARECLASS2(alloy, hir, HIRBuilder);
|
||||
XEDECLARECLASS2(alloy, hir, Instr);
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
@@ -23,6 +26,11 @@ namespace x64 {
|
||||
class X64Backend;
|
||||
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.
|
||||
class XbyakAllocator : public Xbyak::Allocator {
|
||||
public:
|
||||
@@ -39,6 +47,91 @@ public:
|
||||
int Emit(hir::HIRBuilder* builder,
|
||||
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:
|
||||
void* Emplace(X64CodeCache* code_cache);
|
||||
int Emit(hir::HIRBuilder* builder);
|
||||
@@ -47,6 +140,16 @@ private:
|
||||
X64Backend* backend_;
|
||||
X64CodeCache* code_cache_;
|
||||
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) {
|
||||
typedef void(*call_t)(ThreadState* thread_state, uint64_t return_address);
|
||||
((call_t)machine_code_)(thread_state, return_address);
|
||||
//typedef void(*call_t)(ThreadState* thread_state, uint64_t 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user