X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fia32%2Fia32_emitter.c;h=73e3882d9ed922ae4895e8c10d452e131cfa0734;hb=9e4f0f3073faa64348e2e064d75c4a5752531cd5;hp=b5b5abe3ddd645fc1ab47771ebcbd3bc5c71ffc5;hpb=80d22a2b8ed15af53c7134a3025da89ccb1923ca;p=libfirm diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index b5b5abe3d..73e3882d9 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -37,18 +37,20 @@ #include "irargs_t.h" #include "irprog_t.h" #include "iredges_t.h" +#include "irtools.h" #include "execfreq.h" #include "error.h" #include "raw_bitset.h" #include "dbginfo.h" +#include "lc_opts.h" -#include "../besched_t.h" -#include "../benode_t.h" +#include "../besched.h" +#include "../benode.h" #include "../beabi.h" #include "../be_dbgout.h" #include "../beemitter.h" #include "../begnuas.h" -#include "../beirg_t.h" +#include "../beirg.h" #include "../be_dbgout.h" #include "ia32_emitter.h" @@ -1303,7 +1305,7 @@ static void emit_ia32_SwitchJmp(const ir_node *node) /** * Emits code for a unconditional jump. */ -static void emit_Jmp(const ir_node *node) +static void emit_ia32_Jmp(const ir_node *node) { ir_node *block; @@ -1616,6 +1618,16 @@ static void emit_be_IncSP(const ir_node *node) } } +static inline bool is_unknown_reg(const arch_register_t *reg) +{ + if(reg == &ia32_gp_regs[REG_GP_UKNWN] + || reg == &ia32_xmm_regs[REG_XMM_UKNWN] + || reg == &ia32_vfp_regs[REG_VFP_UKNWN]) + return true; + + return false; +} + /** * Emits code for Copy/CopyKeep. */ @@ -1871,6 +1883,7 @@ static void ia32_register_emitters(void) IA32_EMIT(Minus64Bit); IA32_EMIT(SwitchJmp); IA32_EMIT(ClimbFrame); + IA32_EMIT(Jmp); /* benode emitter */ BE_EMIT(Copy); @@ -1881,12 +1894,10 @@ static void ia32_register_emitters(void) BE_IGN(Barrier); BE_IGN(Keep); - BE_IGN(RegParams); + BE_IGN(Start); /* firm emitter */ - EMIT(Jmp); IGN(Phi); - IGN(Start); #undef BE_EMIT #undef EMIT @@ -2221,6 +2232,1129 @@ static const lc_opt_table_entry_t ia32_emitter_options[] = { LC_OPT_LAST }; +/* ==== Experimental binary emitter ==== */ + +static unsigned char reg_gp_map[N_ia32_gp_REGS]; +static unsigned char reg_mmx_map[N_ia32_mmx_REGS]; +static unsigned char reg_sse_map[N_ia32_xmm_REGS]; +static unsigned char pnc_map_signed[8]; +static unsigned char pnc_map_unsigned[8]; + +static void build_reg_map(void) +{ + reg_gp_map[REG_EAX] = 0x0; + reg_gp_map[REG_ECX] = 0x1; + reg_gp_map[REG_EDX] = 0x2; + reg_gp_map[REG_EBX] = 0x3; + reg_gp_map[REG_ESP] = 0x4; + reg_gp_map[REG_EBP] = 0x5; + reg_gp_map[REG_ESI] = 0x6; + reg_gp_map[REG_EDI] = 0x7; + + pnc_map_signed[pn_Cmp_Eq] = 0x04; + pnc_map_signed[pn_Cmp_Lt] = 0x0C; + pnc_map_signed[pn_Cmp_Le] = 0x0E; + pnc_map_signed[pn_Cmp_Gt] = 0x0F; + pnc_map_signed[pn_Cmp_Ge] = 0x0D; + pnc_map_signed[pn_Cmp_Lg] = 0x05; + + pnc_map_unsigned[pn_Cmp_Eq] = 0x04; + pnc_map_unsigned[pn_Cmp_Lt] = 0x02; + pnc_map_unsigned[pn_Cmp_Le] = 0x06; + pnc_map_unsigned[pn_Cmp_Gt] = 0x07; + pnc_map_unsigned[pn_Cmp_Ge] = 0x03; + pnc_map_unsigned[pn_Cmp_Lg] = 0x05; +} + +static unsigned char pnc2cc(int pnc) +{ + unsigned char cc; + if (pnc == ia32_pn_Cmp_parity) { + cc = 0x0A; + } else if (pnc & ia32_pn_Cmp_float || pnc & ia32_pn_Cmp_unsigned) { + cc = pnc_map_unsigned[pnc & 0x07]; + } else { + cc = pnc_map_signed[pnc & 0x07]; + } + assert(cc != 0); + return cc; +} + +/** Sign extension bit values for binops */ +enum SignExt { + UNSIGNED_IMM = 0, /**< unsigned immediate */ + SIGNEXT_IMM = 2, /**< sign extended immediate */ +}; + +/** The mod encoding of the ModR/M */ +enum Mod { + MOD_IND = 0x00, /**< [reg1] */ + MOD_IND_BYTE_OFS = 0x40, /**< [reg1 + byte ofs] */ + MOD_IND_WORD_OFS = 0x80, /**< [reg1 + word ofs] */ + MOD_REG = 0xC0 /**< reg1 */ +}; + +/** create R/M encoding for ModR/M */ +#define ENC_RM(x) (x) +/** create REG encoding for ModR/M */ +#define ENC_REG(x) ((x) << 3) + +/** create encoding for a SIB byte */ +#define ENC_SIB(scale, index, base) ((scale) << 6 | (index) << 3 | (base)) + +/* Node: The following routines are supposed to append bytes, words, dwords + to the output stream. + Currently the implementation is stupid in that it still creates output + for an "assembler" in the form of .byte, .long + We will change this when enough infrastructure is there to create complete + machine code in memory/object files */ + +static void bemit8(const unsigned char byte) +{ + be_emit_irprintf("\t.byte 0x%x\n", byte); + be_emit_write_line(); +} + +static void bemit16(const unsigned short u16) +{ + be_emit_irprintf("\t.word 0x%x\n", u16); + be_emit_write_line(); +} + +static void bemit32(const unsigned u32) +{ + be_emit_irprintf("\t.long 0x%x\n", u32); + be_emit_write_line(); +} + +/** + * Emit address of an entity. If @p is_relative is true then a relative + * offset from behind the address to the entity is created. + */ +static void bemit_entity(ir_entity *entity, bool entity_sign, int offset, + bool is_relative) +{ + if (entity == NULL) { + bemit32(offset); + return; + } + + if (is_relative) { + offset -= 4; + } + + /* the final version should remember the position in the bytestream + and patch it with the correct address at linktime... */ + be_emit_cstring("\t.long "); + if (entity_sign) + be_emit_char('-'); + set_entity_backend_marked(entity, 1); + be_gas_emit_entity(entity); + + if (is_relative) { + be_emit_cstring("-."); + } + + if (offset != 0) { + be_emit_irprintf("%+d", offset); + } + be_emit_char('\n'); + be_emit_write_line(); +} + +static void bemit_jmp_destination(const ir_node *dest_block) +{ + be_emit_cstring("\t.long "); + ia32_emit_block_name(dest_block); + be_emit_cstring(" - . - 4\n"); + be_emit_write_line(); +} + +/* end emit routines, all emitters following here should only use the functions + above. */ + +/** Create a ModR/M byte for src1,src2 registers */ +static void bemit_modrr(const arch_register_t *src1, + const arch_register_t *src2) +{ + unsigned char modrm = MOD_REG; + modrm |= ENC_RM(reg_gp_map[src1->index]); + modrm |= ENC_REG(reg_gp_map[src2->index]); + bemit8(modrm); +} + +/** Create a ModR/M byte for one register and extension */ +static void bemit_modru(const arch_register_t *reg, unsigned ext) +{ + unsigned char modrm = MOD_REG; + assert(ext <= 7); + modrm |= ENC_RM(reg_gp_map[reg->index]); + modrm |= ENC_REG(ext); + bemit8(modrm); +} + +/** + * Calculate the size of an (unsigned) immediate in bytes. + * + * @param offset an offset + */ +static unsigned get_unsigned_imm_size(unsigned offset) +{ + if (offset < 256) { + return 1; + } else if (offset < 65536) { + return 2; + } else { + return 4; + } +} + +/** + * Calculate the size of an signed immediate in bytes. + * + * @param offset an offset + */ +static unsigned get_signed_imm_size(int offset) +{ + if (-128 <= offset && offset < 128) { + return 1; + } else if (-32768 <= offset && offset < 32768) { + return 2; + } else { + return 4; + } +} + +/** + * Emit an address mode. + * + * @param reg content of the reg field: either a register index or an opcode extension + * @param node the node + */ +static void bemit_mod_am(unsigned reg, const ir_node *node) +{ + ir_entity *ent = get_ia32_am_sc(node); + int offs = get_ia32_am_offs_int(node); + ir_node *base = get_irn_n(node, n_ia32_base); + int has_base = !is_ia32_NoReg_GP(base); + ir_node *index = get_irn_n(node, n_ia32_index); + int has_index = !is_ia32_NoReg_GP(index); + unsigned modrm = 0; + unsigned sib = 0; + unsigned emitoffs = 0; + bool emitsib = false; + unsigned base_enc; + + /* set the mod part depending on displacement */ + if (ent != NULL) { + modrm |= MOD_IND_WORD_OFS; + emitoffs = 32; + } else if (offs == 0) { + modrm |= MOD_IND; + emitoffs = 0; + } else if (-128 <= offs && offs < 128) { + modrm |= MOD_IND_BYTE_OFS; + emitoffs = 8; + } else { + modrm |= MOD_IND_WORD_OFS; + emitoffs = 32; + } + + if (has_base) { + const arch_register_t *base_reg = arch_get_irn_register(base); + base_enc = reg_gp_map[base_reg->index]; + } else { + /* Use the EBP encoding + MOD_IND if NO base register. There is + * always a 32bit offset present in this case. */ + modrm = MOD_IND; + base_enc = 0x05; + emitoffs = 32; + } + + /* Determine if we need a SIB byte. */ + if (has_index) { + const arch_register_t *reg_index = arch_get_irn_register(index); + int scale = get_ia32_am_scale(node); + assert(scale < 4); + /* R/M set to ESP means SIB in 32bit mode. */ + modrm |= ENC_RM(0x04); + sib = ENC_SIB(scale, reg_gp_map[reg_index->index], base_enc); + emitsib = true; + } else if (base_enc == 0x04) { + /* for the above reason we are forced to emit a SIB when base is ESP. + * Only the base is used, index must be ESP too, which means no index. + */ + modrm |= ENC_RM(0x04); + sib = ENC_SIB(0, 0x04, 0x04); + emitsib = true; + } else { + modrm |= ENC_RM(base_enc); + } + + /* We are forced to emit an 8bit offset as EBP base without offset is a + * special case for SIB without base register. */ + if (base_enc == 0x05 && emitoffs == 0) { + modrm |= MOD_IND_BYTE_OFS; + emitoffs = 8; + } + + modrm |= ENC_REG(reg); + + bemit8(modrm); + if (emitsib) + bemit8(sib); + + /* emit displacement */ + if (emitoffs == 8) { + bemit8((unsigned) offs); + } else if (emitoffs == 32) { + bemit_entity(ent, is_ia32_am_sc_sign(node), offs, false); + } +} + +/** + * Emit a binop with a immediate operand. + * + * @param node the node to emit + * @param opcode_eax the opcode for the op eax, imm variant + * @param opcode the opcode for the reg, imm variant + * @param ruval the opcode extension for opcode + */ +static void bemit_binop_with_imm( + const ir_node *node, + unsigned char opcode_ax, + unsigned char opcode, unsigned char ruval) +{ + /* Use in-reg, because some instructions (cmp, test) have no out-reg. */ + const ir_node *op = get_irn_n(node, n_ia32_binary_right); + const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(op); + unsigned size; + + /* Some instructions (test) have no short form with 32bit value + 8bit + * immediate. */ + if (attr->symconst != NULL || opcode & SIGNEXT_IMM) { + size = 4; + } else { + /* check for sign extension */ + size = get_signed_imm_size(attr->offset); + } + + switch (size) { + case 1: + bemit8(opcode | SIGNEXT_IMM); + /* cmp has this special mode */ + if (get_ia32_op_type(node) == ia32_AddrModeS) { + bemit_mod_am(ruval, node); + } else { + const arch_register_t *reg = get_in_reg(node, n_ia32_binary_left); + bemit_modru(reg, ruval); + } + bemit8((unsigned char)attr->offset); + return; + case 2: + case 4: + /* check for eax variant: this variant is shorter for 32bit immediates only */ + if (get_ia32_op_type(node) == ia32_AddrModeS) { + bemit8(opcode); + bemit_mod_am(ruval, node); + } else { + const arch_register_t *reg = get_in_reg(node, n_ia32_binary_left); + if (reg->index == REG_EAX) { + bemit8(opcode_ax); + } else { + bemit8(opcode); + bemit_modru(reg, ruval); + } + } + bemit_entity(attr->symconst, attr->sc_sign, attr->offset, false); + return; + } + panic("invalid imm size?!?"); +} + +/** + * Emits a binop. + */ +static void bemit_binop_2(const ir_node *node, unsigned code) +{ + const arch_register_t *out = get_in_reg(node, n_ia32_binary_left); + ia32_op_type_t am_type = get_ia32_op_type(node); + unsigned char d = 0; + const arch_register_t *op2; + + switch (am_type) { + case ia32_AddrModeS: + d = 2; + /* FALLTHROUGH */ + case ia32_AddrModeD: + bemit8(code | d); + bemit_mod_am(reg_gp_map[out->index], node); + return; + case ia32_Normal: + bemit8(code); + op2 = get_in_reg(node, n_ia32_binary_right); + bemit_modrr(out, op2); + return; + } + panic("invalid address mode"); +} + +/** + * Emit a binop. + */ +static void bemit_binop(const ir_node *node, const unsigned char opcodes[4]) +{ + ir_node *right = get_irn_n(node, n_ia32_binary_right); + if (is_ia32_Immediate(right)) { + bemit_binop_with_imm(node, opcodes[1], opcodes[2], opcodes[3]); + } else { + bemit_binop_2(node, opcodes[0]); + } +} + +/** + * Emit an unop. + */ +static void bemit_unop(const ir_node *node, unsigned char code, unsigned char ext, int input) +{ + bemit8(code); + if (get_ia32_op_type(node) == ia32_Normal) { + const arch_register_t *in = get_in_reg(node, input); + bemit_modru(in, ext); + } else { + bemit_mod_am(ext, node); + } +} + +static void bemit_unop_reg(const ir_node *node, unsigned char code, int input) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit_unop(node, code, reg_gp_map[out->index], input); +} + +static void bemit_unop_mem(const ir_node *node, unsigned char code, unsigned char ext) +{ + bemit8(code); + bemit_mod_am(ext, node); +} + +static void bemit_immediate(const ir_node *node, bool relative) +{ + const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node); + bemit_entity(attr->symconst, attr->sc_sign, attr->offset, relative); +} + +static void bemit_copy(const ir_node *copy) +{ + const arch_register_t *in = get_in_reg(copy, 0); + const arch_register_t *out = get_out_reg(copy, 0); + + if (in == out || is_unknown_reg(in)) + return; + /* copies of vf nodes aren't real... */ + if (arch_register_get_class(in) == &ia32_reg_classes[CLASS_ia32_vfp]) + return; + + if (get_irn_mode(copy) == mode_E) { + panic("NIY"); + } else { + assert(arch_register_get_class(in) == &ia32_reg_classes[CLASS_ia32_gp]); + bemit8(0x89); + bemit_modrr(out, in); + } +} + +static void bemit_perm(const ir_node *node) +{ + const arch_register_t *in0 = arch_get_irn_register(get_irn_n(node, 0)); + const arch_register_t *in1 = arch_get_irn_register(get_irn_n(node, 1)); + const arch_register_class_t *cls0 = arch_register_get_class(in0); + + assert(cls0 == arch_register_get_class(in1) && "Register class mismatch at Perm"); + + if (cls0 == &ia32_reg_classes[CLASS_ia32_gp]) { + if (in0->index == REG_EAX) { + bemit8(0x90 + reg_gp_map[in1->index]); + } else if (in1->index == REG_EAX) { + bemit8(0x90 + reg_gp_map[in0->index]); + } else { + bemit8(0x87); + bemit_modrr(in0, in1); + } + } else if (cls0 == &ia32_reg_classes[CLASS_ia32_xmm]) { + panic("unimplemented"); // TODO implement + //ia32_emitf(NULL, "\txorpd %R, %R\n", in1, in0); + //ia32_emitf(NULL, "\txorpd %R, %R\n", in0, in1); + //ia32_emitf(node, "\txorpd %R, %R\n", in1, in0); + } else if (cls0 == &ia32_reg_classes[CLASS_ia32_vfp]) { + /* is a NOP */ + } else if (cls0 == &ia32_reg_classes[CLASS_ia32_st]) { + /* is a NOP */ + } else { + panic("unexpected register class in be_Perm (%+F)", node); + } +} + +static void bemit_xor0(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit8(0x31); + bemit_modrr(out, out); +} + +static void bemit_mov_const(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit8(0xB8 + reg_gp_map[out->index]); + bemit_immediate(node, false); +} + +/** + * Creates a function for a Binop with 3 possible encodings. + */ +#define BINOP(op, op0, op1, op2, op2_ext) \ +static void bemit_ ## op(const ir_node *node) { \ + static const unsigned char op ## _codes[] = {op0, op1, op2, op2_ext}; \ + bemit_binop(node, op ## _codes); \ +} + +/* insn def eax,imm imm */ +BINOP(add, 0x01, 0x05, 0x81, 0) +BINOP(or, 0x09, 0x0D, 0x81, 1) +BINOP(adc, 0x11, 0x15, 0x81, 2) +BINOP(sbb, 0x19, 0x1D, 0x81, 3) +BINOP(and, 0x21, 0x25, 0x81, 4) +BINOP(sub, 0x29, 0x2D, 0x81, 5) +BINOP(xor, 0x31, 0x35, 0x81, 6) +BINOP(cmp, 0x39, 0x3D, 0x81, 7) +BINOP(test, 0x85, 0xA9, 0xF7, 0) + +/** + * Creates a function for an Unop with code /ext encoding. + */ +#define UNOP(op, code, ext, input) \ +static void bemit_ ## op(const ir_node *node) { \ + bemit_unop(node, code, ext, input); \ +} + +UNOP(not, 0xF7, 2, n_ia32_Not_val) +UNOP(neg, 0xF7, 3, n_ia32_Neg_val) +UNOP(mul, 0xF7, 4, n_ia32_Mul_right) +UNOP(imul1op, 0xF7, 5, n_ia32_IMul1OP_right) +UNOP(div, 0xF7, 6, n_ia32_Div_divisor) +UNOP(idiv, 0xF7, 7, n_ia32_IDiv_divisor) + +/* TODO: am support for IJmp */ +UNOP(ijmp, 0xFF, 4, n_ia32_IJmp_target) + +#define SHIFT(op, ext) \ +static void bemit_##op(const ir_node *node) \ +{ \ + const arch_register_t *out = get_out_reg(node, 0); \ + ir_node *count = get_irn_n(node, 1); \ + if (is_ia32_Immediate(count)) { \ + int offset = get_ia32_immediate_attr_const(count)->offset; \ + if (offset == 1) { \ + bemit8(0xD1); \ + bemit_modru(out, ext); \ + } else { \ + bemit8(0xC1); \ + bemit_modru(out, ext); \ + bemit8(offset); \ + } \ + } else { \ + bemit8(0xD3); \ + bemit_modru(out, ext); \ + } \ +} + +SHIFT(rol, 0) +SHIFT(ror, 1) +SHIFT(shl, 4) +SHIFT(shr, 5) +SHIFT(sar, 7) + +static void bemit_cmp8bit(const ir_node *node) +{ + ir_node *right = get_irn_n(node, n_ia32_binary_right); + if (is_ia32_Immediate(right)) { + if (get_ia32_op_type(node) == ia32_Normal) { + const arch_register_t *out = get_in_reg(node, n_ia32_Cmp_left); + if (out->index == REG_EAX) { + bemit8(0x3C); + } else { + bemit8(0x80); + bemit_modru(out, 7); + } + } else { + bemit8(0x80); + bemit_mod_am(7, node); + } + bemit8(get_ia32_immediate_attr_const(right)->offset); + } else { + bemit8(0x3A); + const arch_register_t *out = get_in_reg(node, n_ia32_Cmp_left); + if (get_ia32_op_type(node) == ia32_Normal) { + const arch_register_t *in = get_in_reg(node, n_ia32_Cmp_right); + bemit_modrr(out, in); + } else { + bemit_mod_am(reg_gp_map[out->index], node); + } + } +} + +static void bemit_imul(const ir_node *node) +{ + ir_node *right = get_irn_n(node, n_ia32_IMul_right); + /* Do we need the immediate form? */ + if (is_ia32_Immediate(right)) { + int imm = get_ia32_immediate_attr_const(right)->offset; + if (get_signed_imm_size(imm) == 1) { + bemit_unop_reg(node, 0x6B, n_ia32_IMul_left); + bemit8(imm); + } else { + bemit_unop_reg(node, 0x69, n_ia32_IMul_left); + bemit32(imm); + } + } else { + bemit8(0x0F); + bemit_unop_reg(node, 0xAF, n_ia32_IMul_right); + } +} + +static void bemit_dec(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, pn_ia32_Dec_res); + bemit8(0x48 + reg_gp_map[out->index]); +} + +static void bemit_inc(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, pn_ia32_Inc_res); + bemit8(0x40 + reg_gp_map[out->index]); +} + +#define UNOPMEM(op, code, ext) \ +static void bemit_##op(const ir_node *node) \ +{ \ + bemit_unop_mem(node, code, ext); \ +} + +UNOPMEM(notmem, 0xF7, 2) +UNOPMEM(negmem, 0xF7, 3) +UNOPMEM(incmem, 0xFF, 0) +UNOPMEM(decmem, 0xFF, 1) + +static void bemit_set(const ir_node *node) +{ + pn_Cmp pnc; + + bemit8(0x0F); + + pnc = get_ia32_condcode(node); + pnc = determine_final_pnc(node, n_ia32_Set_eflags, pnc); + if (get_ia32_attr_const(node)->data.ins_permuted) + pnc = ia32_get_negated_pnc(pnc); + + bemit8(0x90 + pnc2cc(pnc)); + bemit_modru(get_out_reg(node, pn_ia32_Set_res), 2); +} + +/** + * Emit a Lea. + */ +static void bemit_lea(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit8(0x8D); + bemit_mod_am(reg_gp_map[out->index], node); +} + +/** + * Emit a single opcode. + */ +#define EMIT_SINGLEOP(op, code) \ +static void bemit_ ## op(const ir_node *node) { \ + (void) node; \ + bemit8(code); \ +} + +//EMIT_SINGLEOP(daa, 0x27) +//EMIT_SINGLEOP(das, 0x2F) +//EMIT_SINGLEOP(aaa, 0x37) +//EMIT_SINGLEOP(aas, 0x3F) +//EMIT_SINGLEOP(nop, 0x90) +EMIT_SINGLEOP(cwtl, 0x98) +EMIT_SINGLEOP(cltd, 0x99) +//EMIT_SINGLEOP(fwait, 0x9B) +EMIT_SINGLEOP(sahf, 0x9E) +//EMIT_SINGLEOP(popf, 0x9D) +EMIT_SINGLEOP(int3, 0xCC) +//EMIT_SINGLEOP(iret, 0xCF) +//EMIT_SINGLEOP(xlat, 0xD7) +//EMIT_SINGLEOP(lock, 0xF0) +EMIT_SINGLEOP(rep, 0xF3) +//EMIT_SINGLEOP(halt, 0xF4) +EMIT_SINGLEOP(cmc, 0xF5) +EMIT_SINGLEOP(stc, 0xF9) +//EMIT_SINGLEOP(cli, 0xFA) +//EMIT_SINGLEOP(sti, 0xFB) +//EMIT_SINGLEOP(std, 0xFD) + +/** + * Emits a MOV out, [MEM]. + */ +static void bemit_load(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + + if (out->index == REG_EAX) { + ir_node *base = get_irn_n(node, n_ia32_base); + int has_base = !is_ia32_NoReg_GP(base); + ir_node *index = get_irn_n(node, n_ia32_index); + int has_index = !is_ia32_NoReg_GP(index); + if (!has_base && !has_index) { + ir_entity *ent = get_ia32_am_sc(node); + int offs = get_ia32_am_offs_int(node); + /* load from constant address to EAX can be encoded + as 0xA1 [offset] */ + bemit8(0xA1); + bemit_entity(ent, 0, offs, false); + return; + } + } + bemit8(0x8B); + bemit_mod_am(reg_gp_map[out->index], node); +} + +/** + * Emits a MOV [mem], in. + */ +static void bemit_store(const ir_node *node) +{ + const ir_node *value = get_irn_n(node, n_ia32_Store_val); + unsigned size = get_mode_size_bits(get_ia32_ls_mode(node)); + + if (is_ia32_Immediate(value)) { + if (size == 8) { + bemit8(0xC6); + bemit_mod_am(0, node); + bemit8(get_ia32_immediate_attr_const(value)->offset); + } else if (size == 16) { + bemit8(0x66); + bemit8(0xC7); + bemit_mod_am(0, node); + bemit16(get_ia32_immediate_attr_const(value)->offset); + } else { + bemit8(0xC7); + bemit_mod_am(0, node); + bemit_immediate(value, false); + } + } else { + const arch_register_t *in = get_in_reg(node, n_ia32_Store_val); + + if (in->index == REG_EAX) { + ir_node *base = get_irn_n(node, n_ia32_base); + int has_base = !is_ia32_NoReg_GP(base); + ir_node *index = get_irn_n(node, n_ia32_index); + int has_index = !is_ia32_NoReg_GP(index); + if (!has_base && !has_index) { + ir_entity *ent = get_ia32_am_sc(node); + int offs = get_ia32_am_offs_int(node); + /* store to constant address from EAX can be encoded as + * 0xA2/0xA3 [offset]*/ + if (size == 8) { + bemit8(0xA2); + } else { + if (size == 16) + bemit8(0x66); + bemit8(0xA3); + } + bemit_entity(ent, 0, offs, false); + return; + } + } + + if (size == 8) { + bemit8(0x88); + } else { + if (size == 16) + bemit8(0x66); + bemit8(0x89); + } + bemit_mod_am(reg_gp_map[in->index], node); + } +} + +static void bemit_conv_i2i(const ir_node *node) +{ + ir_mode *smaller_mode = get_ia32_ls_mode(node); + unsigned opcode; + + bemit8(0x0F); + /* 8 16 bit source + * movzx B6 B7 + * movsx BE BF + */ + opcode = 0xB6; + if (mode_is_signed(smaller_mode)) opcode |= 0x08; + if (get_mode_size_bits(smaller_mode) == 16) opcode |= 0x01; + bemit_unop_reg(node, opcode, n_ia32_Conv_I2I_val); +} + +/** + * Emit a Push. + */ +static void bemit_push(const ir_node *node) +{ + const ir_node *value = get_irn_n(node, n_ia32_Push_val); + + if (is_ia32_Immediate(value)) { + const ia32_immediate_attr_t *attr + = get_ia32_immediate_attr_const(value); + unsigned size = get_unsigned_imm_size(attr->offset); + if (attr->symconst) + size = 4; + switch (size) { + case 1: + bemit8(0x6A); + bemit8((unsigned char)attr->offset); + break; + case 2: + case 4: + bemit8(0x68); + bemit_immediate(value, false); + break; + } + } else if (is_ia32_NoReg_GP(value)) { + bemit8(0xFF); + bemit_mod_am(6, node); + } else { + const arch_register_t *reg = get_in_reg(node, n_ia32_Push_val); + bemit8(0x50 + reg_gp_map[reg->index]); + } +} + +/** + * Emit a Pop. + */ +static void bemit_pop(const ir_node *node) +{ + const arch_register_t *reg = get_out_reg(node, pn_ia32_Pop_res); + bemit8(0x58 + reg_gp_map[reg->index]); +} + +static void bemit_popmem(const ir_node *node) +{ + bemit8(0x8F); + bemit_mod_am(0, node); +} + +static void bemit_call(const ir_node *node) +{ + ir_node *proc = get_irn_n(node, n_ia32_Call_addr); + + if (is_ia32_Immediate(proc)) { + bemit8(0xE8); + bemit_immediate(proc, true); + } else { + bemit_unop(node, 0xFF, 2, n_ia32_Call_addr); + } +} + +static void bemit_jmp(const ir_node *dest_block) +{ + bemit8(0xE9); + bemit_jmp_destination(dest_block); +} + +static void bemit_jump(const ir_node *node) +{ + if (can_be_fallthrough(node)) + return; + + bemit_jmp(get_cfop_target_block(node)); +} + +static void bemit_jcc(int pnc, const ir_node *dest_block) +{ + unsigned char cc = pnc2cc(pnc); + bemit8(0x0F); + bemit8(0x80 + cc); + bemit_jmp_destination(dest_block); +} + +static void bemit_ia32_jcc(const ir_node *node) +{ + int pnc = get_ia32_condcode(node); + int need_parity_label = 0; + const ir_node *proj_true; + const ir_node *proj_false; + const ir_node *dest_true; + const ir_node *dest_false; + const ir_node *block; + + pnc = determine_final_pnc(node, 0, pnc); + + /* get both Projs */ + proj_true = get_proj(node, pn_ia32_Jcc_true); + assert(proj_true && "Jcc without true Proj"); + + proj_false = get_proj(node, pn_ia32_Jcc_false); + assert(proj_false && "Jcc without false Proj"); + + block = get_nodes_block(node); + + if (can_be_fallthrough(proj_true)) { + /* exchange both proj's so the second one can be omitted */ + const ir_node *t = proj_true; + + proj_true = proj_false; + proj_false = t; + pnc = ia32_get_negated_pnc(pnc); + } + + dest_true = get_cfop_target_block(proj_true); + dest_false = get_cfop_target_block(proj_false); + + if (pnc & ia32_pn_Cmp_float) { + panic("Float jump NIY"); + /* Some floating point comparisons require a test of the parity flag, + * which indicates that the result is unordered */ + switch (pnc & 15) { + case pn_Cmp_Uo: { + ia32_emitf(proj_true, "\tjp %L\n"); + break; + } + + case pn_Cmp_Leg: + ia32_emitf(proj_true, "\tjnp %L\n"); + break; + + case pn_Cmp_Eq: + case pn_Cmp_Lt: + case pn_Cmp_Le: + /* we need a local label if the false proj is a fallthrough + * as the falseblock might have no label emitted then */ + if (can_be_fallthrough(proj_false)) { + need_parity_label = 1; + ia32_emitf(proj_false, "\tjp 1f\n"); + } else { + ia32_emitf(proj_false, "\tjp %L\n"); + } + goto emit_jcc; + + case pn_Cmp_Ug: + case pn_Cmp_Uge: + case pn_Cmp_Ne: + ia32_emitf(proj_true, "\tjp %L\n"); + goto emit_jcc; + + default: + goto emit_jcc; + } + } else { +emit_jcc: + bemit_jcc(pnc, dest_true); + } + + if (need_parity_label) { + panic("parity label NIY"); + } + + /* the second Proj might be a fallthrough */ + if (can_be_fallthrough(proj_false)) { + /* it's a fallthrough */ + } else { + bemit_jmp(dest_false); + } +} + +/** + * Emits a return. + */ +static void bemit_return(const ir_node *node) +{ + unsigned pop = be_Return_get_pop(node); + if (pop > 0 || be_Return_get_emit_pop(node)) { + bemit8(0xC2); + assert(pop <= 0xffff); + bemit16(pop); + } else { + bemit8(0xC3); + } +} + +static void bemit_incsp(const ir_node *node) +{ + int offs; + const arch_register_t *reg; + unsigned size; + unsigned ext; + + offs = be_get_IncSP_offset(node); + if (offs == 0) + return; + + if (offs > 0) { + ext = 5; /* sub */ + } else { + ext = 0; /* add */ + offs = -offs; + } + + size = get_signed_imm_size(offs); + bemit8(size == 1 ? 0x83 : 0x81); + + reg = get_out_reg(node, 0); + bemit_modru(reg, ext); + + if (size == 1) { + bemit8(offs); + } else { + bemit32(offs); + } +} + +/** + * The type of a emitter function. + */ +typedef void (*emit_func) (const ir_node *); + +/** + * Set a node emitter. Make it a bit more type safe. + */ +static void register_emitter(ir_op *op, emit_func func) +{ + op->ops.generic = (op_func) func; +} + +static void ia32_register_binary_emitters(void) +{ + /* first clear the generic function pointer for all ops */ + clear_irp_opcodes_generic_func(); + + /* benode emitter */ + register_emitter(op_be_Copy, bemit_copy); + register_emitter(op_be_CopyKeep, bemit_copy); + register_emitter(op_be_IncSP, bemit_incsp); + register_emitter(op_be_Perm, bemit_perm); + register_emitter(op_be_Return, bemit_return); + register_emitter(op_ia32_Adc, bemit_adc); + register_emitter(op_ia32_Add, bemit_add); + register_emitter(op_ia32_And, bemit_and); + register_emitter(op_ia32_Breakpoint, bemit_int3); + register_emitter(op_ia32_Call, bemit_call); + register_emitter(op_ia32_Cltd, bemit_cltd); + register_emitter(op_ia32_Cmc, bemit_cmc); + register_emitter(op_ia32_Cmp8Bit, bemit_cmp8bit); + register_emitter(op_ia32_Cmp, bemit_cmp); + register_emitter(op_ia32_Const, bemit_mov_const); + register_emitter(op_ia32_Conv_I2I8Bit, bemit_conv_i2i); + register_emitter(op_ia32_Conv_I2I, bemit_conv_i2i); + register_emitter(op_ia32_Cwtl, bemit_cwtl); + register_emitter(op_ia32_Dec, bemit_dec); + register_emitter(op_ia32_DecMem, bemit_decmem); + register_emitter(op_ia32_Div, bemit_div); + register_emitter(op_ia32_IDiv, bemit_idiv); + register_emitter(op_ia32_IJmp, bemit_ijmp); + register_emitter(op_ia32_IMul1OP, bemit_imul1op); + register_emitter(op_ia32_IMul, bemit_imul); + register_emitter(op_ia32_Inc, bemit_inc); + register_emitter(op_ia32_IncMem, bemit_incmem); + register_emitter(op_ia32_Jcc, bemit_ia32_jcc); + register_emitter(op_ia32_Jmp, bemit_jump); + register_emitter(op_ia32_Lea, bemit_lea); + register_emitter(op_ia32_Load, bemit_load); + register_emitter(op_ia32_Mul, bemit_mul); + register_emitter(op_ia32_Neg, bemit_neg); + register_emitter(op_ia32_NegMem, bemit_negmem); + register_emitter(op_ia32_Not, bemit_not); + register_emitter(op_ia32_NotMem, bemit_notmem); + register_emitter(op_ia32_Or, bemit_or); + register_emitter(op_ia32_Pop, bemit_pop); + register_emitter(op_ia32_PopEbp, bemit_pop); + register_emitter(op_ia32_PopMem, bemit_popmem); + register_emitter(op_ia32_Push, bemit_push); + register_emitter(op_ia32_RepPrefix, bemit_rep); + register_emitter(op_ia32_Rol, bemit_rol); + register_emitter(op_ia32_Ror, bemit_ror); + register_emitter(op_ia32_Sahf, bemit_sahf); + register_emitter(op_ia32_Sar, bemit_sar); + register_emitter(op_ia32_Sbb, bemit_sbb); + register_emitter(op_ia32_Set, bemit_set); + register_emitter(op_ia32_Shl, bemit_shl); + register_emitter(op_ia32_Shr, bemit_shr); + register_emitter(op_ia32_Stc, bemit_stc); + register_emitter(op_ia32_Store8Bit, bemit_store); + register_emitter(op_ia32_Store, bemit_store); + register_emitter(op_ia32_Sub, bemit_sub); + register_emitter(op_ia32_Test, bemit_test); + register_emitter(op_ia32_Xor0, bemit_xor0); + register_emitter(op_ia32_Xor, bemit_xor); + + /* ignore the following nodes */ + register_emitter(op_ia32_ProduceVal, emit_Nothing); + register_emitter(op_be_Barrier, emit_Nothing); + register_emitter(op_be_Keep, emit_Nothing); + register_emitter(op_be_Start, emit_Nothing); + register_emitter(op_Phi, emit_Nothing); + register_emitter(op_Start, emit_Nothing); +} + +static void gen_binary_block(ir_node *block) +{ + ir_node *node; + + ia32_emit_block_header(block); + + /* emit the contents of the block */ + sched_foreach(block, node) { + ia32_emit_node(node); + } +} + +void ia32_gen_binary_routine(ia32_code_gen_t *ia32_cg, ir_graph *irg) +{ + ir_entity *entity = get_irg_entity(irg); + int i, n; + + cg = ia32_cg; + isa = cg->isa; + + ia32_register_binary_emitters(); + + be_gas_emit_function_prolog(entity, ia32_cg_config.function_alignment); + + /* we use links to point to target blocks */ + ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK); + irg_block_walk_graph(irg, ia32_gen_labels, NULL, NULL); + + /* initialize next block links */ + n = ARR_LEN(cg->blk_sched); + for (i = 0; i < n; ++i) { + ir_node *block = cg->blk_sched[i]; + ir_node *prev = i > 0 ? cg->blk_sched[i-1] : NULL; + + set_irn_link(block, prev); + } + + for (i = 0; i < n; ++i) { + ir_node *block = cg->blk_sched[i]; + gen_binary_block(block); + } + + be_gas_emit_function_epilog(entity); + be_dbg_method_end(); + be_emit_char('\n'); + be_emit_write_line(); + + ir_free_resources(irg, IR_RESOURCE_IRN_LINK); +} + + + + void ia32_init_emitter(void) { lc_opt_entry_t *be_grp; @@ -2231,5 +3365,7 @@ void ia32_init_emitter(void) lc_opt_add_table(ia32_grp, ia32_emitter_options); + build_reg_map(); + FIRM_DBG_REGISTER(dbg, "firm.be.ia32.emitter"); }