mirror of
https://github.com/open-goal/jak-project
synced 2026-09-12 20:56:09 -04:00
542 lines
17 KiB
C++
542 lines
17 KiB
C++
/*!
|
|
* @file x86_Emitter_Code.cpp
|
|
* Emitter for converting IR and static objects into GOAL object files for x86 - Code Generation
|
|
* from IR
|
|
*/
|
|
|
|
#include "x86_Emitter.h"
|
|
#include "IGen.h"
|
|
|
|
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
// IR TRANSLATION
|
|
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
/*!
|
|
* Move a constant into a register
|
|
*/
|
|
void x86_Emitter::do_constvar(IR_LoadInteger& cv) {
|
|
auto gpr = gpr_id(*cv.value);
|
|
|
|
// todo zero
|
|
|
|
if (cv.s_value == 0) {
|
|
emit_instr(IGen::xor_zero_gpr(gpr));
|
|
} else if (cv.s_value > 0) {
|
|
if (cv.us_value < UINT32_MAX) {
|
|
emit_instr(IGen::mov_gpr64_u32(gpr, cv.us_value));
|
|
} else {
|
|
// need a real 64 bit load
|
|
emit_instr(IGen::mov_gpr64_u64(gpr, cv.us_value));
|
|
}
|
|
} else {
|
|
if (cv.s_value >= INT32_MIN) {
|
|
emit_instr(IGen::mov_gpr64_s32(gpr, cv.s_value));
|
|
} else {
|
|
// need a real 64 bit load
|
|
emit_instr(IGen::mov_gpr64_u64(gpr, cv.us_value));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Return a variable
|
|
*/
|
|
void x86_Emitter::do_return(IR_Return& ret) {
|
|
// we need to insert a null instruction here so we can have a jump target to here, even if we
|
|
// don't emit any real instructions
|
|
emit_instr(IGen::null());
|
|
|
|
// if we aren't None, we should actually return something
|
|
if (!std::dynamic_pointer_cast<NonePlace>(ret.value)) {
|
|
emit_mov_gpr64_gpr64_or_null(ret.dest, ret.value);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Set one reg equal to another. Can handle XMM/GPR sets.
|
|
* Currently all XMM sets are treated as floats.
|
|
*/
|
|
void x86_Emitter::do_set(IR_Set& set) {
|
|
auto dreg = get_ca(*set.dest).reg_id;
|
|
auto sreg = get_ca(*set.src).reg_id;
|
|
|
|
if (dreg < 16 && sreg < 16) {
|
|
emit_mov_gpr64_gpr64_or_null(set.dest, set.src);
|
|
} else if (dreg >= 16 && sreg >= 16) {
|
|
// emit_instr(IGen::mov_xmm32_xmm32(xmm_id(*set.dest), xmm_id(*set.src)));
|
|
emit_mov_xmm32_xmm32_or_null(set.dest, set.src);
|
|
} else if (dreg < 16 && sreg >= 16) {
|
|
emit_instr(IGen::movd_gpr32_xmm32(gpr_id(*set.dest), xmm_id(*set.src)));
|
|
} else if (dreg >= 16 && sreg < 16) {
|
|
emit_instr(IGen::movd_xmm32_gpr32(xmm_id(*set.dest), gpr_id(*set.src)));
|
|
} else {
|
|
throw std::runtime_error("invalid set - mixed operands");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* A jump to a label. Can be forward or backward.
|
|
*/
|
|
void x86_Emitter::do_goto_label(IR_Goto_Label& go_to) {
|
|
assert(go_to.resolved); // make sure the label is actually valid...
|
|
emit_instr(IGen::jmp_32());
|
|
|
|
// create a record.
|
|
StaticJumpRecord rec;
|
|
rec.resolved = false;
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.offset_into_instr = 1;
|
|
rec.type = SIGNED_32_RIP;
|
|
rec.target_ir_idx = go_to.label->idx;
|
|
rec.function_offset = function_offset;
|
|
static_jumps[current_seg].push_back(rec);
|
|
}
|
|
|
|
/*!
|
|
* The various kinds of conditional branches
|
|
*/
|
|
void x86_Emitter::do_cond_branch(IR_ConditionalBranch& br) {
|
|
switch (br.cond.kind) {
|
|
case EQUAL_64:
|
|
do_cmp_branch(br, IGen::je_32());
|
|
break;
|
|
case NOT_EQUAL_64:
|
|
do_cmp_branch(br, IGen::jne_32());
|
|
break;
|
|
case LEQ_64:
|
|
if (br.cond.is_signed) {
|
|
do_cmp_branch(br, IGen::jle_32());
|
|
} else {
|
|
do_cmp_branch(br, IGen::jbe_32());
|
|
}
|
|
break;
|
|
case GEQ_64:
|
|
if (br.cond.is_signed) {
|
|
do_cmp_branch(br, IGen::jge_32());
|
|
} else {
|
|
do_cmp_branch(br, IGen::jae_32());
|
|
}
|
|
break;
|
|
case LT_64:
|
|
if (br.cond.is_signed) {
|
|
do_cmp_branch(br, IGen::jl_32());
|
|
} else {
|
|
do_cmp_branch(br, IGen::jb_32());
|
|
}
|
|
break;
|
|
case GT_64:
|
|
if (br.cond.is_signed) {
|
|
do_cmp_branch(br, IGen::jg_32());
|
|
} else {
|
|
do_cmp_branch(br, IGen::ja_32());
|
|
}
|
|
break;
|
|
default:
|
|
throw std::runtime_error("unknown branch type in do_cond_branch");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Set the value of a symbol
|
|
*/
|
|
void x86_Emitter::do_set_symbol(IR_SetSymbolValue& set_symbol) {
|
|
auto dst_as_sym = std::dynamic_pointer_cast<SymbolPlace>(set_symbol.dest);
|
|
assert(dst_as_sym);
|
|
emit_instr(IGen::store32_r64off32s_gpr32(ST_REG, 0x0badbeef, gpr_id(*set_symbol.value)));
|
|
SymbolMemAccessRec rec;
|
|
rec.offset = instructions[current_seg].back().offset_of_disp();
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.seg = current_seg;
|
|
symbol_mem_access_recs[current_seg][dst_as_sym->name].push_back(rec);
|
|
}
|
|
|
|
/*!
|
|
* Get the value of a symbol
|
|
*/
|
|
void x86_Emitter::do_get_symbol(IR_GetSymbolValue& get_symbol) {
|
|
emit_instr(IGen::load32_gpr32sz_r64off32s(gpr_id(*get_symbol.dest), 0xbad0beef, ST_REG,
|
|
get_symbol.sext));
|
|
SymbolMemAccessRec rec;
|
|
rec.offset = instructions[current_seg].back().offset_of_disp();
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.seg = current_seg;
|
|
symbol_mem_access_recs[current_seg][get_symbol.symbol->name].push_back(rec);
|
|
}
|
|
|
|
/*!
|
|
* Call a function
|
|
*/
|
|
void x86_Emitter::do_function_call(IR_FunctionCall& fcall) {
|
|
// currently the function call pointer has the GOAL offset address.
|
|
// we need to change this to actually do a function call.
|
|
|
|
emit_mov_gpr64_gpr64_or_null(fcall.func_call, fcall.func_in);
|
|
|
|
// make sure the function address reg is correct
|
|
auto freg = get_ca(*fcall.func_call);
|
|
assert(freg.kind == REGISTER);
|
|
assert(freg.reg_id == T9_REG);
|
|
|
|
// do the add
|
|
emit_instr(IGen::add_gpr64_gpr64(freg.reg_id, OFF_REG));
|
|
|
|
// now do the call
|
|
emit_instr(IGen::call_r64(freg.reg_id));
|
|
}
|
|
|
|
/*!
|
|
* Get the address of a static variable
|
|
* TODO - can the size of this be decreased?
|
|
*/
|
|
void x86_Emitter::do_static_var_addr(IR_StaticVarAddr& var_addr) {
|
|
// get the offset:
|
|
StaticVarAddrRecord rec;
|
|
load_u64_to_gpr(0xbadcafebadcafe, var_addr.dest);
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.offset_into_instr = instructions[current_seg].back().offset_of_imm();
|
|
rec.resolved = false;
|
|
rec.place = std::dynamic_pointer_cast<StaticPlace>(var_addr.src);
|
|
rec.current_rbp_instr_idx = current_rbp_instr_idx;
|
|
rec.size = 8;
|
|
assert(rec.place);
|
|
static_var_addr_recs[current_seg].push_back(rec);
|
|
|
|
// and add the base
|
|
emit_instr(IGen::add_gpr64_gpr64(gpr_id(*var_addr.dest), RBP));
|
|
emit_instr(IGen::sub_gpr64_gpr64(gpr_id(*var_addr.dest), OFF_REG));
|
|
}
|
|
|
|
/*!
|
|
* Load a static variable (32 bits)
|
|
* TODO - generalize this (and maybe the IR) for other sizes
|
|
*/
|
|
void x86_Emitter::do_static_var_32(IR_StaticVar32& var_addr) {
|
|
auto ca = get_ca(*var_addr.dest);
|
|
if (ca.reg_id >= 16) {
|
|
emit_instr(IGen::load32_xmm32_r64off32s(xmm_id(*var_addr.dest), RBP, 0xcafecafe));
|
|
StaticVarAddrRecord rec;
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.offset_into_instr = instructions[current_seg].back().offset_of_disp();
|
|
rec.resolved = false;
|
|
rec.place = std::dynamic_pointer_cast<StaticPlace>(var_addr.src);
|
|
rec.current_rbp_instr_idx = current_rbp_instr_idx;
|
|
rec.size = 4;
|
|
assert(rec.place);
|
|
static_var_addr_recs[current_seg].push_back(rec);
|
|
} else {
|
|
emit_instr(IGen::load32_gpr32sz_r64off32s(gpr_id(*var_addr.dest), 0xcafecafe, RBP,
|
|
var_addr.src->type.type->load_signed));
|
|
StaticVarAddrRecord rec;
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.offset_into_instr = instructions[current_seg].back().offset_of_disp();
|
|
rec.resolved = false;
|
|
rec.place = std::dynamic_pointer_cast<StaticPlace>(var_addr.src);
|
|
rec.current_rbp_instr_idx = current_rbp_instr_idx;
|
|
rec.size = 4;
|
|
assert(rec.place);
|
|
static_var_addr_recs[current_seg].push_back(rec);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Get the address of a function
|
|
*/
|
|
void x86_Emitter::do_function_addr(IR_FunctionAddr& func_addr) {
|
|
FuncAddrRecord rec;
|
|
load_u64_to_gpr(0xcafebadcafebad, func_addr.dest);
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.offset_into_instr = instructions[current_seg].back().offset_of_imm();
|
|
rec.place = std::dynamic_pointer_cast<LambdaPlace>(func_addr.src);
|
|
rec.resolve = false;
|
|
rec.current_rbp_instr_idx = current_rbp_instr_idx;
|
|
assert(rec.place);
|
|
func_addr_recs[current_seg].push_back(rec);
|
|
emit_instr(IGen::add_gpr64_gpr64(gpr_id(*func_addr.dest), RBP));
|
|
emit_instr(IGen::sub_gpr64_gpr64(gpr_id(*func_addr.dest), OFF_REG));
|
|
}
|
|
|
|
/*!
|
|
* Do math on integers
|
|
*/
|
|
void x86_Emitter::do_integer_math(IR_IntegerMath& math) {
|
|
switch (math.math_kind) {
|
|
case ADD_64:
|
|
emit_instr(IGen::add_gpr64_gpr64(gpr_id(*math.d), gpr_id(*math.a0)));
|
|
break;
|
|
|
|
case SUB_64:
|
|
emit_instr(IGen::sub_gpr64_gpr64(gpr_id(*math.d), gpr_id(*math.a0)));
|
|
break;
|
|
|
|
case IMUL_32:
|
|
emit_instr(IGen::imul_gpr32_gpr32(gpr_id(*math.d), gpr_id(*math.a0)));
|
|
emit_instr(IGen::movsx_r64_r32(gpr_id(*math.d), gpr_id(*math.d)));
|
|
break;
|
|
|
|
case IDIV_32:
|
|
emit_instr(IGen::cdq());
|
|
emit_instr(IGen::idiv_gpr32(gpr_id(*math.a0)));
|
|
emit_instr(IGen::movsx_r64_r32(gpr_id(*math.d), RAX));
|
|
break;
|
|
|
|
case IMOD_32:
|
|
emit_instr(IGen::cdq());
|
|
emit_instr(IGen::idiv_gpr32(gpr_id(*math.a0)));
|
|
emit_instr(IGen::movsx_r64_r32(gpr_id(*math.d), RDX));
|
|
break;
|
|
|
|
case SHLV_64:
|
|
emit_instr(IGen::shl_gpr64_cl(gpr_id(*math.d)));
|
|
break;
|
|
case SHRV_64:
|
|
emit_instr(IGen::shr_gpr64_cl(gpr_id(*math.d)));
|
|
break;
|
|
case SARV_64:
|
|
emit_instr(IGen::sar_gpr64_cl(gpr_id(*math.d)));
|
|
break;
|
|
|
|
case SHL_64:
|
|
emit_instr(IGen::shl_gpr64_u8(gpr_id(*math.d), math.sa));
|
|
break;
|
|
case SHR_64:
|
|
emit_instr(IGen::shr_gpr64_u8(gpr_id(*math.d), math.sa));
|
|
break;
|
|
case SAR_64:
|
|
emit_instr(IGen::sar_gpr64_u8(gpr_id(*math.d), math.sa));
|
|
break;
|
|
|
|
case OR_64:
|
|
emit_instr(IGen::or_gpr64_gpr64(gpr_id(*math.d), gpr_id(*math.a0)));
|
|
break;
|
|
case AND_64:
|
|
emit_instr(IGen::and_gpr64_gpr64(gpr_id(*math.d), gpr_id(*math.a0)));
|
|
break;
|
|
case XOR_64:
|
|
emit_instr(IGen::xor_gpr64_gpr64(gpr_id(*math.d), gpr_id(*math.a0)));
|
|
break;
|
|
case NOT_64:
|
|
emit_instr(IGen::not_gpr64(gpr_id(*math.d)));
|
|
break;
|
|
|
|
default:
|
|
throw std::runtime_error("unknown integer math kind in emitter");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Do Math on Floats
|
|
*/
|
|
void x86_Emitter::do_float_math(IR_FloatMath& fl) {
|
|
switch (fl.math_kind) {
|
|
case MUL_SS:
|
|
emit_instr(IGen::mulss_xmm_xmm(xmm_id(*fl.d), xmm_id(*fl.a0)));
|
|
break;
|
|
case DIV_SS:
|
|
emit_instr(IGen::divss_xmm_xmm(xmm_id(*fl.d), xmm_id(*fl.a0)));
|
|
break;
|
|
case SUB_SS:
|
|
emit_instr(IGen::subss_xmm_xmm(xmm_id(*fl.d), xmm_id(*fl.a0)));
|
|
break;
|
|
case ADD_SS:
|
|
emit_instr(IGen::addss_xmm_xmm(xmm_id(*fl.d), xmm_id(*fl.a0)));
|
|
break;
|
|
default:
|
|
throw std::runtime_error("unknown float math kind in emitter");
|
|
}
|
|
}
|
|
|
|
void x86_Emitter::do_int_to_float(IR_IntToFloat& i2f) {
|
|
emit_instr(IGen::int32_to_float(xmm_id(*i2f.dest), gpr_id(*i2f.src)));
|
|
}
|
|
|
|
void x86_Emitter::do_float_to_int(IR_FloatToInt& f2i) {
|
|
emit_instr(IGen::float_to_int64(gpr_id(*f2i.dest), xmm_id(*f2i.src)));
|
|
}
|
|
|
|
/*!
|
|
* Get a pointer to a symbol.
|
|
*/
|
|
void x86_Emitter::do_get_symbol_object(IR_GetSymbolObj& get_sym) {
|
|
auto dest_id = get_ca(*get_sym.dest).reg_id;
|
|
emit_instr(IGen::mov_gpr64_gpr64(dest_id, ST_REG)); // s7
|
|
emit_instr(IGen::add_gpr64_imm32s(dest_id, 0xcafecafe)); // + offset
|
|
SymbolMemAccessRec rec;
|
|
rec.offset = instructions[current_seg].back().offset_of_imm();
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.seg = current_seg;
|
|
symbol_mem_access_recs[current_seg][get_sym.sym->name].push_back(rec);
|
|
emit_instr(IGen::sub_gpr64_gpr64(dest_id, OFF_REG));
|
|
}
|
|
|
|
/*!
|
|
* Load from memory!
|
|
*/
|
|
void x86_Emitter::do_load_const_offset(IR_LoadConstOffset& load) {
|
|
auto dst_reg = gpr_id(*load.dst);
|
|
auto src_reg = gpr_id(*load.src);
|
|
emit_instr(IGen::mov_gpr64_gpr64(dst_reg, src_reg));
|
|
emit_instr(IGen::add_gpr64_gpr64(dst_reg, OFF_REG));
|
|
if (load.size == 8) {
|
|
emit_instr(IGen::load64_gpr64_r64off32s(dst_reg, load.offset, dst_reg));
|
|
} else if (load.size == 4) {
|
|
emit_instr(IGen::load32_gpr32sz_r64off32s(dst_reg, load.offset, dst_reg, load.is_signed));
|
|
} else if (load.size == 2 && !load.is_signed) {
|
|
emit_instr(IGen::load16_gpr16z_r64off32s(dst_reg, dst_reg, load.offset));
|
|
} else if (load.size == 2 && load.is_signed) {
|
|
emit_instr(IGen::load16_gpr16s_r64off32s(dst_reg, dst_reg, load.offset));
|
|
} else if (load.size == 1 && !load.is_signed) {
|
|
emit_instr(IGen::load16_gpr8z_r64off32s(dst_reg, dst_reg, load.offset));
|
|
} else {
|
|
throw std::runtime_error("unsupported load size in do_load_const_offset " + load.print());
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Store into memory!
|
|
*/
|
|
void x86_Emitter::do_store_const_offset(IR_StoreConstOffset& store) {
|
|
// this sucks
|
|
auto mem_reg = gpr_id(*store.mem);
|
|
auto val_reg = gpr_id(*store.val);
|
|
|
|
if (mem_reg == val_reg) {
|
|
assert(mem_reg != BP_REG);
|
|
assert(val_reg != BP_REG);
|
|
emit_instr(IGen::push_gpr64(BP_REG));
|
|
emit_instr(IGen::mov_gpr64_gpr64(BP_REG, OFF_REG));
|
|
emit_instr(IGen::add_gpr64_gpr64(BP_REG, mem_reg));
|
|
if (store.size == 8) {
|
|
emit_instr(IGen::store64_r64off32s_gpr64(BP_REG, store.offset, val_reg));
|
|
} else if (store.size == 4) {
|
|
emit_instr(IGen::store32_r64off32s_gpr32(BP_REG, store.offset, val_reg));
|
|
} else if (store.size == 2) {
|
|
emit_instr(IGen::store16_r64off32s_gpr16(BP_REG, store.offset, val_reg));
|
|
} else if (store.size == 1) {
|
|
emit_instr(IGen::store8_r64off32s_gpr8(BP_REG, store.offset, val_reg));
|
|
} else {
|
|
throw std::runtime_error("unsupported load size in do_store_const_offset");
|
|
}
|
|
emit_instr(IGen::pop_gpr64(BP_REG));
|
|
} else {
|
|
emit_instr(IGen::add_gpr64_gpr64(mem_reg, OFF_REG));
|
|
if (store.size == 8) {
|
|
emit_instr(IGen::store64_r64off32s_gpr64(mem_reg, store.offset, val_reg));
|
|
} else if (store.size == 4) {
|
|
emit_instr(IGen::store32_r64off32s_gpr32(mem_reg, store.offset, val_reg));
|
|
} else if (store.size == 2) {
|
|
emit_instr(IGen::store16_r64off32s_gpr16(mem_reg, store.offset, val_reg));
|
|
} else if (store.size == 1) {
|
|
emit_instr(IGen::store8_r64off32s_gpr8(mem_reg, store.offset, val_reg));
|
|
} else {
|
|
throw std::runtime_error("unsupported load size in do_store_const_offset");
|
|
}
|
|
emit_instr(IGen::sub_gpr64_gpr64(mem_reg, OFF_REG));
|
|
}
|
|
}
|
|
|
|
void x86_Emitter::do_get_ra_ptr(IR_GetReturnAddressPointer& get_ra) {
|
|
auto dst_reg = gpr_id(*get_ra.dest);
|
|
uint64_t offset = (int64_t)(stack_offset + 0);
|
|
emit_instr(IGen::mov_gpr64_u64(dst_reg, offset));
|
|
emit_instr(IGen::add_gpr64_gpr64(dst_reg, RSP));
|
|
emit_instr(IGen::sub_gpr64_gpr64(dst_reg, OFF_REG));
|
|
}
|
|
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
// CODEGEN UTILITIES
|
|
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
/*!
|
|
* Load a uint64_t into a gpr.
|
|
*/
|
|
void x86_Emitter::load_u64_to_gpr(uint64_t value, std::shared_ptr<Place> var) {
|
|
auto as_reg = std::dynamic_pointer_cast<GprPlace>(var);
|
|
if (as_reg) {
|
|
emit_instr(IGen::mov_gpr64_u64(gpr_id(*var), value));
|
|
} else {
|
|
throw std::runtime_error("don't know how to load constant to this variable");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Emit a move between two gpr64's if the two given gpr's aren't the same.
|
|
* If a move isn't emitted, a null instruction is emitted instead.
|
|
*/
|
|
void x86_Emitter::emit_mov_gpr64_gpr64_or_null(uint8_t dst, uint8_t src) {
|
|
if (dst == src) {
|
|
emit_instr(IGen::null());
|
|
} else {
|
|
emit_instr(IGen::mov_gpr64_gpr64(dst, src));
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Emit a move between two gpr64's if the two given gpr's aren't the same.
|
|
* If a move isn't emitted, a null instruction is emitted instead.
|
|
*/
|
|
void x86_Emitter::emit_mov_gpr64_gpr64_or_null(std::shared_ptr<Place> dst,
|
|
std::shared_ptr<Place> src) {
|
|
emit_mov_gpr64_gpr64_or_null(gpr_id(*dst), gpr_id(*src));
|
|
}
|
|
|
|
void x86_Emitter::emit_mov_xmm32_xmm32_or_null(std::shared_ptr<Place> dst,
|
|
std::shared_ptr<Place> src) {
|
|
auto dst_id = xmm_id(*dst);
|
|
auto src_id = xmm_id(*src);
|
|
if (dst_id == src_id) {
|
|
emit_instr(IGen::null());
|
|
} else {
|
|
emit_instr(IGen::mov_xmm32_xmm32(dst_id, src_id));
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Given the jump instruction, create the cmp/jmp sequence.
|
|
*/
|
|
void x86_Emitter::do_cmp_branch(IR_ConditionalBranch& br, Instruction jump_instr) {
|
|
assert(br.resolved);
|
|
if (br.cond.is_float) {
|
|
emit_instr(IGen::cmp_flt_flt(xmm_id(*br.cond.a), xmm_id(*br.cond.b)));
|
|
} else {
|
|
emit_instr(IGen::cmp_gpr64_gpr64(gpr_id(*br.cond.a), gpr_id(*br.cond.b)));
|
|
}
|
|
|
|
emit_instr(jump_instr);
|
|
StaticJumpRecord rec;
|
|
rec.resolved = false;
|
|
rec.instr_idx = instructions[current_seg].size() - 1;
|
|
rec.offset_into_instr = instructions[current_seg].back().offset_of_imm();
|
|
rec.type = SIGNED_32_RIP;
|
|
rec.target_ir_idx = br.label->idx;
|
|
rec.function_offset = function_offset;
|
|
static_jumps[current_seg].push_back(rec);
|
|
}
|
|
|
|
void x86_Emitter::do_asm(IR_Asm& asm_op) {
|
|
switch (asm_op.asm_kind) {
|
|
case IR_Asm::RET:
|
|
assert(asm_op.args.empty());
|
|
emit_instr(IGen::ret());
|
|
break;
|
|
case IR_Asm::RET_REGISTER:
|
|
emit_instr(IGen::ret());
|
|
break;
|
|
case IR_Asm::PUSH:
|
|
assert(asm_op.args.size() == 1);
|
|
emit_instr(IGen::push_gpr64(gpr_id(*asm_op.args.at(0))));
|
|
break;
|
|
case IR_Asm::POP:
|
|
assert(asm_op.args.size() == 1);
|
|
emit_instr(IGen::pop_gpr64(gpr_id(*asm_op.args.at(0))));
|
|
break;
|
|
case IR_Asm::JMP:
|
|
assert(asm_op.args.size() == 1);
|
|
emit_instr(IGen::jmp_r64(gpr_id(*asm_op.args.at(0))));
|
|
break;
|
|
case IR_Asm::SUB:
|
|
assert(asm_op.args.size() == 2);
|
|
emit_instr(IGen::sub_gpr64_gpr64(gpr_id(*asm_op.args.at(0)), gpr_id(*asm_op.args.at(2))));
|
|
break;
|
|
default:
|
|
throw std::runtime_error("unknown asm op in emitter");
|
|
}
|
|
} |