From 65a677516d45936d113d4170932c52c4b9364891 Mon Sep 17 00:00:00 2001 From: Matthias Braun Date: Thu, 17 Sep 2009 14:12:40 +0000 Subject: [PATCH] experimental beginning of a binary emitter I had lying around here [r26539] --- ir/be/ia32/ia32_emitter.c | 526 ++++++++++++++++++++++++++++++++++++++ ir/be/ia32/ia32_emitter.h | 1 + 2 files changed, 527 insertions(+) diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index cc1af638e..424b81a29 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -2223,6 +2223,530 @@ static const lc_opt_table_entry_t ia32_emitter_options[] = { LC_OPT_LAST }; +/* ==== Experimental binary emitter ==== */ + +static unsigned char reg_map[N_ia32_gp_REGS]; + +static void build_reg_map(void) +{ + reg_map[REG_EAX] = 0x0; + reg_map[REG_ECX] = 0x1; + reg_map[REG_EDX] = 0x2; + reg_map[REG_EBX] = 0x3; + reg_map[REG_ESP] = 0x4; + reg_map[REG_EBP] = 0x5; + reg_map[REG_ESI] = 0x6; + reg_map[REG_EDI] = 0x7; +} + +/* 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 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(); +} + +static void bemit_entity(ir_entity *entity, bool entity_sign, int offset, + bool is_relative) +{ + if (entity == NULL) { + bemit32(offset); + return; + } + + /* 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(); +} + +/* end emit routines, all emitters following here should only use the functions + above. */ + +static void bemit_modrr(const arch_register_t *op1_dest, + const arch_register_t *op2) +{ + unsigned char modrm = 0xC0; + modrm |= reg_map[op1_dest->index]; + modrm |= reg_map[op2->index] << 3; + bemit8(modrm); +} + +static void bemit_modru(const arch_register_t *dest, unsigned val) +{ + assert(val <= 7); + unsigned char modrm = 0xC0; + modrm |= reg_map[dest->index]; + modrm |= val << 3; + bemit8(modrm); +} + +static unsigned get_imm_size(ir_entity *entity, int offset) +{ + if (entity != NULL) + return 32; + if (offset >= -127 && offset < 128) { + return 8; + } else if (offset >= -32768 && offset < 32767) { + return 16; + } else { + return 32; + } +} + +static void bemit_binop_with_imm(const ir_node *node, unsigned opimm8, + unsigned opimm32, unsigned ruval) +{ + const arch_register_t *reg = get_out_reg(node, 0); + 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 = get_imm_size(attr->symconst, attr->offset); + + switch (size) { + case 8: + bemit8(opimm8); + bemit_modru(reg, ruval); + bemit8(attr->offset); + return; + case 16: + case 32: + bemit8(opimm32); + bemit_modru(reg, ruval); + bemit_entity(attr->symconst, attr->sc_sign, attr->offset, false); + return; + } + panic("invalid imm size?!?"); +} + +static void bemit_modsourceam(unsigned dest_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; + + /* set the mod part depending on displacement */ + if (ent != NULL) { + modrm |= 0x80; + emitoffs = 32; + } else if (offs == 0) { + emitoffs = 0; + } else if (offs >= -127 && offs <= 128) { + modrm |= 0x40; + emitoffs = 8; + } else { + modrm |= 0x80; + emitoffs = 32; + } + + /* determine if we need a SIB byte */ + if (has_index) { + const arch_register_t *reg_index = arch_get_irn_register(index); + assert(reg_index->index != REG_ESP); + sib |= reg_map[reg_index->index] << 3; + + if (has_base) { + const arch_register_t *reg = arch_get_irn_register(base); + sib |= reg_map[reg->index]; + } else { + sib |= 0x05; + } + + int scale = get_ia32_am_scale(node); + assert(scale < 4); + sib |= scale << 6; + emitsib = true; + } + + /* determine modrm byte */ + if (emitsib) { + modrm |= 0x04; + } else if (has_base) { + const arch_register_t *reg = arch_get_irn_register(base); + /* we are forced to emit a sib when base is ESP */ + if (reg->index == REG_ESP) { + sib = 0x24; + emitsib = true; + + /* we are forced to emit a 32bit offset as EBP base without + offset is a special case for displacement without base */ + } else if (reg->index == REG_EBP && emitoffs == 0) { + assert( (modrm & 0xC0) == 0); + emitoffs = 8; + modrm |= 0x40; + } + modrm |= reg_map[reg->index]; + } else { + modrm = 0x05; + emitoffs = 32; + } + + modrm |= dest_reg << 3; + + 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); + } +} + +static void bemit_binop(const ir_node *node, unsigned modrr, unsigned am) +{ + const arch_register_t *out = get_in_reg(node, n_ia32_binary_left); + if (get_ia32_op_type(node) == ia32_AddrModeS) { + bemit8(am); + bemit_modsourceam(reg_map[out->index], node); + } else { + const arch_register_t *op2 = get_in_reg(node, n_ia32_binary_right); + assert(get_ia32_op_type(node) == ia32_Normal); + bemit8(modrr); + bemit_modrr(out, op2); + } +} + +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 ir_node *op = be_get_Copy_op(copy); + const arch_register_t *in = arch_get_irn_register(op); + const arch_register_t *out = arch_get_irn_register(copy); + + 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_xor0(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit8(0x31); + bemit_modrr(out, out); +} + +static void bemit_const(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit8(0xB8 + reg_map[out->index]); + bemit_immediate(node, false); +} + +static void bemit_add(const ir_node *node) +{ + ir_node *right = get_irn_n(node, n_ia32_binary_right); + if (is_ia32_Immediate(right)) { + /* TODO: there's a shorter variant with DEST=EAX */ + bemit_binop_with_imm(node, 0x83, 0x81, 0); + } else { + bemit_binop(node, 0x01, 0x03); + } +} + +static void bemit_sub(const ir_node *node) +{ + ir_node *right = get_irn_n(node, n_ia32_binary_right); + if (is_ia32_Immediate(right)) { + /* TODO: there's a shorter variant with DEST=EAX */ + bemit_binop_with_imm(node, 0x83, 0x81, 5); + } else { + bemit_binop(node, 0x29, 0x2B); + } +} + +static void bemit_xor(const ir_node *node) +{ + ir_node *right = get_irn_n(node, n_ia32_binary_right); + if (is_ia32_Immediate(right)) { + /* TODO: there's a shorter variant with DEST=EAX */ + bemit_binop_with_imm(node, 0x83, 0x81, 6); + } else { + bemit_binop(node, 0x31, 0x33); + } +} + +static void bemit_not(const ir_node *node) +{ + const arch_register_t *reg = get_out_reg(node, 0); + bemit8(0xF7); + bemit_modru(reg, 2); +} + +static void bemit_lea(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + bemit8(0x8D); + bemit_modsourceam(reg_map[out->index], node); +} + +static void bemit_cltd(const ir_node *node) +{ + (void) node; + bemit8(0x99); +} + +static void bemit_load(const ir_node *node) +{ + const arch_register_t *out = get_out_reg(node, 0); + + /* TODO: load from constant address to EAX can be encoded + as 0xA1 [offset] */ + bemit8(0x8B); + bemit_modsourceam(reg_map[out->index], node); +} + +static void bemit_store(const ir_node *node) +{ + const ir_node *value = get_irn_n(node, n_ia32_Store_val); + + if (is_ia32_Immediate(value)) { + bemit8(0xC7); + bemit_modsourceam(0, node); + bemit_immediate(value, false); + } else { + /* TODO: store to constant address from EAX can be encoded as + 0xA3 [offset]*/ + const arch_register_t *in = get_in_reg(node, n_ia32_Store_val); + bemit8(0x89); + bemit_modsourceam(reg_map[in->index], node); + } +} + +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_imm_size(attr->symconst, attr->offset); + /* TODO: check for bitsizes different from 32... */ + switch (size) { + case 8: + bemit8(0x6A); + bemit8(attr->offset); + break; + case 16: + case 32: + bemit8(0x68); + bemit_immediate(value, false); + break; + } + } else { + bemit8(0xFF); + bemit_modsourceam(6, node); + } +} + +static void bemit_pop(const ir_node *node) +{ + const arch_register_t *reg = get_out_reg(node, pn_ia32_Pop_res); + /* TODO: check for AM pop */ + bemit8(0x58 + reg_map[reg->index]); +} + +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 { + panic("indirect call NIY"); + } +} + +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) +{ + const arch_register_t *reg = get_out_reg(node, 0); + int offs = be_get_IncSP_offset(node); + unsigned size = get_imm_size(NULL, offs); + + if (offs > 0) { + bemit8(size == 8 ? 0x83 : 0x81); + bemit_modru(reg, 5); /* sub */ + if (size == 8) { + bemit8(offs); + } else { + bemit32(offs); + } + } else if (offs < 0) { + bemit8(size == 8 ? 0x83 : 0x81); + bemit_modru(reg, 0); /* add */ + if (size == 8) { + 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_Return, bemit_return); + register_emitter(op_be_IncSP, bemit_incsp); + register_emitter(op_ia32_Add, bemit_add); + register_emitter(op_ia32_Call, bemit_call); + register_emitter(op_ia32_Cltd, bemit_cltd); + register_emitter(op_ia32_Sub, bemit_sub); + register_emitter(op_ia32_Xor0, bemit_xor0); + register_emitter(op_ia32_Xor, bemit_xor); + register_emitter(op_ia32_Const, bemit_const); + register_emitter(op_ia32_Lea, bemit_lea); + register_emitter(op_ia32_Load, bemit_load); + register_emitter(op_ia32_Not, bemit_not); + register_emitter(op_ia32_Push, bemit_push); + register_emitter(op_ia32_Pop, bemit_pop); + register_emitter(op_ia32_Store, bemit_store); + + /* 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_RegParams, 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; @@ -2233,5 +2757,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"); } diff --git a/ir/be/ia32/ia32_emitter.h b/ir/be/ia32/ia32_emitter.h index f490c462b..0b22c01b1 100644 --- a/ir/be/ia32/ia32_emitter.h +++ b/ir/be/ia32/ia32_emitter.h @@ -53,6 +53,7 @@ void ia32_emit_am(const ir_node *node); void ia32_emit_x87_binop(const ir_node *node); void ia32_gen_routine(ia32_code_gen_t *cg, ir_graph *irg); +void ia32_gen_binary_routine(ia32_code_gen_t *ia32_cg, ir_graph *irg); /** Initializes the Emitter. */ void ia32_init_emitter(void); -- 2.20.1