X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fia32%2Fia32_emitter.c;h=4b652f9a12e151cc56be06d5a51a362a30bdc3cf;hb=88209e771a7f2f0409e1d563bc126e4174728944;hp=358e1cecc5ee5fae142ce0bbedae193e6ded3368;hpb=3b1a4ee674e9522acfd95b6d49c91f86f9b97d32;p=libfirm diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index 358e1cecc..4b652f9a1 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -1,9 +1,16 @@ +/** + * This file implements the node emitter. + * + * $Id$ + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif #include +#include "xmalloc.h" #include "tv.h" #include "iredges.h" #include "debug.h" @@ -11,8 +18,11 @@ #include "irprintf.h" #include "irop_t.h" #include "irargs_t.h" +#include "irprog_t.h" +#include "iredges_t.h" #include "../besched.h" +#include "../benode_t.h" #include "ia32_emitter.h" #include "gen_ia32_emitter.h" @@ -20,11 +30,20 @@ #include "ia32_new_nodes.h" #include "ia32_map_regs.h" +#ifdef obstack_chunk_alloc +# undef obstack_chunk_alloc +# define obstack_chunk_alloc xmalloc +#else +# define obstack_chunk_alloc xmalloc +# define obstack_chunk_free free +#endif + +extern int obstack_printf(struct obstack *obst, char *fmt, ...); + #define SNPRINTF_BUF_LEN 128 static const arch_env_t *arch_env = NULL; - /************************************************************* * _ _ __ _ _ * (_) | | / _| | | | | @@ -36,41 +55,6 @@ static const arch_env_t *arch_env = NULL; * |_| |_| *************************************************************/ -/** - * Return node's tarval as string. - */ -const char *node_const_to_str(ir_node *n) { - char *buf; - tarval *tv = get_ia32_Immop_tarval(n); - - if (tv) { - buf = malloc(SNPRINTF_BUF_LEN); - tarval_snprintf(buf, SNPRINTF_BUF_LEN, tv); - return buf; - } - else if (get_ia32_old_ir(n)) { - return get_sc_name(get_ia32_old_ir(n)); - } - else - return "0"; -} - -/** - * Returns node's offset as string. - */ -char *node_offset_to_str(ir_node *n) { - char *buf; - tarval *tv = get_ia32_am_offs(n); - - if (tv) { - buf = malloc(SNPRINTF_BUF_LEN); - tarval_snprintf(buf, SNPRINTF_BUF_LEN, tv); - return buf; - } - else - return ""; -} - /* We always pass the ir_node which is a pointer. */ static int ia32_get_arg_type(const lc_arg_occ_t *occ) { return lc_arg_type_ptr; @@ -80,7 +64,7 @@ static int ia32_get_arg_type(const lc_arg_occ_t *occ) { /** * Returns the register at in position pos. */ -static const arch_register_t *get_in_reg(ir_node *irn, int pos) { +static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { ir_node *op; const arch_register_t *reg = NULL; @@ -99,12 +83,10 @@ static const arch_register_t *get_in_reg(ir_node *irn, int pos) { /** * Returns the register at out position pos. */ -static const arch_register_t *get_out_reg(ir_node *irn, int pos) { +static const arch_register_t *get_out_reg(const ir_node *irn, int pos) { ir_node *proj; const arch_register_t *reg = NULL; - assert(get_irn_n_edges(irn) > pos && "Invalid OUT position"); - /* 1st case: irn is not of mode_T, so it has only */ /* one OUT register -> good */ /* 2nd case: irn is of mode_T -> collect all Projs and ask the */ @@ -133,32 +115,26 @@ static const arch_register_t *get_out_reg(ir_node *irn, int pos) { return reg; } -/** - * Returns the number of the in register at position pos. - */ -int get_ia32_reg_nr(ir_node *irn, int pos, int in_out) { - const arch_register_t *reg; - - if (in_out == 1) { - reg = get_in_reg(irn, pos); - } - else { - reg = get_out_reg(irn, pos); - } - - return arch_register_get_index(reg); -} +enum io_direction { + IN_REG, + OUT_REG +}; /** * Returns the name of the in register at position pos. */ -const char *get_ia32_reg_name(ir_node *irn, int pos, int in_out) { +static const char *get_ia32_reg_name(ir_node *irn, int pos, enum io_direction in_out) { const arch_register_t *reg; - if (in_out == 1) { + if (in_out == IN_REG) { reg = get_in_reg(irn, pos); } else { + /* destination address mode nodes don't have outputs */ + if (is_ia32_irn(irn) && get_ia32_op_type(irn) == ia32_AddrModeD) { + return "MEM"; + } + reg = get_out_reg(irn, pos); } @@ -178,19 +154,13 @@ static int ia32_get_reg_name(lc_appendable_t *app, if (!X) return lc_arg_append(app, occ, "(null)", 6); - if (occ->conversion == 'S') { - buf = get_ia32_reg_name(X, nr, 1); - } - else { /* 'D' */ - buf = get_ia32_reg_name(X, nr, 0); - } + buf = get_ia32_reg_name(X, nr, occ->conversion == 'S' ? IN_REG : OUT_REG); - lc_appendable_chadd(app, '%'); return lc_arg_append(app, occ, buf, strlen(buf)); } /** - * Returns the tarval or offset of an ia32 as a string. + * Returns the tarval, offset or scale of an ia32 as a string. */ static int ia32_const_to_str(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *arg) @@ -202,13 +172,13 @@ static int ia32_const_to_str(lc_appendable_t *app, return lc_arg_append(app, occ, "(null)", 6); if (occ->conversion == 'C') { - buf = node_const_to_str(X); + buf = get_ia32_cnst(X); } else { /* 'O' */ - buf = node_offset_to_str(X); + buf = get_ia32_am_offs(X); } - return lc_arg_append(app, occ, buf, strlen(buf)); + return buf ? lc_arg_append(app, occ, buf, strlen(buf)) : 0; } /** @@ -217,15 +187,23 @@ static int ia32_const_to_str(lc_appendable_t *app, static int ia32_get_mode_suffix(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *arg) { - ir_node *X = arg->v_ptr; + ir_node *X = arg->v_ptr; + ir_mode *mode = get_irn_mode(X); + + if (mode == mode_T) { + mode = is_ia32_AddrModeS(X) || is_ia32_AddrModeD(X) ? get_ia32_ls_mode(X) : get_ia32_res_mode(X); + } if (!X) return lc_arg_append(app, occ, "(null)", 6); - if (get_mode_size_bits(get_irn_mode(X)) == 32) - return lc_appendable_chadd(app, 's'); - else - return lc_appendable_chadd(app, 'd'); + if (mode_is_float(mode)) { + return lc_appendable_chadd(app, get_mode_size_bits(mode) == 32 ? 's' : 'd'); + } + else { + + return lc_appendable_chadd(app, mode_is_signed(mode) ? 's' : 'z'); + } } /** @@ -242,7 +220,6 @@ const lc_arg_env_t *ia32_get_arg_env(void) { if(env == NULL) { /* extend the firm printer */ env = firm_get_arg_env(); - //lc_arg_new_env(); lc_arg_register(env, "ia32:sreg", 'S', &ia32_reg_handler); lc_arg_register(env, "ia32:dreg", 'D', &ia32_reg_handler); @@ -255,32 +232,198 @@ const lc_arg_env_t *ia32_get_arg_env(void) { } /** - * For 2-address code we need to make sure the first src reg is equal to dest reg. + * Emits registers and/or address mode of a binary operation. */ -void equalize_dest_src(FILE *F, ir_node *n) { - if (get_ia32_reg_nr(n, 0, 1) != get_ia32_reg_nr(n, 0, 0)) { - if (get_irn_arity(n) > 1 && get_ia32_reg_nr(n, 1, 1) == get_ia32_reg_nr(n, 0, 0)) { - if (! is_op_commutative(get_irn_op(n))) { - /* we only need to exchange for non-commutative ops */ - lc_efprintf(ia32_get_arg_env(), F, "\txchg %1S, %2S\t\t\t/* xchg src1 <-> src2 for 2 address code */\n", n, n); +char *ia32_emit_binop(const ir_node *n, ia32_emit_env_t *env) { + static char *buf = NULL; + + /* verify that this function is never called on non-AM supporting operations */ + assert(get_ia32_am_support(n) != ia32_am_None && "emit binop expects addressmode support"); + + if (! buf) { + buf = xcalloc(1, SNPRINTF_BUF_LEN); + } + else { + memset(buf, 0, SNPRINTF_BUF_LEN); + } + + switch(get_ia32_op_type(n)) { + case ia32_Normal: + if (get_ia32_cnst(n)) { + lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%3S, %s", n, get_ia32_cnst(n)); } + else { + const arch_register_t *in1 = get_in_reg(n, 2); + const arch_register_t *in2 = get_in_reg(n, 3); + const arch_register_t *out = get_ia32_n_res(n) > 0 ? get_out_reg(n, 0) : NULL; + const arch_register_t *in; + + in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2; + out = out ? out : in1; + + snprintf(buf, SNPRINTF_BUF_LEN, "%s, %s", \ + arch_register_get_name(out), arch_register_get_name(in)); + } + break; + case ia32_AddrModeS: + lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%4S, %s", n, ia32_emit_am(n, env)); + break; + case ia32_AddrModeD: + if (get_ia32_cnst(n)) { + lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%s, %s", ia32_emit_am(n, env), get_ia32_cnst(n)); + } + else { + const arch_register_t *in1 = get_in_reg(n, 2); + const char *reg_name; + ir_mode *mode = get_ia32_res_mode(n); + + mode = mode ? mode : get_ia32_ls_mode(n); + + switch(get_mode_size_bits(mode)) { + case 8: + reg_name = ia32_get_mapped_reg_name(env->isa->regs_8bit, in1); + break; + case 16: + reg_name = ia32_get_mapped_reg_name(env->isa->regs_16bit, in1); + break; + case 32: + reg_name = arch_register_get_name(in1); + break; + default: + assert(0 && "unsupported mode size"); + break; + } + + lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%s, %s", ia32_emit_am(n, env), reg_name); + } + break; + default: + assert(0 && "unsupported op type"); + } + + return buf; +} + +/** + * Emits registers and/or address mode of a unary operation. + */ +char *ia32_emit_unop(const ir_node *n, ia32_emit_env_t *env) { + static char *buf = NULL; + + if (! buf) { + buf = xcalloc(1, SNPRINTF_BUF_LEN); + } + else { + memset(buf, 0, SNPRINTF_BUF_LEN); + } + + switch(get_ia32_op_type(n)) { + case ia32_Normal: + lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%1D", n); + break; + case ia32_am_Dest: + snprintf(buf, SNPRINTF_BUF_LEN, ia32_emit_am(n, env)); + break; + default: + assert(0 && "unsupported op type"); + } + + return buf; +} + +/** + * Emits address mode. + */ +char *ia32_emit_am(const ir_node *n, ia32_emit_env_t *env) { + ia32_am_flavour_t am_flav = get_ia32_am_flavour(n); + int had_output = 0; + char *s; + int size; + static struct obstack *obst = NULL; + ir_mode *mode = get_ia32_ls_mode(n); + + if (! is_ia32_Lea(n)) + assert(mode && "AM node must have ls_mode attribute set."); + + if (! obst) { + obst = xcalloc(1, sizeof(*obst)); + } + else { + obstack_free(obst, NULL); + } + + /* obstack_free with NULL results in an uninitialized obstack */ + obstack_init(obst); + + if (mode) { + switch (get_mode_size_bits(mode)) { + case 8: + obstack_printf(obst, "BYTE "); + break; + case 16: + obstack_printf(obst, "WORD "); + break; + default: + break; } - else { - lc_efprintf(ia32_get_arg_env(), F, "\tmovl %1S, %1D\t\t\t/* src -> dest for 2 address code */\n", n, n); + } + + obstack_printf(obst, "["); + + if (am_flav & ia32_B) { + lc_eoprintf(ia32_get_arg_env(), obst, "%1S", n); + had_output = 1; + } + + if (am_flav & ia32_I) { + if (had_output) { + obstack_printf(obst, "+"); } + + lc_eoprintf(ia32_get_arg_env(), obst, "%2S", n); + + if (am_flav & ia32_S) { + obstack_printf(obst, "*%d", 1 << get_ia32_am_scale(n)); + } + + had_output = 1; } + + if (am_flav & ia32_O) { + obstack_printf(obst, get_ia32_am_offs(n)); + } + + obstack_printf(obst, "] "); + + size = obstack_object_size(obst); + s = obstack_finish(obst); + s[size - 1] = '\0'; + + return s; } -/* + + +/** + * Formated print of commands and comments. + */ +static void ia32_fprintf_format(FILE *F, char *cmd_buf, char *cmnt_buf) { + fprintf(F, "\t%-35s %-60s\n", cmd_buf, cmnt_buf); +} + + + +/** * Add a number to a prefix. This number will not be used a second time. */ -char *get_unique_label(char *buf, size_t buflen, const char *prefix) { +static char *get_unique_label(char *buf, size_t buflen, const char *prefix) { static unsigned long id = 0; snprintf(buf, buflen, "%s%lu", prefix, ++id); return buf; } + /************************************************* * _ _ _ * (_) | | | @@ -291,6 +434,9 @@ char *get_unique_label(char *buf, size_t buflen, const char *prefix) { * *************************************************/ +#undef IA32_DO_EMIT +#define IA32_DO_EMIT ia32_fprintf_format(F, cmd_buf, cmnt_buf) + /* * coding of conditions */ @@ -367,53 +513,69 @@ static char *get_cfop_target(const ir_node *irn, char *buf) { /** * Emits the jump sequence for a conditional jump (cmp + jmp_true + jmp_false) */ -static void finish_CondJmp(FILE *F, ir_node *irn) { +static void finish_CondJmp(FILE *F, const ir_node *irn) { const ir_node *proj; const ir_edge_t *edge; char buf[SNPRINTF_BUF_LEN]; + char cmd_buf[SNPRINTF_BUF_LEN]; + char cmnt_buf[SNPRINTF_BUF_LEN]; edge = get_irn_out_edge_first(irn); proj = get_edge_src_irn(edge); assert(is_Proj(proj) && "CondJmp with a non-Proj"); if (get_Proj_proj(proj) == 1) { - fprintf(F, "\tj%s %s\t\t\t/* cmp(a, b) == TRUE */\n", + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "j%s %s", get_cmp_suffix(get_ia32_pncode(irn), !mode_is_signed(get_irn_mode(get_irn_n(irn, 0)))), get_cfop_target(proj, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; cmp(a, b) == TRUE"); } else { - fprintf(F, "\tjn%s %s\t\t\t/* cmp(a, b) == FALSE */\n", + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "jn%s %s", get_cmp_suffix(get_ia32_pncode(irn), !mode_is_signed(get_irn_mode(get_irn_n(irn, 0)))), get_cfop_target(proj, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; cmp(a, b) == FALSE"); } + IA32_DO_EMIT; + edge = get_irn_out_edge_next(irn, edge); if (edge) { proj = get_edge_src_irn(edge); assert(is_Proj(proj) && "CondJmp with a non-Proj"); - fprintf(F, "\tjmp %s\t\t\t/* otherwise */\n", get_cfop_target(proj, buf)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "jmp %s", get_cfop_target(proj, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; otherwise"); + + IA32_DO_EMIT; } } /** - * Emits code for conditional jump with two variables. + * Emits code for conditional jump. */ -static void emit_ia32_CondJmp(ir_node *irn, emit_env_t *env) { +static void CondJmp_emitter(const ir_node *irn, ia32_emit_env_t *env) { FILE *F = env->out; + char cmd_buf[SNPRINTF_BUF_LEN]; + char cmnt_buf[SNPRINTF_BUF_LEN]; - lc_efprintf(ia32_get_arg_env(), F, "\tcmp %2S, %1S\t\t\t/* CondJmp(%+F, %+F) */\n", irn, irn, - get_irn_n(irn, 0), get_irn_n(irn, 1)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "cmp %s", ia32_emit_binop(irn, env)); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F", irn); + IA32_DO_EMIT; finish_CondJmp(F, irn); } /** - * Emits code for conditional jump with immediate. + * Emits code for conditional jump with two variables. */ -void emit_ia32_CondJmp_i(ir_node *irn, emit_env_t *env) { - FILE *F = env->out; +static void emit_ia32_CondJmp(const ir_node *irn, ia32_emit_env_t *env) { + CondJmp_emitter(irn, env); +} - lc_efprintf(ia32_get_arg_env(), F, "\tcmp %C, %1S\t\t\t/* CondJmp_i(%+F) */\n", irn, irn, get_irn_n(irn, 0)); - finish_CondJmp(F, irn); +/** + * Emits code for conditional jump with immediate. + */ +void emit_ia32_CondJmp_i(const ir_node *irn, ia32_emit_env_t *env) { + CondJmp_emitter(irn, env); } @@ -463,7 +625,7 @@ static int ia32_cmp_branch_t(const void *a, const void *b) { * possible otherwise a cmp-jmp cascade). Port from * cggg ia32 backend */ -void emit_ia32_SwitchJmp(const ir_node *irn, emit_env_t *emit_env) { +void emit_ia32_SwitchJmp(const ir_node *irn, ia32_emit_env_t *emit_env) { unsigned long interval; char buf[SNPRINTF_BUF_LEN]; int last_value, i, pn, do_jmp_tbl = 1; @@ -472,13 +634,14 @@ void emit_ia32_SwitchJmp(const ir_node *irn, emit_env_t *emit_env) { const ir_edge_t *edge; const lc_arg_env_t *env = ia32_get_arg_env(); FILE *F = emit_env->out; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; /* fill the table structure */ - tbl.label = malloc(SNPRINTF_BUF_LEN); + tbl.label = xmalloc(SNPRINTF_BUF_LEN); tbl.label = get_unique_label(tbl.label, SNPRINTF_BUF_LEN, "JMPTBL_"); tbl.defProj = NULL; tbl.num_branches = get_irn_n_edges(irn); - tbl.branches = calloc(tbl.num_branches, sizeof(tbl.branches[0])); + tbl.branches = xcalloc(tbl.num_branches, sizeof(tbl.branches[0])); tbl.min_value = INT_MAX; tbl.max_value = INT_MIN; @@ -525,52 +688,71 @@ void emit_ia32_SwitchJmp(const ir_node *irn, emit_env_t *emit_env) { if (do_jmp_tbl) { /* emit the table */ if (tbl.min_value != 0) { - fprintf(F, "\tcmpl %lu, -%d", interval, tbl.min_value); - lc_efprintf(env, F, "(%1S)\t\t/* first switch value is not 0 */\n", irn); + lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "cmpl %lu, -%d(%1S)", + interval, tbl.min_value, irn); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; first switch value is not 0"); + + IA32_DO_EMIT; } else { - fprintf(F, "\tcmpl %lu, ", interval); - lc_efprintf(env, F, "%1S\t\t\t/* compare for switch */\n", irn); + lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "cmpl %lu, %1S", interval, irn); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; compare for switch"); + + IA32_DO_EMIT; } - fprintf(F, "\tja %s\t\t\t/* default jump if out of range */\n", get_cfop_target(tbl.defProj, buf)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "ja %s", get_cfop_target(tbl.defProj, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; default jump if out of range "); + IA32_DO_EMIT; if (tbl.num_branches > 1) { /* create table */ - //fprintf(F, "\tjmp *%s", tbl.label); - lc_efprintf(env, F, "\tjmp *%s(,%1S,4)\t\t/* get jump table entry as target */\n", tbl.label, irn); + lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "jmp [%1S*4+%s]", irn, tbl.label); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; get jump table entry as target"); + IA32_DO_EMIT; - fprintf(F, "\t.section\t.rodata\t\t/* start jump table */\n"); + fprintf(F, "\t.section\t.rodata\n"); fprintf(F, "\t.align 4\n"); fprintf(F, "%s:\n", tbl.label); - fprintf(F, "\t.long %s\t\t\t/* case %d */\n", get_cfop_target(tbl.branches[0].target, buf), tbl.branches[0].value); + + snprintf(cmd_buf, SNPRINTF_BUF_LEN, ".long %s", get_cfop_target(tbl.branches[0].target, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* case %d */\n", tbl.branches[0].value); + IA32_DO_EMIT; last_value = tbl.branches[0].value; for (i = 1; i < tbl.num_branches; ++i) { while (++last_value < tbl.branches[i].value) { - fprintf(F, "\t.long %s\t\t/* default case */\n", get_cfop_target(tbl.defProj, buf)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, ".long %s", get_cfop_target(tbl.defProj, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; default case"); + IA32_DO_EMIT; } - fprintf(F, "\t.long %s\t\t\t/* case %d */\n", get_cfop_target(tbl.branches[i].target, buf), last_value); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, ".long %s", get_cfop_target(tbl.branches[i].target, buf), last_value); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; case %d", last_value); + IA32_DO_EMIT; } - fprintf(F, "\t.text\t\t\t\t/* end of jump table */\n"); + fprintf(F, "\t.text"); } else { /* one jump is enough */ - fprintf(F, "\tjmp %s\t\t/* only one case given */\n", get_cfop_target(tbl.branches[0].target, buf)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "jmp %s", get_cfop_target(tbl.branches[0].target, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; only one case given"); + IA32_DO_EMIT; } } else { // no jump table for (i = 0; i < tbl.num_branches; ++i) { - fprintf(F, "\tcmpl %d, ", tbl.branches[i].value); - lc_efprintf(env, F, "%1S", irn); - fprintf(F, "\t\t\t/* case %d */\n", tbl.branches[i].value); + lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "cmpl %d, %1S", tbl.branches[i].value, irn); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; case %d", i); + IA32_DO_EMIT; fprintf(F, "\tje %s\n", get_cfop_target(tbl.branches[i].target, buf)); } - fprintf(F, "\tjmp %s\t\t\t/* default case */\n", get_cfop_target(tbl.defProj, buf)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "jmp %s", get_cfop_target(tbl.defProj, buf)); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; default case"); + IA32_DO_EMIT; } if (tbl.label) @@ -582,11 +764,13 @@ void emit_ia32_SwitchJmp(const ir_node *irn, emit_env_t *emit_env) { /** * Emits code for a unconditional jump. */ -void emit_Jmp(ir_node *irn, emit_env_t *env) { +void emit_Jmp(const ir_node *irn, ia32_emit_env_t *env) { FILE *F = env->out; + char buf[SNPRINTF_BUF_LEN], cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - char buf[SNPRINTF_BUF_LEN]; - ir_fprintf(F, "\tjmp %s\t\t\t/* Jmp(%+F) */\n", get_cfop_target(irn, buf), get_irn_link(irn)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "jmp %s", get_cfop_target(irn, buf), get_irn_link(irn)); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F(%+F)", irn, get_irn_link(irn)); + IA32_DO_EMIT; } @@ -605,10 +789,10 @@ void emit_Jmp(ir_node *irn, emit_env_t *env) { /** * Emits code for a proj -> node */ -void emit_Proj(ir_node *irn, emit_env_t *env) { +void emit_Proj(const ir_node *irn, ia32_emit_env_t *env) { ir_node *pred = get_Proj_pred(irn); - if (get_irn_opcode(pred) == iro_Start) { + if (get_irn_op(pred) == op_Start) { switch(get_Proj_proj(irn)) { case pn_Start_X_initial_exec: emit_Jmp(irn, env); @@ -619,116 +803,314 @@ void emit_Proj(ir_node *irn, emit_env_t *env) { } } +/********************************** + * _____ ____ + * / ____| | _ \ + * | | ___ _ __ _ _| |_) | + * | | / _ \| '_ \| | | | _ < + * | |___| (_) | |_) | |_| | |_) | + * \_____\___/| .__/ \__, |____/ + * | | __/ | + * |_| |___/ + **********************************/ +/** + * Emit movsb/w instructions to make mov count divideable by 4 + */ +static void emit_CopyB_prolog(FILE *F, int rem, int size) { + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + fprintf(F, "\t/* memcopy %d bytes*/\n", size); + + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "cld"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* copy direction forward*/"); + IA32_DO_EMIT; + + switch(rem) { + case 1: + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsb"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; memcopy remainder 1"); + break; + case 2: + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsw"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; memcopy remainder 2"); + break; + case 3: + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsb"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; memcopy remainder 3"); + IA32_DO_EMIT; + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsw"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; memcopy remainder 3"); + break; + } -/*********************************************************************************** - * _ __ _ - * (_) / _| | | - * _ __ ___ __ _ _ _ __ | |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ - * | '_ ` _ \ / _` | | '_ \ | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / - * | | | | | | (_| | | | | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < - * |_| |_| |_|\__,_|_|_| |_| |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ + IA32_DO_EMIT; +} + +/** + * Emit rep movsd instruction for memcopy. + */ +void emit_ia32_CopyB(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + tarval *tv = get_ia32_Immop_tarval(irn); + int rem = get_tarval_long(tv); + int size = get_tarval_long(get_ia32_Immop_tarval(get_irn_n(irn, 2))); + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + emit_CopyB_prolog(F, rem, size); + + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "rep movsd"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; memcopy"); + IA32_DO_EMIT; +} + +/** + * Emits unrolled memcopy. + */ +void emit_ia32_CopyB_i(const ir_node *irn, ia32_emit_env_t *emit_env) { + tarval *tv = get_ia32_Immop_tarval(irn); + int size = get_tarval_long(tv); + FILE *F = emit_env->out; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + emit_CopyB_prolog(F, size & 0x3, size); + + size >>= 2; + while (size--) { + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsd"); + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "; memcopy unrolled"); + IA32_DO_EMIT; + } +} + + + +/*************************** + * _____ + * / ____| + * | | ___ _ ____ __ + * | | / _ \| '_ \ \ / / + * | |___| (_) | | | \ V / + * \_____\___/|_| |_|\_/ * - ***********************************************************************************/ + ***************************/ /** - * Emits code for a node. + * Emit code for conversions (I, FP), (FP, I) and (FP, FP). */ -void ia32_emit_node(ir_node *irn, void *env) { - emit_env_t *emit_env = env; - firm_dbg_module_t *mod = emit_env->mod; - FILE *F = emit_env->out; +static void emit_ia32_Conv(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + const lc_arg_env_t *env = ia32_get_arg_env(); + char *from, *to, buf[64]; + ir_mode *src_mode, *tgt_mode; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + src_mode = is_ia32_AddrModeS(irn) ? get_ia32_ls_mode(irn) : get_irn_mode(get_irn_n(irn, 2)); + tgt_mode = get_ia32_res_mode(irn); + + from = mode_is_float(src_mode) ? (get_mode_size_bits(src_mode) == 32 ? "ss" : "sd") : "si"; + to = mode_is_float(tgt_mode) ? (get_mode_size_bits(tgt_mode) == 32 ? "ss" : "sd") : "si"; + + switch(get_ia32_op_type(irn)) { + case ia32_Normal: + lc_esnprintf(env, buf, sizeof(buf), "%1D, %3S", irn, irn); + break; + case ia32_AddrModeS: + lc_esnprintf(env, buf, sizeof(buf), "%1D, %s", irn, ia32_emit_am(irn, emit_env)); + break; + default: + assert(0 && "unsupported op type for Conv"); + } - DBG((mod, LEVEL_1, "emitting code for %+F\n", irn)); + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "cvt%s2%s %s", from, to, buf); + lc_esnprintf(env, cmnt_buf, SNPRINTF_BUF_LEN, "; %+F(%+F, %+F)", irn, src_mode, tgt_mode); + IA32_DO_EMIT; +} + +void emit_ia32_Conv_I2FP(const ir_node *irn, ia32_emit_env_t *emit_env) { + emit_ia32_Conv(irn, emit_env); +} + +void emit_ia32_Conv_FP2I(const ir_node *irn, ia32_emit_env_t *emit_env) { + emit_ia32_Conv(irn, emit_env); +} -#define IA32_EMIT(a) if (is_ia32_##a(irn)) { emit_ia32_##a(irn, emit_env); return; } -#define EMIT(a) if (get_irn_opcode(irn) == iro_##a) { emit_##a(irn, emit_env); return; } +void emit_ia32_Conv_FP2FP(const ir_node *irn, ia32_emit_env_t *emit_env) { + emit_ia32_Conv(irn, emit_env); +} - /* generated int emitter functions */ - IA32_EMIT(Copy); - IA32_EMIT(Perm); - IA32_EMIT(Const); - IA32_EMIT(Add); - IA32_EMIT(Add_i); - IA32_EMIT(Sub); - IA32_EMIT(Sub_i); - IA32_EMIT(Minus); - IA32_EMIT(Inc); - IA32_EMIT(Dec); +/******************************************* + * _ _ + * | | | | + * | |__ ___ _ __ ___ __| | ___ ___ + * | '_ \ / _ \ '_ \ / _ \ / _` |/ _ \/ __| + * | |_) | __/ | | | (_) | (_| | __/\__ \ + * |_.__/ \___|_| |_|\___/ \__,_|\___||___/ + * + *******************************************/ - IA32_EMIT(Max); - IA32_EMIT(Min); +/** + * Emits a backend call + */ +void emit_be_Call(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + entity *ent = be_Call_get_entity(irn); + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - IA32_EMIT(And); - IA32_EMIT(And_i); - IA32_EMIT(Or); - IA32_EMIT(Or_i); - IA32_EMIT(Eor); - IA32_EMIT(Eor_i); - IA32_EMIT(Not); + if (ent) { + snprintf(cmd_buf, SNPRINTF_BUF_LEN, "call %s", get_entity_name(ent)); + } + else { + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "%1D", get_irn_n(irn, be_pos_Call_ptr)); + } - IA32_EMIT(Shl); - IA32_EMIT(Shl_i); - IA32_EMIT(Shr); - IA32_EMIT(Shr_i); - IA32_EMIT(Shrs); - IA32_EMIT(Shrs_i); - IA32_EMIT(RotL); - IA32_EMIT(RotL_i); - IA32_EMIT(RotR); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F (be_Call)", irn); - IA32_EMIT(Lea); - IA32_EMIT(Lea_i); + IA32_DO_EMIT; +} - IA32_EMIT(Mul); - IA32_EMIT(Mul_i); +/** + * Emits code to increase stack pointer. + */ +void emit_be_IncSP(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + unsigned offs = be_get_IncSP_offset(irn); + be_stack_dir_t dir = be_get_IncSP_direction(irn); + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + if (offs) { + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "add %1S,%s%u", irn, + (dir == be_stack_dir_along) ? " -" : " ", offs); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F (IncSP)", irn); + } + else { + snprintf(cmd_buf, SNPRINTF_BUF_LEN, " "); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; omitted %+F (IncSP) with 0", irn); + } - IA32_EMIT(Cltd); - IA32_EMIT(DivMod); + IA32_DO_EMIT; +} + +/** + * Emits code to set stack pointer. + */ +void emit_be_SetSP(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %3S", irn, irn); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F (restore SP)", irn); + IA32_DO_EMIT; +} + +/** + * Emits code for Copy. + */ +void emit_be_Copy(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %1S", irn, irn); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F", irn); + IA32_DO_EMIT; +} + +/** + * Emits code for exchange. + */ +void emit_be_Perm(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - IA32_EMIT(Store); - IA32_EMIT(Load); + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "xchg %1S, %2S", irn, irn); + lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "; %+F(%1A, %2A)", irn, irn, irn); + IA32_DO_EMIT; +} - /* generated floating point emitter */ - IA32_EMIT(fConst); +/*********************************************************************************** + * _ __ _ + * (_) / _| | | + * _ __ ___ __ _ _ _ __ | |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ + * | '_ ` _ \ / _` | | '_ \ | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / + * | | | | | | (_| | | | | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < + * |_| |_| |_|\__,_|_|_| |_| |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ + * + ***********************************************************************************/ - IA32_EMIT(fAdd); - IA32_EMIT(fSub); - IA32_EMIT(fMinus); +/** + * Enters the emitter functions for handled nodes into the generic + * pointer of an opcode. + */ +static void ia32_register_emitters(void) { - IA32_EMIT(fMul); - IA32_EMIT(fDiv); +#define IA32_EMIT(a) op_ia32_##a->ops.generic = (op_func)emit_ia32_##a +#define EMIT(a) op_##a->ops.generic = (op_func)emit_##a +#define BE_EMIT(a) op_be_##a->ops.generic = (op_func)emit_be_##a - IA32_EMIT(fMin); - IA32_EMIT(fMax); + /* first clear the generic function pointer for all ops */ + clear_irp_opcodes_generic_func(); - IA32_EMIT(fLoad); - IA32_EMIT(fStore); + /* register all emitter functions defined in spec */ + ia32_register_spec_emitters(); - /* other emitter functions */ + /* other ia32 emitter functions */ IA32_EMIT(CondJmp); - IA32_EMIT(CondJmp_i); IA32_EMIT(SwitchJmp); - + IA32_EMIT(CopyB); + IA32_EMIT(CopyB_i); + IA32_EMIT(Conv_I2FP); + IA32_EMIT(Conv_FP2I); + IA32_EMIT(Conv_FP2FP); + + /* benode emitter */ + BE_EMIT(Call); + BE_EMIT(IncSP); + BE_EMIT(SetSP); + BE_EMIT(Copy); + BE_EMIT(Perm); + + /* firm emitter */ EMIT(Jmp); EMIT(Proj); - ir_fprintf(F, "\t\t\t\t\t/* %+F */\n", irn); +#undef IA32_EMIT +#undef BE_EMIT +#undef EMIT +} + +/** + * Emits code for a node. + */ +static void ia32_emit_node(const ir_node *irn, void *env) { + ia32_emit_env_t *emit_env = env; + firm_dbg_module_t *mod = emit_env->mod; + FILE *F = emit_env->out; + ir_op *op = get_irn_op(irn); + + DBG((mod, LEVEL_1, "emitting code for %+F\n", irn)); + + if (op->ops.generic) { + void (*emit)(const ir_node *, void *) = (void (*)(const ir_node *, void *))op->ops.generic; + (*emit)(irn, env); + } + else { + ir_fprintf(F, "\t%35s ; %+F \n", " ", irn); + } } /** * Walks over the nodes in a block connected by scheduling edges * and emits code for each node. */ -void ia32_gen_block(ir_node *block, void *env) { - ir_node *irn; +static void ia32_gen_block(ir_node *block, void *env) { + const ir_node *irn; if (! is_Block(block)) return; - fprintf(((emit_env_t *)env)->out, "BLOCK_%ld:\n", get_irn_node_nr(block)); + fprintf(((ia32_emit_env_t *)env)->out, "BLOCK_%ld:\n", get_irn_node_nr(block)); sched_foreach(block, irn) { ia32_emit_node(irn, env); } @@ -738,30 +1120,33 @@ void ia32_gen_block(ir_node *block, void *env) { /** * Emits code for function start. */ -void ia32_emit_start(FILE *F, ir_graph *irg) { - const char *irg_name = get_entity_name(get_irg_entity(irg)); +static void ia32_emit_func_prolog(FILE *F, ir_graph *irg) { + entity *irg_ent = get_irg_entity(irg); + const char *irg_name = get_entity_name(irg_ent); - fprintf(F, "\t.text\n"); - fprintf(F, ".globl %s\n", irg_name); - fprintf(F, "\t.type\t%s, @function\n", irg_name); +// fprintf(F, "\t.text\n"); + if (get_entity_visibility(irg_ent) == visibility_external_visible) { + fprintf(F, "global %s\n", irg_name); + } +// fprintf(F, "\t.type\t%s, @function\n", irg_name); fprintf(F, "%s:\n", irg_name); } /** * Emits code for function end */ -void ia32_emit_end(FILE *F, ir_graph *irg) { +static void ia32_emit_func_epilog(FILE *F, ir_graph *irg) { const char *irg_name = get_entity_name(get_irg_entity(irg)); fprintf(F, "\tret\n"); - fprintf(F, "\t.size\t%s, .-%s\n\n", irg_name, irg_name); + //printf(F, "\t.size\t%s, .-%s\n\n", irg_name, irg_name); } /** * Sets labels for control flow nodes (jump target) * TODO: Jump optimization */ -void ia32_gen_labels(ir_node *block, void *env) { +static void ia32_gen_labels(ir_node *block, void *env) { ir_node *pred; int n = get_Block_n_cfgpreds(block); @@ -772,19 +1157,24 @@ void ia32_gen_labels(ir_node *block, void *env) { } /** - * Main driver + * Main driver. Emits the code for one routine. */ -void ia32_gen_routine(FILE *F, ir_graph *irg, const arch_env_t *env) { - emit_env_t emit_env; +void ia32_gen_routine(FILE *F, ir_graph *irg, const ia32_code_gen_t *cg) { + ia32_emit_env_t emit_env; emit_env.mod = firm_dbg_register("ir.be.codegen.ia32"); emit_env.out = F; - emit_env.arch_env = env; + emit_env.arch_env = cg->arch_env; + emit_env.cg = cg; + emit_env.isa = (ia32_isa_t *)cg->arch_env->isa; + + /* set the global arch_env (needed by print hooks) */ + arch_env = cg->arch_env; - arch_env = env; + ia32_register_emitters(); - ia32_emit_start(F, irg); + ia32_emit_func_prolog(F, irg); irg_block_walk_graph(irg, ia32_gen_labels, NULL, &emit_env); irg_walk_blkwise_graph(irg, NULL, ia32_gen_block, &emit_env); - ia32_emit_end(F, irg); + ia32_emit_func_epilog(F, irg); }