// TODO verify that the arguments are being compared in the right order // cmpq offset(%reg), $imm inline void _emitAttrCmpImm(int reg, int offset, int64_t val) { assert((-1L<<31) <= val && val < (1L<<31)-1); int rex = X86::REX_W; if (reg >= 8) { rex |= X86::REX_B; reg -= 8; } assert(reg >= 0 && reg < 8); _emitRex(rex); _emitByte(0x81); assert(-0x80 <= offset && offset < 0x80); if (offset == 0) { _emitModRM(0b00, 7, reg); } else { _emitModRM(0b01, 7, reg); _emitByte(offset); } for (int i = 0; i < 4; i++) { _emitByte(val & 0xff); val >>= 8; } }
inline void _emitCmpDisplacement(uint8_t reg1, uint64_t reg2, int displacement) { // TODO if it's bigger, we could use a larger scale since // things are most likely aligned assert(displacement >= -0x80 && displacement < 0x80); uint8_t flags = 0; flags |= X86::REX_W; if (reg1 >= 8) { flags |= X86::REX_R; reg1 &= 0b111; } if (reg2 >= 8) { flags |= X86::REX_B; reg2 &= 0b111; } _emitRex(flags); _emitByte(0x39); if (displacement == 0) { // Since we're emitting into a fixed-size section I guess there might not be // too much benifit to the more compact encoding, but it makes me feel better: _emitModRM(0b00, reg1, reg2); } else { _emitModRM(0b01, reg1, reg2); _emitByte(displacement); } }
// movzbq %src_reg, %dest_reg void _emitZeroExtend(int src_reg, int dest_reg) { assert(0 <= src_reg && src_reg < 8); assert(0 <= dest_reg && dest_reg < 8); _emitRex(X86::REX_W); _emitByte(0x0f); _emitByte(0xb6); _emitModRM(0b11, dest_reg, src_reg); }
inline void _emitMoveImm64(uint8_t reg, uint64_t value) { assert(reg >= 0 && reg < 8); _emitRex(X86::REX_W); _emitByte(0xb8 + reg); for (int i = 0; i < 8; i++) { _emitByte(value & 0xff); value >>= 8; } }
void _emitCondSet(int dest_reg, int cond_code) { assert(0 <= dest_reg && dest_reg < 8); assert(0 <= cond_code && cond_code < 16); if (dest_reg >= 4) _emitRex(0); _emitByte(0x0f); _emitByte(0x90 + cond_code); _emitModRM(0b11, 0, dest_reg); }
// incq offset(%reg) virtual void _emitIncattr(int reg, int offset) { assert(offset >= -0x80 && offset < 0x80); int rex = X86::REX_W; if (reg >= 8) { rex |= X86::REX_B; reg -= 8; } _emitRex(rex); _emitByte(0xff); _emitModRM(0b01, 0b000, reg); _emitByte(offset); }
void _emitJmp(void* dest_addr) { long offset = (uint8_t*)dest_addr - addr - 2; if (offset >= -0x80 && offset < 0x80) { _emitByte(0xeb); _emitByte(offset); } else { assert(offset >= -1L<<31 && offset < 1L<<31); offset -= 3; _emitByte(0xe9); for (int i = 0; i < 4; i++) { _emitByte(offset & 0xff); offset >>= 8; } } }
// test[q] %reg1, %reg2 inline void _emitTest(int reg1, int reg2) { assert(reg1 >= 0 && reg1 < 8); assert(reg2 >= 0 && reg2 < 8); _emitRex(X86::REX_W); _emitByte(0x85); _emitModRM(0b11, reg1, reg2); }
/// "op $val, %reg" inline void _emitArith(int reg, int val, int opcode) { assert(val >= -0x80 && val < 0x80); assert(opcode < 8); uint8_t flags = 0; flags |= X86::REX_W; if (reg >= 8) { flags |= X86::REX_B; reg &= 0b111; } _emitRex(flags); _emitByte(0x83); _emitModRM(0b11, opcode, reg); _emitByte(val); }
inline void _emitJne(uint8_t* dest_addr, bool unlikely) { _emitJmpCond(dest_addr, X86::COND_NOT_EQUAL, unlikely); int offset = dest_addr - addr - 2; if (unlikely) offset -= 1; if (offset >= -0x80 && offset < 0x80) { if (unlikely) _emitByte(0x2e); _emitByte(0x75); _emitByte(offset); } else { offset -= 4; if (unlikely) _emitByte(0x2e); _emitByte(0x0f); _emitByte(0x85); for (int i = 0; i < 4; i++) { _emitByte(offset & 0xff); offset >>= 8; } } }
// TODO verify that the arguments are being compared in the right order // cmp $val, %reg inline void _emitCmpImm(int reg, int64_t val) { assert((-1L<<31) <= val && val < (1L<<31)-1); int rex = X86::REX_W; if (reg > 8) { rex |= X86::REX_B; reg -= 8; } assert(0 <= reg && reg < 8); _emitRex(rex); _emitByte(0x81); _emitModRM(0b11, 7, reg); for (int i = 0; i < 4; i++) { _emitByte(val & 0xff); val >>= 8; } }
inline void _emitPop(uint8_t reg) { assert(reg != X86::REG_STACK_POINTER); // this might work but most likely a bug assert(0 <= reg && reg < 16); if (reg >= 8) { _emitRex(X86::REX_B); reg -= 8; } assert(reg < 8); _emitByte(0x58 + reg); }
// TODO verify that the arguments are being compared in the right order // cmpq offset(%reg1), %reg2 inline void _emitAttrCmp(int reg1, int reg1_offset, int reg2) { int rex = X86::REX_W; if (reg1 >= 8) { rex |= X86::REX_B; reg1 -= 8; } assert(reg1 >= 0 && reg1 < 8); assert(reg2 >= 0 && reg2 < 8); _emitRex(rex); _emitByte(0x3B); assert(-0x80 <= reg1_offset && reg1_offset < 0x80); if (reg1_offset == 0) { _emitModRM(0b00, reg2, reg1); } else { _emitModRM(0b01, reg2, reg1); _emitByte(reg1_offset); } }
/// mov $displacement(%source) %dest inline void _emitLoadRegIndirect(int source, int displacement, int dest) { bool usesib = false; if (source == 0b100 || dest == 0b1100) { usesib = true; } uint8_t flags = X86::REX_W; if (dest >= 8) { flags |= X86::REX_R; dest &= 0b111; } if (source >= 8) { flags |= X86::REX_B; source &= 0b111; } _emitRex(flags); _emitByte(0x8b); int mode; if (displacement == 0) mode = 0b00; else if (-0x80 <= displacement && displacement < 0x79) mode = 0b01; else mode = 0b10; _emitModRM(mode, dest, source); if (usesib) _emitSIB(0b00, 0b100, source); if (mode == 0b01) { _emitByte(displacement); } else if (mode == 0b10) { for (int i = 0; i < 4; i++) { _emitByte(displacement & 0xff); displacement >>= 8; } } }
// TODO verify that the arguments are being compared in the right order // cmpq %reg1, %reg2 # or maybe they're reversed? inline void _emitCmp(int reg1, int reg2) { int rex = X86::REX_W; if (reg1 >= 8) { rex |= X86::REX_R; reg1 -= 8; } assert(reg1 >= 0 && reg1 < 8); assert(reg2 >= 0 && reg2 < 8); _emitRex(rex); _emitByte(0x39); _emitModRM(0b11, reg1, reg2); }
inline void _emitMoveReg(int source, int dest) { uint8_t flags = 0; flags |= X86::REX_W; if (dest >= 8) { flags |= X86::REX_B; dest &= 0b111; } if (source >= 8) { flags |= X86::REX_R; source &= 0b111; } _emitRex(flags); _emitByte(0x89); _emitModRM(0b11, source, dest); }
inline void _emitJmpCond(uint8_t* dest_addr, X86::ConditionCode condition, bool unlikely) { int offset = dest_addr - addr - 2; if (unlikely) offset -= 1; if (offset >= -0x80 && offset < 0x80) { if (unlikely) _emitByte(0x2e); _emitByte(0x75); _emitByte(offset); } else { offset -= 4; if (unlikely) _emitByte(0x2e); _emitByte(0x0f); _emitByte(0x80 | condition); for (int i = 0; i < 4; i++) { _emitByte(offset & 0xff); offset >>= 8; } } }
inline void _emitRex(uint8_t flags) { assert(0 <= flags && flags < 16); _emitByte(0x40 | flags); }
inline void _emitModRM(uint8_t mod, uint8_t reg, uint8_t rm) { assert(mod < 4); assert(reg < 8); assert(rm < 8); _emitByte((mod << 6) | (reg << 3) | rm); }
inline void _emitSIB(uint8_t scalebits, uint8_t index, uint8_t base) { assert(scalebits < 4); assert(index < 8); assert(base < 8); _emitByte((scalebits << 6) | (index << 3) | base); }