Oh my. Basic CFA/DFA, local variable support, misc fixes, etc.
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user