Files
jak-project/old_compiler/cpp/codegen/IGen.h
T
2020-08-27 11:58:19 -04:00

747 lines
18 KiB
C++

/*!
* @file IGen.h
* Instruction Generation for x86-64
* Generate Instruction objects
*/
#ifndef JAK_IGEN_H
#define JAK_IGEN_H
#include <cstdint>
#include "Instruction.h"
class IGen {
public:
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// MOVES
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* mov gpr, gpr, 64 bits
*/
static Instruction mov_gpr64_gpr64(uint8_t dst, uint8_t src) {
Instruction instr(0x89);
instr.set_modrm_and_rex(src, dst, 3, true);
return instr;
}
/*!
* Move a 64-bit constant into a register.
*/
static Instruction mov_gpr64_u64(uint8_t dst, uint64_t val) {
bool rex_b = false;
if (dst >= 8) {
dst -= 8;
rex_b = true;
}
if (dst < 8) {
Instruction instr(0xb8 + dst);
instr.set(REX(true, false, false, rex_b));
instr.set(Imm(8, val));
return instr;
} else {
throw std::runtime_error("bad instruction mov_gpr64_u64");
}
}
/*!
* Move a 32-bit constant into a register.
*/
static Instruction mov_gpr64_u32(uint8_t dst, uint64_t val) {
assert(val <= UINT32_MAX);
bool rex_b = false;
if (dst >= 8) {
dst -= 8;
rex_b = true;
}
if (dst < 8) {
Instruction instr(0xb8 + dst);
if (rex_b) {
instr.set(REX(false, false, false, rex_b));
}
instr.set(Imm(4, val));
return instr;
} else {
throw std::runtime_error("bad instruction mov_gpr64_u32");
}
}
/*!
* Move a signed 32-bit constant into a register.
* When possible prefer mov_gpr64_u32. (use this only for negative values...)
*/
static Instruction mov_gpr64_s32(uint8_t dst, int64_t val) {
assert(val >= INT32_MIN && val <= INT32_MAX);
Instruction instr(0xc7);
instr.set_modrm_and_rex(0, dst, 3, true);
instr.set(Imm(4, val));
return instr;
}
/*!
* Move 32-bits of xmm to 32 bits of gpr (no sign extension).
*/
static Instruction movd_gpr32_xmm32(uint8_t dst, uint8_t src) {
Instruction instr(0x66);
instr.set_op2(0x0f);
instr.set_op3(0x7e);
instr.set_modrm_and_rex(src, dst, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Move 32-bits of gpr to 32-bits of xmm (no sign extenion)
*/
static Instruction movd_xmm32_gpr32(uint8_t dst, uint8_t src) {
Instruction instr(0x66);
instr.set_op2(0x0f);
instr.set_op3(0x6e);
instr.set_modrm_and_rex(dst, src, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Move 32-bits between xmm's
*/
static Instruction mov_xmm32_xmm32(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x10);
instr.set_modrm_and_rex(dst, src, 3, false);
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// LOADS n' STORES
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* Store 8-bits from register into a memory location that is the sum of a 64-bit register
* and signed 32-bit offset.
*/
static Instruction store8_r64off32s_gpr8(uint8_t dst_reg, int32_t offset, uint8_t src_reg) {
Instruction instr(0x88);
instr.set_modrm_and_rex_for_addr(src_reg, dst_reg, 2, false);
instr.set_disp(Imm(4, offset));
if (src_reg > RBX) {
instr.add_rex();
}
return instr;
}
/*!
* Store 16-bits from register into a memory location that is the sum of a 64-bit register
* and signed 32-bit offset.
*/
static Instruction store16_r64off32s_gpr16(uint8_t dst_reg, int32_t offset, uint8_t src_reg) {
Instruction instr(0x66);
instr.set_op2(0x89);
instr.set_modrm_and_rex_for_addr(src_reg, dst_reg, 2, false);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Store 32-bits from register into a memory location that is the sum of a 64-bit register
* and signed 32-bit offset.
*/
static Instruction store32_r64off32s_gpr32(uint8_t dst_reg, int32_t offset, uint8_t src_reg) {
Instruction instr(0x89);
instr.set_modrm_and_rex_for_addr(src_reg, dst_reg, 2, false);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Store 64-bits from gpr into memory located at 64-bit reg + 32-bit signed offset.
*/
static Instruction store64_r64off32s_gpr64(uint8_t dst_reg, int32_t offset, uint8_t src_reg) {
Instruction instr(0x89);
instr.set_modrm_rex_sib_for_reg_reg_disp32(src_reg, 2, dst_reg, true);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Load 8-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr (zero
* extended)
*/
static Instruction load16_gpr8z_r64off32s(uint8_t dst, uint8_t src, int32_t offset) {
Instruction instr(0x0f);
instr.set_op2(0xb6);
instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, true);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Load 16-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr (zero
* extended)
*/
static Instruction load16_gpr16z_r64off32s(uint8_t dst, uint8_t src, int32_t offset) {
Instruction instr(0x0f);
instr.set_op2(0xb7);
instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, true);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Load 16-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr (sign
* extended)
*/
static Instruction load16_gpr16s_r64off32s(uint8_t dst, uint8_t src, int32_t offset) {
Instruction instr(0x0f);
instr.set_op2(0xbf);
instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, true);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Load 32-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr.
* Use the sext flag to enable sign extension.
*/
static Instruction load32_gpr32sz_r64off32s(uint8_t dst_reg,
int32_t offset,
uint8_t src_reg,
bool sext = false) {
Instruction instr(0x8b);
if (sext) {
instr.op = 0x63;
}
instr.set_modrm_rex_sib_for_reg_reg_disp32(dst_reg, 2, src_reg, sext);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Load 64-bits from memory located at 64-bit reg + 32-bit signed offset into gpr
*/
static Instruction load64_gpr64_r64off32s(uint8_t dst_reg, int32_t offset, uint8_t src_reg) {
Instruction instr(0x8b);
instr.set_modrm_rex_sib_for_reg_reg_disp32(dst_reg, 2, src_reg, true);
instr.set_disp(Imm(4, offset));
return instr;
}
/*!
* Load 32-bits form memory located at 64-bit reg + 32-bit signed offset into xmm (32-bits)
* movss
*/
static Instruction load32_xmm32_r64off32s(uint8_t dst, uint8_t src, int32_t offset) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x10);
instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, false);
instr.set_disp(Imm(4, offset));
instr.swap_op0_rex();
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// FUNCTION STUFF
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* Return instruction
*/
static Instruction ret() { return Instruction(0xc3); }
/*!
* Instruction to push gpr (64-bits) onto the stack
*/
static Instruction push_gpr64(uint8_t reg) {
if (reg >= 8) {
auto i = Instruction(0x50 + reg - 8);
i.set(REX(false, false, false, true));
return i;
}
return Instruction(0x50 + reg);
}
/*!
* Instruction to pop 64 bit gpr from the stack
*/
static Instruction pop_gpr64(uint8_t reg) {
if (reg >= 8) {
auto i = Instruction(0x58 + reg - 8);
i.set(REX(false, false, false, true));
return i;
}
return Instruction(0x58 + reg);
}
/*!
* Call a function stored in a 64-bit gpr
*/
static Instruction call_r64(uint8_t reg) {
Instruction instr(0xff);
if (reg >= 8) {
instr.set(REX(false, false, false, true));
reg -= 8;
}
assert(reg < 8);
ModRM mrm;
mrm.rm = reg;
mrm.reg_op = 2;
mrm.mod = 3;
instr.set(mrm);
return instr;
}
/*!
* Call a function stored in a 64-bit gpr
*/
static Instruction jmp_r64(uint8_t reg) {
Instruction instr(0xff);
if (reg >= 8) {
instr.set(REX(false, false, false, true));
reg -= 8;
}
assert(reg < 8);
ModRM mrm;
mrm.rm = reg;
mrm.reg_op = 4;
mrm.mod = 3;
instr.set(mrm);
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// INTEGER MATH
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* Add 64-bit registers.
*/
static Instruction add_gpr64_gpr64(uint8_t dst, uint8_t src) {
Instruction instr(0x01);
instr.set_modrm_and_rex(src, dst, 3, true);
return instr;
}
/*!
* Add a signed 32 bit immediate to a 64 bit register
* TODO: determine if we can decrease to imm16?
*/
static Instruction add_gpr64_imm32s(uint8_t dst, int32_t offset) {
Instruction instr(0x81);
instr.set_modrm_and_rex(0, dst, 3, true);
instr.set(Imm(4, offset));
return instr;
}
/*!
* Add a signed 32 bit immediate to a 64 bit register
* TODO: determine if we can decrease to imm16?
*/
static Instruction add_gpr64_imm8s(uint8_t dst, int8_t v) {
Instruction instr(0x83);
instr.set_modrm_and_rex(0, dst, 3, true);
instr.set(Imm(1, v));
return instr;
}
/*!
* Subtract 64-bit registers
*/
static Instruction sub_gpr64_gpr64(uint8_t dst, uint8_t src) {
Instruction instr(0x29);
instr.set_modrm_and_rex(src, dst, 3, true);
return instr;
}
/*!
* Multiply gprs (32-bit, signed).
*/
static Instruction imul_gpr32_gpr32(uint8_t dst, uint8_t src) {
Instruction instr(0xf);
instr.set_op2(0xaf);
instr.set_modrm_and_rex(dst, src, 3, false);
return instr;
}
/*!
* Divide (idiv, 32 bit)
*/
static Instruction idiv_gpr32(uint8_t reg) {
Instruction instr(0xf7);
instr.set_modrm_and_rex(7, reg, 3, false);
return instr;
}
/*!
* Convert doubleword to quadword for division.
* Blame Intel for this disaster.
*/
static Instruction cdq() {
Instruction instr(0x99);
return instr;
}
/*!
* Move from gpr32 to gpr64, with sign extension.
* Needed for division madness.
*/
static Instruction movsx_r64_r32(uint8_t dst, uint8_t src) {
Instruction instr(0x63);
instr.set_modrm_and_rex(dst, src, 3, true);
return instr;
}
/*!
* Compare gpr64. This sets the flags for the jumps.
*/
static Instruction cmp_gpr64_gpr64(uint8_t a, uint8_t b) {
Instruction instr(0x3b);
instr.set_modrm_and_rex(a, b, 3, true);
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// BIT STUFF
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* Or of two gprs
*/
static Instruction or_gpr64_gpr64(uint8_t dst, uint8_t src) {
Instruction instr(0x0b);
instr.set_modrm_and_rex(dst, src, 3, true);
return instr;
}
/*!
* And of two gprs
*/
static Instruction and_gpr64_gpr64(uint8_t dst, uint8_t src) {
Instruction instr(0x23);
instr.set_modrm_and_rex(dst, src, 3, true);
return instr;
}
/*!
* Xor of two gprs
*/
static Instruction xor_gpr64_gpr64(uint8_t dst, uint8_t src) {
Instruction instr(0x33);
instr.set_modrm_and_rex(dst, src, 3, true);
return instr;
}
/*!
* This is the way "real" compilers zero registers, so we should do it too.
*/
static Instruction xor_zero_gpr(uint8_t reg) {
Instruction instr(0x31);
instr.set_modrm_and_rex(reg, reg, 3, false);
return instr;
}
/*!
* Bitwise not a gpr
*/
static Instruction not_gpr64(uint8_t reg) {
Instruction instr(0xf7);
instr.set_modrm_and_rex(2, reg, 3, true);
return instr;
}
/*!
* Shift 64-bit gpr left by CL register
*/
static Instruction shl_gpr64_cl(uint8_t reg) {
Instruction instr(0xd3);
instr.set_modrm_and_rex(4, reg, 3, true);
return instr;
}
/*!
* Shift 64-bit gpr right (logical) by CL register
*/
static Instruction shr_gpr64_cl(uint8_t reg) {
Instruction instr(0xd3);
instr.set_modrm_and_rex(5, reg, 3, true);
return instr;
}
/*!
* Shift 64-bit gpr right (arithmetic) by CL register
*/
static Instruction sar_gpr64_cl(uint8_t reg) {
Instruction instr(0xd3);
instr.set_modrm_and_rex(7, reg, 3, true);
return instr;
}
/*!
* Shift 64-ptr left (logical) by the constant shift amount "sa".
*/
static Instruction shl_gpr64_u8(uint8_t reg, uint8_t sa) {
Instruction instr(0xc1);
instr.set_modrm_and_rex(4, reg, 3, true);
instr.set(Imm(1, sa));
return instr;
}
/*!
* Shift 64-ptr right (logical) by the constant shift amount "sa".
*/
static Instruction shr_gpr64_u8(uint8_t reg, uint8_t sa) {
Instruction instr(0xc1);
instr.set_modrm_and_rex(5, reg, 3, true);
instr.set(Imm(1, sa));
return instr;
}
/*!
* Shift 64-ptr right (arithmetic) by the constant shift amount "sa".
*/
static Instruction sar_gpr64_u8(uint8_t reg, uint8_t sa) {
Instruction instr(0xc1);
instr.set_modrm_and_rex(7, reg, 3, true);
instr.set(Imm(1, sa));
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// CONTROL FLOW
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* Jump, 32-bit constant offset. The offset is by default 0 and must be patched later.
*/
static Instruction jmp_32() {
Instruction instr(0xe9);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump if equal.
* TODO - can we get away with 16 bits?
*/
static Instruction je_32() {
Instruction instr(0x0f);
instr.set_op2(0x84);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump not equal.
* TODO - can we get away with 16 bits?
*/
static Instruction jne_32() {
Instruction instr(0x0f);
instr.set_op2(0x85);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump less than or equal.
* TODO - can we get away with 16 bits?
*/
static Instruction jle_32() {
Instruction instr(0x0f);
instr.set_op2(0x8e);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump greater than or equal.
* TODO - can we get away with 16 bits?
*/
static Instruction jge_32() {
Instruction instr(0x0f);
instr.set_op2(0x8d);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump less than
* TODO - can we get away with 16 bits?
*/
static Instruction jl_32() {
Instruction instr(0x0f);
instr.set_op2(0x8c);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump greater than
* TODO - can we get away with 16 bits?
*/
static Instruction jg_32() {
Instruction instr(0x0f);
instr.set_op2(0x8f);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump below or equal
* TODO - can we get away with 16 bits?
*/
static Instruction jbe_32() {
Instruction instr(0x0f);
instr.set_op2(0x86);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump above or equal
* TODO - can we get away with 16 bits?
*/
static Instruction jae_32() {
Instruction instr(0x0f);
instr.set_op2(0x83);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump below
* TODO - can we get away with 16 bits?
*/
static Instruction jb_32() {
Instruction instr(0x0f);
instr.set_op2(0x82);
instr.set(Imm(4, 0));
return instr;
}
/*!
* Jump above
* TODO - can we get away with 16 bits?
*/
static Instruction ja_32() {
Instruction instr(0x0f);
instr.set_op2(0x87);
instr.set(Imm(4, 0));
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// FLOAT MATH
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* Compare two floats and set flag register for jump
*/
static Instruction cmp_flt_flt(uint8_t a, uint8_t b) {
Instruction instr(0x0f);
instr.set_op2(0x2e);
instr.set_modrm_and_rex(a, b, 3, false);
return instr;
}
/*!
* Multiply two floats in xmm's
*/
static Instruction mulss_xmm_xmm(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x59);
instr.set_modrm_and_rex(dst, src, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Divide two floats in xmm's
*/
static Instruction divss_xmm_xmm(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x5e);
instr.set_modrm_and_rex(dst, src, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Subtract two floats in xmm's
*/
static Instruction subss_xmm_xmm(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x5c);
instr.set_modrm_and_rex(dst, src, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Add two floats in xmm's
*/
static Instruction addss_xmm_xmm(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x58);
instr.set_modrm_and_rex(dst, src, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Convert GPR int32 to XMM float (single precision)
*/
static Instruction int32_to_float(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x2a);
instr.set_modrm_and_rex(dst, src, 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Convert XMM float to GPR int32(single precision) (truncate)
*/
static Instruction float_to_int64(uint8_t dst, uint8_t src) {
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x2c);
instr.set_modrm_and_rex(dst, src, 3, true);
instr.swap_op0_rex();
return instr;
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// UTILITIES
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*!
* A "null" instruction. This instruction does not generate any bytes
* but can be referred to by a label. Useful to insert in place of a real instruction
* if the real instruction has been optimized out.
*/
static Instruction null() {
Instruction i(0);
i.is_null = true;
return i;
}
/*!
* A "function start" instruction. This emits no opcodes, but is used
* to determine where to insert the function type tag and how to align a function.
*/
static Instruction function_start() {
Instruction i(0);
i.is_null = true;
i.is_function_start = true;
return i;
}
};
#endif // JAK_IGEN_H