503 lines
14 KiB
C
503 lines
14 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2007, 2009 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
#include "x86_64.h"
|
|
|
|
#ifdef DEBUG
|
|
void format_machine_inst(void);
|
|
void initialize_registers(void);
|
|
void reset_instruction(void);
|
|
void print_source_operand(void);
|
|
void print_destination_operand(void);
|
|
void print_instruction(void);
|
|
void set_memory_reg(void);
|
|
void set_register_reg(void);
|
|
void clear_memory_reg(void);
|
|
#endif
|
|
|
|
void emit_base_offset(int base, int offset);
|
|
|
|
/* Define the COND values for use in the 2 byte JCC instructions.. */
|
|
|
|
#define INST_SIZE 1
|
|
#define XFER_BYTE_INST_SIZE 3
|
|
#define XFER_LONG_INST_SIZE 6
|
|
#define BRB_INST_SIZE 2
|
|
#define JMP_LONG_INST_SIZE 5
|
|
#define CALL_4LCLDO_XFER 2 /* index in ttt from start of call[sp] and forlcldo to xfer_table index */
|
|
#define MAX_BRANCH_CODEGEN_SIZE 32 /* The length in bytes, of the longest form of branch instruction sequence */
|
|
|
|
|
|
int x86_64_arg_reg(int indx);
|
|
#define GET_ARG_REG(indx) x86_64_arg_reg(indx)
|
|
|
|
/* Should be offset from RSP */
|
|
#define STACK_ARG_OFFSET(indx) (8 * indx)
|
|
|
|
enum condition
|
|
{
|
|
JO,
|
|
JNO,
|
|
JB,
|
|
JC = JB,
|
|
JNAE = JC,
|
|
JNB,
|
|
JNC = JNB,
|
|
JAE = JNB,
|
|
JZ,
|
|
JE = JZ,
|
|
JNZ,
|
|
JNE = JNZ,
|
|
JBE,
|
|
JNA = JBE,
|
|
JNBE,
|
|
JA = JNBE,
|
|
JS,
|
|
JNS,
|
|
JP,
|
|
JPE = JP,
|
|
JNP,
|
|
JPO = JNP,
|
|
JL,
|
|
JNGE = JL,
|
|
JNL,
|
|
JGE = JNL,
|
|
JLE,
|
|
JNG = JLE,
|
|
JNLE,
|
|
JG = JNLE
|
|
};
|
|
|
|
#define REX_FIELD char
|
|
|
|
#define REX_OP 0x40
|
|
#define REX_B 0x01
|
|
#define REX_X 0x02
|
|
#define REX_R 0x04
|
|
#define REX_W 0x08
|
|
|
|
/* Note that while the register number we use in gtm can go from 0-15, the number of bits in the modrm/sib regfields are only 3.
|
|
* So if we need to use the regs r8-r15, then a bit in the REX field should be set (depending on whether the register is encoded
|
|
* in the sib or opcode or modrm, etc.
|
|
*/
|
|
#define SET_REX_PREFIX(REX_MANDATORY, REX_COND, reg) \
|
|
{ \
|
|
if (reg & 0x8) { /* If bit 3 (index starting from 0) is set, set the specified REX bit */ \
|
|
emit_base_info.rex |= REX_COND; \
|
|
} \
|
|
emit_base_info.rex |= REX_OP | REX_MANDATORY; \
|
|
emit_base_info.rex_set = 1; \
|
|
}
|
|
|
|
typedef union
|
|
{
|
|
ModR_M modrm;
|
|
unsigned char byte;
|
|
} modrm_byte_type;
|
|
|
|
typedef union
|
|
{
|
|
SIB sib;
|
|
unsigned char byte;
|
|
} sib_byte_type;
|
|
|
|
/* Instead of filling up the code buf, emit_base_offset will instead fill this structure, which will be used by the
|
|
* macro which actually creates the instruction stream
|
|
*/
|
|
struct emit_base_info
|
|
{
|
|
REX_FIELD rex;
|
|
modrm_byte_type modrm_byte;
|
|
sib_byte_type sib_byte;
|
|
char offset8;
|
|
int offset32;
|
|
int64_t offset64;
|
|
char imm8;
|
|
int imm32;
|
|
int64_t imm64;
|
|
int rex_set;
|
|
int modrm_byte_set;
|
|
int sib_byte_set; /* Not using bitfields, since this would generate faster code. Since this is a one-type only
|
|
* created object, no real need for space optimization
|
|
*/
|
|
int offset8_set;
|
|
int offset32_set;
|
|
int offset64_set;
|
|
int imm8_set;
|
|
int imm32_set;
|
|
int imm64_set;
|
|
};
|
|
|
|
GBLREF struct emit_base_info emit_base_info;
|
|
|
|
/* Define the macros for the instructions to be generated.. */
|
|
|
|
#define GENERIC_OPCODE_BEQ 1
|
|
#define GENERIC_OPCODE_BGE 2
|
|
#define GENERIC_OPCODE_BGT 3
|
|
#define GENERIC_OPCODE_BLE 4
|
|
#define GENERIC_OPCODE_BLT 5
|
|
#define GENERIC_OPCODE_BNE 6
|
|
#define GENERIC_OPCODE_BLBC 7
|
|
#define GENERIC_OPCODE_BLBS 8
|
|
#define GENERIC_OPCODE_BR 9
|
|
#define GENERIC_OPCODE_LDA 11
|
|
#define GENERIC_OPCODE_LOAD 12
|
|
#define GENERIC_OPCODE_STORE 13
|
|
#define GENERIC_OPCODE_STORE_ZERO 14
|
|
#define GENERIC_OPCODE_NOP 15
|
|
|
|
#define LONG_JUMP_OFFSET (0x7ffffffc)
|
|
#define MAX_OFFSET 0xffffffff
|
|
#define EMIT_JMP_ADJUST_BRANCH_OFFSET branch_offset -= 5
|
|
|
|
|
|
#define EMIT_BASE_CODE_SIZE ((emit_base_info.rex_set ? 1 : 0) /* REX */ + 1 /* OPCODE */ + \
|
|
(emit_base_info.modrm_byte_set ? 1 : 0) /* MODRM */ + \
|
|
(emit_base_info.sib_byte_set ? 1 : 0) + (emit_base_info.offset8_set ? 1 : 0) + \
|
|
(emit_base_info.offset32_set ? 4 : 0) + (emit_base_info.offset64_set ? 8 : 0) + \
|
|
(emit_base_info.imm8_set ? 1 : 0) + (emit_base_info.imm32_set ? 4 : 0) + \
|
|
(emit_base_info.imm64_set ? 8 : 0))
|
|
|
|
#define CODE_BUF_GEN(op_code) \
|
|
{ \
|
|
(emit_base_info.rex_set) ? (code_buf[code_idx++] = emit_base_info.rex):0; \
|
|
code_buf[code_idx++] = (char) op_code; \
|
|
if (emit_base_info.modrm_byte_set) { \
|
|
code_buf[code_idx++] = emit_base_info.modrm_byte.byte; \
|
|
} \
|
|
if (emit_base_info.sib_byte_set) { \
|
|
code_buf[code_idx++] = emit_base_info.sib_byte.byte; \
|
|
} \
|
|
if (emit_base_info.offset8_set) { \
|
|
code_buf[code_idx++] = emit_base_info.offset8; \
|
|
} \
|
|
if (emit_base_info.offset32_set) { \
|
|
*((int4 *)&code_buf[code_idx]) = emit_base_info.offset32; \
|
|
code_idx += SIZEOF(int4) ; \
|
|
} \
|
|
if (emit_base_info.offset64_set) { \
|
|
*((int64_t *)&code_buf[code_idx]) = emit_base_info.offset64; \
|
|
code_idx += SIZEOF(int64_t) ; \
|
|
} \
|
|
if (emit_base_info.imm8_set) { \
|
|
code_buf[code_idx++] = emit_base_info.imm8; \
|
|
} \
|
|
if (emit_base_info.imm32_set) { \
|
|
*((int4 *)&code_buf[code_idx]) = emit_base_info.imm32; \
|
|
code_idx += SIZEOF(int4) ; \
|
|
} \
|
|
if (emit_base_info.imm64_set) { \
|
|
*((int64_t *)&code_buf[code_idx]) = emit_base_info.imm64; \
|
|
code_idx += SIZEOF(int64_t) ; \
|
|
} \
|
|
}
|
|
|
|
#define IGEN_LOAD_WORD_REG_8(reg) \
|
|
{ \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Gv_Ev) \
|
|
}
|
|
#define IGEN_LOAD_NATIVE_REG(reg) IGEN_LOAD_WORD_REG_8(reg)
|
|
|
|
/* Sign extended load */
|
|
#define IGEN_LOAD_WORD_REG_4(reg) \
|
|
{ \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOVSXD_Gv_Ev) \
|
|
}
|
|
|
|
#define IGEN_STORE_WORD_REG_8(reg) \
|
|
{ \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Ev_Gv) \
|
|
}
|
|
|
|
#define IGEN_STORE_WORD_REG_4(reg) \
|
|
{ \
|
|
SET_REX_PREFIX(0, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Ev_Gv) \
|
|
}
|
|
|
|
#define IGEN_STORE_ZERO_REG_8(reg) \
|
|
{ \
|
|
SET_REX_PREFIX(REX_W, 0, 0) \
|
|
emit_base_info.imm32 = 0; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Ev_Iv) \
|
|
}
|
|
|
|
#define IGEN_STORE_ZERO_REG_4(reg) \
|
|
{ \
|
|
emit_base_info.imm32 = 0; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Ev_Iv) \
|
|
}
|
|
|
|
#define IGEN_GENERIC_REG(opcode, reg) \
|
|
{\
|
|
switch(opcode) \
|
|
{\
|
|
case GENERIC_OPCODE_LDA:\
|
|
IGEN_LOAD_ADDR_REG(reg)\
|
|
break;\
|
|
case GENERIC_OPCODE_LOAD:\
|
|
if ( next_ptr_offset == 4 ) { \
|
|
IGEN_LOAD_WORD_REG_4(reg); \
|
|
} else {\
|
|
IGEN_LOAD_WORD_REG_8(reg); \
|
|
} \
|
|
next_ptr_offset = 8; \
|
|
break;\
|
|
case GENERIC_OPCODE_STORE:\
|
|
if ( next_ptr_offset == 4 ) { \
|
|
IGEN_STORE_WORD_REG_4(reg); \
|
|
} else {\
|
|
IGEN_STORE_WORD_REG_8(reg); \
|
|
} \
|
|
next_ptr_offset = 8; \
|
|
break;\
|
|
case GENERIC_OPCODE_STORE_ZERO:\
|
|
if ( next_ptr_offset == 4 ) { \
|
|
IGEN_STORE_ZERO_REG_4(reg); \
|
|
} else {\
|
|
IGEN_STORE_ZERO_REG_8(reg); \
|
|
} \
|
|
next_ptr_offset = 8; \
|
|
break;\
|
|
default: /* which opcode ? */ \
|
|
GTMASSERT;\
|
|
break;\
|
|
}\
|
|
}
|
|
|
|
#define IGEN_LOAD_LINKAGE(reg) IGEN_LOAD_WORD_REG_8(reg)
|
|
|
|
#define IGEN_LOAD_ADDR_REG(reg) \
|
|
{ \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_LEA_Gv_M) \
|
|
}
|
|
|
|
#define GEN_STORE_ARG(reg, offset) \
|
|
{ \
|
|
X86_64_ONLY(force_32 = TRUE;) \
|
|
GEN_STORE_WORD_8(reg, I386_REG_RSP, offset) \
|
|
X86_64_ONLY(force_32 = FALSE;) \
|
|
}
|
|
|
|
#define GEN_LOAD_WORD_8(reg, base_reg, offset) \
|
|
{ \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Gv_Ev) \
|
|
}
|
|
|
|
/* Load 4 bytes with sign extension */
|
|
#define GEN_LOAD_WORD_4(reg, base_reg, offset) \
|
|
{ \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOVSXD_Gv_Ev) \
|
|
}
|
|
|
|
#define GEN_STORE_WORD_8(reg, base_reg, offset) \
|
|
{ \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Ev_Gv) \
|
|
}
|
|
|
|
#define GEN_STORE_WORD_4(reg, base_reg, offset) \
|
|
{ \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(0, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(I386_INS_MOV_Ev_Gv) \
|
|
}
|
|
|
|
#define GEN_LOAD_IMMED(reg, imval) \
|
|
{ \
|
|
int op_code = I386_INS_MOV_eAX + (reg & 0x7); \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
SET_REX_PREFIX(0, REX_B, reg) \
|
|
emit_base_info.imm32 = (int) imval & 0xffffffff; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_CLEAR_WORD_EMIT(reg) emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_STORE_ZERO, reg)
|
|
|
|
#define GEN_LOAD_WORD_EMIT(reg) emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LOAD, reg)
|
|
|
|
#define EMIT_TRIP_ILIT_GEN GEN_LOAD_IMMED(trg_reg, immediate)
|
|
|
|
#define GEN_XFER_TBL_CALL(xfer) \
|
|
{ \
|
|
emit_base_offset(GTM_REG_XFER_TABLE, xfer); \
|
|
emit_base_info.rex_set = 0; \
|
|
assert(GTM_REG_XFER_TABLE <= 7); /* if its a 64 bit register, we might need to set the REX bit */ \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = (char) I386_INS_CALL_Ev; \
|
|
CODE_BUF_GEN(I386_INS_Grp5_Prefix) \
|
|
}
|
|
|
|
#define GEN_CMP_EAX_IMM32(imm) \
|
|
{ \
|
|
int op_code = I386_INS_CMP_eAX_Iv ; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
emit_base_info.imm32 = (int4) imm & 0xffffffff; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
|
|
|
|
#define GEN_CMP_MEM64_ZERO(base_reg, offset) \
|
|
{ \
|
|
int op_code = I386_INS_Grp1_Ev_Iv_Prefix; \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(REX_W, 0, 0) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = I386_INS_CMP__; \
|
|
emit_base_info.imm32 = (int4) 0; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_CMP_MEM32_ZERO(base_reg, offset) \
|
|
{ \
|
|
int op_code = I386_INS_Grp1_Ev_Iv_Prefix; \
|
|
emit_base_offset(base_reg, offset); \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = I386_INS_CMP__; \
|
|
emit_base_info.imm32 = (int4) 0 ; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_CMP_REG_MEM32(reg, base_reg, offset) \
|
|
{ \
|
|
int op_code = I386_INS_CMP_Gv_Ev; \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(0, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_CMP_REG_MEM64(reg, base_reg, offset) \
|
|
{ \
|
|
int op_code = I386_INS_CMP_Gv_Ev; \
|
|
emit_base_offset(base_reg, offset); \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg & 0x7; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_CMP_REGS(reg1, reg2) \
|
|
{ \
|
|
int op_code = I386_INS_CMP_Gv_Ev; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
SET_REX_PREFIX(REX_W, REX_R, reg1) \
|
|
SET_REX_PREFIX(0, REX_B, reg2) \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = reg1 & 0x7; \
|
|
emit_base_info.modrm_byte.modrm.mod = I386_MOD32_REGISTER; \
|
|
emit_base_info.modrm_byte.modrm.r_m = reg2 & 0x7; \
|
|
emit_base_info.modrm_byte_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
/* Note that there is no CMP_IMM64 in AMD64 */
|
|
#define GEN_CMP_IMM32(reg, imm) \
|
|
{ \
|
|
int op_code = I386_INS_Grp1_Ev_Iv_Prefix; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = I386_INS_CMP__; \
|
|
emit_base_info.modrm_byte.modrm.mod = I386_MOD32_REGISTER; \
|
|
emit_base_info.modrm_byte.modrm.r_m = reg & 0x7; \
|
|
emit_base_info.modrm_byte_set = 1; \
|
|
SET_REX_PREFIX(0, REX_B, reg) \
|
|
emit_base_info.imm32 = (int4) imm & 0xffffffff; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
|
|
#define GEN_SUBTRACT_REGS(src1, src2, trgt) GTMASSERT;
|
|
|
|
#define GEN_ADD_IMMED(reg, imval) \
|
|
{ \
|
|
int op_code = I386_INS_Grp1_Ev_Iv_Prefix; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
SET_REX_PREFIX(REX_W, REX_B, reg) \
|
|
emit_base_info.modrm_byte_set = 1; \
|
|
emit_base_info.modrm_byte.modrm.mod = I386_MOD32_REGISTER; \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = I386_INS_ADD__; \
|
|
emit_base_info.modrm_byte.modrm.r_m = reg & 0x7; \
|
|
emit_base_info.imm32 = (int4) imval; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_MOVE_REG(trg, src) \
|
|
{ \
|
|
int op_code = I386_INS_MOV_Ev_Gv; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
SET_REX_PREFIX(REX_W, REX_B, trg) \
|
|
SET_REX_PREFIX(0, REX_R, src) \
|
|
emit_base_info.modrm_byte_set = 1; \
|
|
emit_base_info.modrm_byte.modrm.mod = I386_MOD32_REGISTER; \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = src; \
|
|
emit_base_info.modrm_byte.modrm.r_m = trg & 0x7; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_JUMP_REG(reg) \
|
|
{ \
|
|
int op_code = I386_INS_Grp5_Prefix; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
SET_REX_PREFIX(REX_OP | REX_W, REX_B, reg) \
|
|
emit_base_info.modrm_byte_set = 1; \
|
|
emit_base_info.modrm_byte.modrm.mod = I386_MOD32_REGISTER; \
|
|
emit_base_info.modrm_byte.modrm.reg_opcode = I386_INS_JMP_Ev; \
|
|
emit_base_info.modrm_byte.modrm.r_m = reg & 0x7; \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
#define GEN_PCREL \
|
|
{ \
|
|
int op_code = I386_INS_CALL_Jv; \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
emit_base_info.imm32 = 0; \
|
|
emit_base_info.imm32_set = 1; \
|
|
CODE_BUF_GEN(op_code) \
|
|
memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info)); \
|
|
op_code = I386_INS_POP_eAX + (GTM_REG_CODEGEN_TEMP & 0x7); \
|
|
SET_REX_PREFIX(0, REX_B, GTM_REG_CODEGEN_TEMP) \
|
|
assert(GTM_REG_CODEGEN_TEMP > 7); \
|
|
CODE_BUF_GEN(op_code) \
|
|
}
|
|
|
|
/*
|
|
* GT.M on AIX and SPARC is 64bit
|
|
* By default the loads/stores use ldd/std(load double),
|
|
* but if the value being dealt with is a word,the
|
|
* opcode in generic_inst is changed to ldw/stw
|
|
* On other platforms, it is defined to null
|
|
*/
|
|
#define REVERT_GENERICINST_TO_WORD(inst)
|