X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fia32%2Fia32_emitter.c;h=ad6a9df2dc17db7e11bc63631be716775a16ddd6;hb=d9b6f3a9505efb7dc1f692d2f7958a575532bdbf;hp=c124a68f25a56830c6ebd6dfbd59449c9528b524;hpb=2adf84106c02caf097c2d6cf1764706bdc437bcc;p=libfirm diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index c124a68f2..ad6a9df2d 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -1,7 +1,27 @@ +/* + * Copyright (C) 1995-2007 University of Karlsruhe. All right reserved. + * + * This file is part of libFirm. + * + * This file may be distributed and/or modified under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation and appearing in the file LICENSE.GPL included in the + * packaging of this file. + * + * Licensees holding valid libFirm Professional Edition licenses may use + * this file in accordance with the libFirm Commercial License. + * Agreement provided with the Software. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + /** - * This file implements the node emitter. - * @author Christian Wuerdig, Matthias Braun - * $Id$ + * @file + * @brief This file implements the ia32 node emitter. + * @author Christian Wuerdig, Matthias Braun + * @version $Id$ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -29,6 +49,7 @@ #include "../be_dbgout.h" #include "../beemitter.h" #include "../begnuas.h" +#include "../beirg_t.h" #include "ia32_emitter.h" #include "gen_ia32_emitter.h" @@ -38,17 +59,20 @@ #include "ia32_map_regs.h" #include "bearch_ia32_t.h" +DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;) + #define BLOCK_PREFIX ".L" #define SNPRINTF_BUF_LEN 128 -/* global arch_env for lc_printf functions */ -static const arch_env_t *arch_env = NULL; - /** * Returns the register at in position pos. */ -static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { +static +const arch_register_t *get_in_reg(ia32_emit_env_t *env, const ir_node *irn, + int pos) +{ + const arch_env_t *arch_env = env->arch_env; ir_node *op; const arch_register_t *reg = NULL; @@ -85,9 +109,13 @@ static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { /** * Returns the register at out position pos. */ -static const arch_register_t *get_out_reg(const ir_node *irn, int pos) { - ir_node *proj; - const arch_register_t *reg = NULL; +static +const arch_register_t *get_out_reg(ia32_emit_env_t *env, const ir_node *irn, + int pos) +{ + const arch_env_t *arch_env = env->arch_env; + ir_node *proj; + const arch_register_t *reg = NULL; /* 1st case: irn is not of mode_T, so it has only */ /* one OUT register -> good */ @@ -115,19 +143,11 @@ static const arch_register_t *get_out_reg(const ir_node *irn, int pos) { return reg; } -/** - * Returns an ident for the given tarval tv. - */ -static ident *get_ident_for_tv(tarval *tv) { - char buf[256]; - tarval_snprintf(buf, sizeof(buf), tv); - return new_id_from_str(buf); -} - /** * Determine the gnu assembler suffix that indicates a mode */ -static char get_mode_suffix(const ir_mode *mode) { +static +char get_mode_suffix(const ir_mode *mode) { if(mode_is_float(mode)) { switch(get_mode_size_bits(mode)) { case 32: @@ -135,10 +155,11 @@ static char get_mode_suffix(const ir_mode *mode) { case 64: return 'l'; case 80: + case 96: return 't'; } } else { - assert(mode_is_int(mode) || mode_is_reference(mode)); + assert(mode_is_int(mode) || mode_is_reference(mode) || mode_is_character(mode)); switch(get_mode_size_bits(mode)) { case 64: return 'q'; @@ -153,9 +174,9 @@ static char get_mode_suffix(const ir_mode *mode) { panic("Can't output mode_suffix for %+F\n", mode); } -static int produces_result(const ir_node *node) { +static +int produces_result(const ir_node *node) { return !(is_ia32_St(node) || - is_ia32_Store8Bit(node) || is_ia32_CondJmp(node) || is_ia32_xCondJmp(node) || is_ia32_CmpSet(node) || @@ -163,7 +184,9 @@ static int produces_result(const ir_node *node) { is_ia32_SwitchJmp(node)); } -static const char *ia32_get_reg_name_for_mode(ia32_emit_env_t *env, ir_mode *mode, const arch_register_t *reg) { +static +const char *ia32_get_reg_name_for_mode(ia32_emit_env_t *env, ir_mode *mode, + const arch_register_t *reg) { switch(get_mode_size_bits(mode)) { case 8: return ia32_get_mapped_reg_name(env->isa->regs_8bit, reg); @@ -177,7 +200,8 @@ static const char *ia32_get_reg_name_for_mode(ia32_emit_env_t *env, ir_mode *mod /** * Add a number to a prefix. This number will not be used a second time. */ -static 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; @@ -201,13 +225,14 @@ static char *get_unique_label(char *buf, size_t buflen, const char *prefix) { #undef be_emit_cstring #define be_emit_cstring(env,x) { be_emit_string_len(env->emit, x, sizeof(x)-1); } #define be_emit_ident(env,i) be_emit_ident(env->emit,i) +#define be_emit_tarval(env,tv) be_emit_tarval(env->emit,tv) #define be_emit_write_line(env) be_emit_write_line(env->emit) #define be_emit_finish_line_gas(env,n) be_emit_finish_line_gas(env->emit,n) #define be_emit_pad_comment(env) be_emit_pad_comment(env->emit) void ia32_emit_source_register(ia32_emit_env_t *env, const ir_node *node, int pos) { - const arch_register_t *reg = get_in_reg(node, pos); + const arch_register_t *reg = get_in_reg(env, node, pos); const char *reg_name = arch_register_get_name(reg); assert(pos < get_irn_arity(node)); @@ -217,7 +242,7 @@ void ia32_emit_source_register(ia32_emit_env_t *env, const ir_node *node, int po } void ia32_emit_dest_register(ia32_emit_env_t *env, const ir_node *node, int pos) { - const arch_register_t *reg = get_out_reg(node, pos); + const arch_register_t *reg = get_out_reg(env, node, pos); const char *reg_name = arch_register_get_name(reg); be_emit_char(env, '%'); @@ -226,7 +251,7 @@ void ia32_emit_dest_register(ia32_emit_env_t *env, const ir_node *node, int pos) void ia32_emit_x87_name(ia32_emit_env_t *env, const ir_node *node, int pos) { - ia32_attr_t *attr = get_ia32_attr(node); + const ia32_x87_attr_t *attr = get_ia32_x87_attr_const(node); assert(pos < 3); be_emit_char(env, '%'); @@ -236,38 +261,55 @@ void ia32_emit_x87_name(ia32_emit_env_t *env, const ir_node *node, int pos) void ia32_emit_immediate(ia32_emit_env_t *env, const ir_node *node) { tarval *tv; + ir_entity *ent; ident *id; + be_emit_char(env, '$'); + switch(get_ia32_immop_type(node)) { case ia32_ImmConst: tv = get_ia32_Immop_tarval(node); - id = get_ident_for_tv(tv); - break; + be_emit_tarval(env, tv); + return; case ia32_ImmSymConst: - id = get_ia32_Immop_symconst(node); - break; - default: - assert(0); - be_emit_string(env, "BAD"); + ent = get_ia32_Immop_symconst(node); + set_entity_backend_marked(ent, 1); + id = get_entity_ld_ident(ent); + be_emit_ident(env, id); return; + case ia32_ImmNone: + break; } - be_emit_ident(env, id); + assert(0); + be_emit_string(env, "BAD"); + return; } -void ia32_emit_mode_suffix(ia32_emit_env_t *env, const ir_mode *mode) +static +void ia32_emit_mode_suffix_mode(ia32_emit_env_t *env, const ir_mode *mode) { be_emit_char(env, get_mode_suffix(mode)); } +void ia32_emit_mode_suffix(ia32_emit_env_t *env, const ir_node *node) +{ + ir_mode *mode = get_ia32_ls_mode(node); + if(mode == NULL) + mode = mode_Iu; + + ia32_emit_mode_suffix_mode(env, mode); +} + void ia32_emit_x87_mode_suffix(ia32_emit_env_t *env, const ir_node *node) { ir_mode *mode = get_ia32_ls_mode(node); if(mode != NULL) - ia32_emit_mode_suffix(env, mode); + ia32_emit_mode_suffix_mode(env, mode); } -static char get_xmm_mode_suffix(ir_mode *mode) +static +char get_xmm_mode_suffix(ir_mode *mode) { assert(mode_is_float(mode)); switch(get_mode_size_bits(mode)) { @@ -307,7 +349,8 @@ void ia32_emit_extend_suffix(ia32_emit_env_t *env, const ir_mode *mode) } } -static void ia32_emit_function_object(ia32_emit_env_t *env, const char *name) +static +void ia32_emit_function_object(ia32_emit_env_t *env, const char *name) { switch (be_gas_flavour) { case GAS_FLAVOUR_NORMAL: @@ -327,7 +370,8 @@ static void ia32_emit_function_object(ia32_emit_env_t *env, const char *name) } } -static void ia32_emit_function_size(ia32_emit_env_t *env, const char *name) +static +void ia32_emit_function_size(ia32_emit_env_t *env, const char *name) { switch (be_gas_flavour) { case GAS_FLAVOUR_NORMAL: @@ -344,82 +388,94 @@ static void ia32_emit_function_size(ia32_emit_env_t *env, const char *name) } +static +void emit_ia32_Immediate(ia32_emit_env_t *env, const ir_node *node); /** * Emits registers and/or address mode of a binary operation. */ void ia32_emit_binop(ia32_emit_env_t *env, const ir_node *node) { - switch(get_ia32_op_type(node)) { - case ia32_Normal: - if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { - be_emit_char(env, '$'); - ia32_emit_immediate(env, node); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 2); - } else { - const arch_register_t *in1 = get_in_reg(node, 2); - const arch_register_t *in2 = get_in_reg(node, 3); - const arch_register_t *out = produces_result(node) ? get_out_reg(node, 0) : NULL; - const arch_register_t *in; - const char *in_name; - - in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2; - out = out ? out : in1; - in_name = arch_register_get_name(in); - - if (is_ia32_emit_cl(node)) { - assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in) && "shift operation needs ecx"); - in_name = "cl"; - } + int right_pos; + const ir_node *right_op; - be_emit_char(env, '%'); - be_emit_string(env, in_name); - be_emit_cstring(env, ", %"); - be_emit_string(env, arch_register_get_name(out)); - } + switch(get_ia32_op_type(node)) { + case ia32_Normal: + right_op = get_irn_n(node, 3); + if(is_ia32_Immediate(right_op)) { + emit_ia32_Immediate(env, right_op); + be_emit_cstring(env, ", "); + ia32_emit_source_register(env, node, 2); break; - case ia32_AddrModeS: - if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { - assert(!produces_result(node) && "Source AM with Const must not produce result"); - ia32_emit_am(env, node); - be_emit_cstring(env, ", $"); - ia32_emit_immediate(env, node); - } else if (produces_result(node)) { - ia32_emit_am(env, node); - be_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } else { - ia32_emit_am(env, node); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 2); + } else if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { + ia32_emit_immediate(env, node); + be_emit_cstring(env, ", "); + ia32_emit_source_register(env, node, 2); + } else { + const arch_register_t *in1 = get_in_reg(env, node, 2); + const arch_register_t *in2 = get_in_reg(env, node, 3); + const arch_register_t *out = produces_result(node) ? get_out_reg(env, node, 0) : NULL; + const arch_register_t *in; + const char *in_name; + + in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2; + out = out ? out : in1; + in_name = arch_register_get_name(in); + + if (is_ia32_emit_cl(node)) { + assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in) && "shift operation needs ecx"); + in_name = "cl"; } - break; - case ia32_AddrModeD: - if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { - be_emit_char(env, '$'); - ia32_emit_immediate(env, node); - be_emit_cstring(env, ", "); - ia32_emit_am(env, node); - } else { - const arch_register_t *in1 = get_in_reg(node, get_irn_arity(node) == 5 ? 3 : 2); - ir_mode *mode = get_ia32_ls_mode(node); - const char *in_name; - in_name = ia32_get_reg_name_for_mode(env, mode, in1); + be_emit_char(env, '%'); + be_emit_string(env, in_name); + be_emit_cstring(env, ", %"); + be_emit_string(env, arch_register_get_name(out)); + } + break; + case ia32_AddrModeS: + ia32_emit_am(env, node); + be_emit_cstring(env, ", "); + if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { + assert(!produces_result(node) && "Source AM with Const must not produce result"); + ia32_emit_immediate(env, node); + } else if (produces_result(node)) { + ia32_emit_dest_register(env, node, 0); + } else { + ia32_emit_source_register(env, node, 2); + } + break; + case ia32_AddrModeD: + right_pos = get_irn_arity(node) == 5 ? 3 : 2; + right_op = get_irn_n(node, right_pos); + if(is_ia32_Immediate(right_op)) { + emit_ia32_Immediate(env, right_op); + be_emit_cstring(env, ", "); + ia32_emit_am(env, node); + break; + } else if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { + ia32_emit_immediate(env, node); + be_emit_cstring(env, ", "); + ia32_emit_am(env, node); + } else { + const arch_register_t *in1 = get_in_reg(env, node, right_pos); + ir_mode *mode = get_ia32_ls_mode(node); + const char *in_name; - if (is_ia32_emit_cl(node)) { - assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in1) && "shift operation needs ecx"); - in_name = "cl"; - } + in_name = ia32_get_reg_name_for_mode(env, mode, in1); - be_emit_char(env, '%'); - be_emit_string(env, in_name); - be_emit_cstring(env, ", "); - ia32_emit_am(env, node); + if (is_ia32_emit_cl(node)) { + assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in1) && "shift operation needs ecx"); + in_name = "cl"; } - break; - default: - assert(0 && "unsupported op type"); + + be_emit_char(env, '%'); + be_emit_string(env, in_name); + be_emit_cstring(env, ", "); + ia32_emit_am(env, node); + } + break; + default: + assert(0 && "unsupported op type"); } } @@ -433,10 +489,10 @@ void ia32_emit_x87_binop(ia32_emit_env_t *env, const ir_node *node) { // should not happen... assert(0); } else { - ia32_attr_t *attr = get_ia32_attr(node); - const arch_register_t *in1 = attr->x87[0]; - const arch_register_t *in2 = attr->x87[1]; - const arch_register_t *out = attr->x87[2]; + const ia32_x87_attr_t *x87_attr = get_ia32_x87_attr_const(node); + const arch_register_t *in1 = x87_attr->x87[0]; + const arch_register_t *in2 = x87_attr->x87[1]; + const arch_register_t *out = x87_attr->x87[2]; const arch_register_t *in; in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2; @@ -457,35 +513,39 @@ void ia32_emit_x87_binop(ia32_emit_env_t *env, const ir_node *node) { } } +void ia32_emit_am_or_dest_register(ia32_emit_env_t *env, const ir_node *node, + int pos) { + if(get_ia32_op_type(node) == ia32_Normal) { + ia32_emit_dest_register(env, node, pos); + } else { + assert(get_ia32_op_type(node) == ia32_AddrModeD); + ia32_emit_am(env, node); + } +} + /** * Emits registers and/or address mode of a unary operation. */ -void ia32_emit_unop(ia32_emit_env_t *env, const ir_node *node) { +void ia32_emit_unop(ia32_emit_env_t *env, const ir_node *node, int pos) { + const ir_node *op; + switch(get_ia32_op_type(node)) { - case ia32_Normal: - if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { - be_emit_char(env, '$'); - ia32_emit_immediate(env, node); - } else { - if (is_ia32_Mul(node) || is_ia32_IMul1OP(node)) { - ia32_emit_source_register(env, node, 3); - } else if(is_ia32_IDiv(node) || is_ia32_Div(node)) { - ia32_emit_source_register(env, node, 4); - } else if(is_ia32_Push(node)) { - ia32_emit_source_register(env, node, 2); - } else if(is_ia32_Pop(node)) { - ia32_emit_dest_register(env, node, 1); - } else { - ia32_emit_dest_register(env, node, 0); - } - } - break; - case ia32_AddrModeS: - case ia32_AddrModeD: - ia32_emit_am(env, node); - break; - default: - assert(0 && "unsupported op type"); + case ia32_Normal: + op = get_irn_n(node, pos); + if (is_ia32_Immediate(op)) { + emit_ia32_Immediate(env, op); + } else if (is_ia32_ImmConst(node) || is_ia32_ImmSymConst(node)) { + ia32_emit_immediate(env, node); + } else { + ia32_emit_source_register(env, node, pos); + } + break; + case ia32_AddrModeS: + case ia32_AddrModeD: + ia32_emit_am(env, node); + break; + default: + assert(0 && "unsupported op type"); } } @@ -493,42 +553,59 @@ void ia32_emit_unop(ia32_emit_env_t *env, const ir_node *node) { * Emits address mode. */ void ia32_emit_am(ia32_emit_env_t *env, const ir_node *node) { - ia32_am_flavour_t am_flav = get_ia32_am_flavour(node); - ident *id = get_ia32_am_sc(node); - int offs = get_ia32_am_offs_int(node); + ir_entity *ent = get_ia32_am_sc(node); + int offs = get_ia32_am_offs_int(node); + ir_node *base = get_irn_n(node, 0); + int has_base = !is_ia32_NoReg_GP(base); + ir_node *index = get_irn_n(node, 1); + int has_index = !is_ia32_NoReg_GP(index); /* just to be sure... */ assert(!is_ia32_use_frame(node) || get_ia32_frame_ent(node) != NULL); /* emit offset */ - if (id != NULL) { + if (ent != NULL) { + ident *id; + + set_entity_backend_marked(ent, 1); + id = get_entity_ld_ident(ent); if (is_ia32_am_sc_sign(node)) be_emit_char(env, '-'); be_emit_ident(env, id); + + if(get_entity_owner(ent) == get_tls_type()) { + if (get_entity_visibility(ent) == visibility_external_allocated) { + be_emit_cstring(env, "@INDNTPOFF"); + } else { + be_emit_cstring(env, "@NTPOFF"); + } + } } if(offs != 0) { - if(id != NULL) { + if(ent != NULL) { be_emit_irprintf(env->emit, "%+d", offs); } else { be_emit_irprintf(env->emit, "%d", offs); } } - if (am_flav & (ia32_B | ia32_I)) { + if (has_base || has_index) { be_emit_char(env, '('); /* emit base */ - if (am_flav & ia32_B) { + if (has_base) { ia32_emit_source_register(env, node, 0); } /* emit index + scale */ - if (am_flav & ia32_I) { + if (has_index) { + int scale; be_emit_char(env, ','); ia32_emit_source_register(env, node, 1); - if (am_flav & ia32_S) { + scale = get_ia32_am_scale(node); + if (scale > 0) { be_emit_irprintf(env->emit, ",%d", 1 << get_ia32_am_scale(node)); } } @@ -560,7 +637,8 @@ struct cmp2conditon_t { /* * positive conditions for signed compares */ -static const struct cmp2conditon_t cmp2condition_s[] = { +static +const struct cmp2conditon_t cmp2condition_s[] = { { NULL, pn_Cmp_False }, /* always false */ { "e", pn_Cmp_Eq }, /* == */ { "l", pn_Cmp_Lt }, /* < */ @@ -582,7 +660,8 @@ static const struct cmp2conditon_t cmp2condition_s[] = { /* * positive conditions for unsigned compares */ -static const struct cmp2conditon_t cmp2condition_u[] = { +static +const struct cmp2conditon_t cmp2condition_u[] = { { NULL, pn_Cmp_False }, /* always false */ { "e", pn_Cmp_Eq }, /* == */ { "b", pn_Cmp_Lt }, /* < */ @@ -596,7 +675,8 @@ static const struct cmp2conditon_t cmp2condition_u[] = { /* * returns the condition code */ -static const char *get_cmp_suffix(int cmp_code) +static +const char *get_cmp_suffix(pn_Cmp cmp_code) { assert( (cmp2condition_s[cmp_code & 15].num) == (cmp_code & 15)); assert( (cmp2condition_u[cmp_code & 7].num) == (cmp_code & 7)); @@ -617,18 +697,29 @@ void ia32_emit_cmp_suffix(ia32_emit_env_t *env, long pnc) /** * Returns the target block for a control flow node. */ -static ir_node *get_cfop_target_block(const ir_node *irn) { +static +ir_node *get_cfop_target_block(const ir_node *irn) { return get_irn_link(irn); } /** - * Returns the target label for a control flow node. + * Emits a block label for the given block. */ +static +void ia32_emit_block_name(ia32_emit_env_t *env, const ir_node *block) +{ + be_emit_cstring(env, BLOCK_PREFIX); + be_emit_irprintf(env->emit, "%d", get_irn_node_nr(block)); +} + +/** + * Emits the target label for a control flow node. + */ +static void ia32_emit_cfop_target(ia32_emit_env_t * env, const ir_node *node) { ir_node *block = get_cfop_target_block(node); - be_emit_cstring(env, BLOCK_PREFIX); - be_emit_irprintf(env->emit, "%d", get_irn_node_nr(block)); + ia32_emit_block_name(env, block); } /** Return the next block in Block schedule */ @@ -639,7 +730,8 @@ static ir_node *next_blk_sched(const ir_node *block) { /** * Returns the Proj with projection number proj and NOT mode_M */ -static ir_node *get_proj(const ir_node *node, long proj) { +static +ir_node *get_proj(const ir_node *node, long proj) { const ir_edge_t *edge; ir_node *src; @@ -661,8 +753,9 @@ static ir_node *get_proj(const ir_node *node, long proj) { /** * Emits the jump sequence for a conditional jump (cmp + jmp_true + jmp_false) */ -static void finish_CondJmp(ia32_emit_env_t *env, const ir_node *node, - ir_mode *mode, long pnc) { +static +void finish_CondJmp(ia32_emit_env_t *env, const ir_node *node, ir_mode *mode, + long pnc) { const ir_node *proj_true; const ir_node *proj_false; const ir_node *block; @@ -711,7 +804,7 @@ static void finish_CondJmp(ia32_emit_env_t *env, const ir_node *node, ia32_emit_cfop_target(env, proj_false); be_emit_finish_line_gas(env, proj_false); } else { - be_emit_cstring(env, "\t/* fallthrough to"); + be_emit_cstring(env, "\t/* fallthrough to "); ia32_emit_cfop_target(env, proj_false); be_emit_cstring(env, " */"); be_emit_finish_line_gas(env, proj_false); @@ -721,7 +814,8 @@ static void finish_CondJmp(ia32_emit_env_t *env, const ir_node *node, /** * Emits code for conditional jump. */ -static void CondJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { +static +void CondJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, "\tcmp "); ia32_emit_binop(env, node); be_emit_finish_line_gas(env, node); @@ -732,45 +826,41 @@ static void CondJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code for conditional jump with two variables. */ -static void emit_ia32_CondJmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_CondJmp(ia32_emit_env_t *env, const ir_node *node) { CondJmp_emitter(env, node); } /** * Emits code for conditional test and jump. */ -static void TestJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { - if(is_ia32_ImmSymConst(node) || is_ia32_ImmConst(node)) { - be_emit_cstring(env, "\ttest $"); - ia32_emit_immediate(env, node); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - be_emit_finish_line_gas(env, node); - } else { - be_emit_cstring(env, "\ttest "); - ia32_emit_source_register(env, node, 1); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - be_emit_finish_line_gas(env, node); - } +static +void TestJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { + be_emit_cstring(env, "\ttest "); + ia32_emit_binop(env, node); + be_emit_finish_line_gas(env, node); + finish_CondJmp(env, node, mode_Iu, get_ia32_pncode(node)); } /** * Emits code for conditional test and jump with two variables. */ -static void emit_ia32_TestJmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_TestJmp(ia32_emit_env_t *env, const ir_node *node) { TestJmp_emitter(env, node); } -static void emit_ia32_CJmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_CJmp(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, "/* omitted redundant test */"); be_emit_finish_line_gas(env, node); finish_CondJmp(env, node, mode_Is, get_ia32_pncode(node)); } -static void emit_ia32_CJmpAM(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_CJmpAM(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, "/* omitted redundant test/cmp */"); be_emit_finish_line_gas(env, node); @@ -780,7 +870,8 @@ static void emit_ia32_CJmpAM(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code for conditional SSE floating point jump with two variables. */ -static void emit_ia32_xCondJmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_xCondJmp(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, "\tucomi"); ia32_emit_xmm_mode_suffix(env, node); be_emit_char(env, ' '); @@ -793,22 +884,23 @@ static void emit_ia32_xCondJmp(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code for conditional x87 floating point jump with two variables. */ -static void emit_ia32_x87CondJmp(ia32_emit_env_t *env, const ir_node *node) { - ia32_attr_t *attr = get_ia32_attr(node); - const char *reg = attr->x87[1]->name; - long pnc = get_ia32_pncode(node); +static +void emit_ia32_x87CondJmp(ia32_emit_env_t *env, const ir_node *node) { + const ia32_x87_attr_t *x87_attr = get_ia32_x87_attr_const(node); + const char *reg = x87_attr->x87[1]->name; + long pnc = get_ia32_pncode(node); switch (get_ia32_irn_opcode(node)) { case iro_ia32_fcomrJmp: pnc = get_inversed_pnc(pnc); - reg = attr->x87[0]->name; + reg = x87_attr->x87[0]->name; case iro_ia32_fcomJmp: default: be_emit_cstring(env, "\tfucom "); break; case iro_ia32_fcomrpJmp: pnc = get_inversed_pnc(pnc); - reg = attr->x87[0]->name; + reg = x87_attr->x87[0]->name; case iro_ia32_fcompJmp: be_emit_cstring(env, "\tfucomp "); break; @@ -834,37 +926,62 @@ static void emit_ia32_x87CondJmp(ia32_emit_env_t *env, const ir_node *node) { finish_CondJmp(env, node, mode_E, pnc); } -static void CMov_emitter(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_register_or_immediate(ia32_emit_env_t *env, const ir_node *node, + int pos) +{ + ir_node *op = get_irn_n(node, pos); + if(is_ia32_Immediate(op)) { + emit_ia32_Immediate(env, op); + } else { + ia32_emit_source_register(env, node, pos); + } +} + +static +int is_ia32_Immediate_0(const ir_node *node) +{ + const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node); + + return attr->offset == 0 && attr->symconst == NULL; +} + +static +void CMov_emitter(ia32_emit_env_t *env, const ir_node *node) +{ long pnc = get_ia32_pncode(node); - int is_PsiCondCMov = is_ia32_PsiCondCMov(node); - int idx_left = 2 - is_PsiCondCMov; - int idx_right = 3 - is_PsiCondCMov; const arch_register_t *in1, *in2, *out; out = arch_get_irn_register(env->arch_env, node); - in1 = arch_get_irn_register(env->arch_env, get_irn_n(node, idx_left)); - in2 = arch_get_irn_register(env->arch_env, get_irn_n(node, idx_right)); + in1 = arch_get_irn_register(env->arch_env, get_irn_n(node, 2)); + in2 = arch_get_irn_register(env->arch_env, get_irn_n(node, 3)); /* we have to emit the cmp first, because the destination register */ /* could be one of the compare registers */ if (is_ia32_CmpCMov(node)) { - be_emit_cstring(env, "\tcmp "); - ia32_emit_source_register(env, node, 1); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); + long pncr = pnc & ~ia32_pn_Cmp_Unsigned; + ir_node *cmp_right = get_irn_n(node, 1); + + if( (pncr == pn_Cmp_Eq || pncr == pn_Cmp_Lg) + && is_ia32_Immediate(cmp_right) + && is_ia32_Immediate_0(cmp_right)) { + be_emit_cstring(env, "\ttest "); + ia32_emit_source_register(env, node, 0); + be_emit_cstring(env, ", "); + ia32_emit_source_register(env, node, 0); + } else { + be_emit_cstring(env, "\tcmp "); + emit_register_or_immediate(env, node, 1); + be_emit_cstring(env, ", "); + ia32_emit_source_register(env, node, 0); + } } else if (is_ia32_xCmpCMov(node)) { be_emit_cstring(env, "\tucomis"); - ia32_emit_mode_suffix(env, get_irn_mode(node)); + ia32_emit_mode_suffix_mode(env, get_irn_mode(node)); be_emit_char(env, ' '); ia32_emit_source_register(env, node, 1); be_emit_cstring(env, ", "); ia32_emit_source_register(env, node, 0); - } else if (is_PsiCondCMov) { - /* omit compare because flags are already set by And/Or */ - be_emit_cstring(env, "\ttest "); - ia32_emit_source_register(env, node, 0); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); } else { assert(0 && "unsupported CMov"); } @@ -875,59 +992,45 @@ static void CMov_emitter(ia32_emit_env_t *env, const ir_node *node) { } else if (REGS_ARE_EQUAL(out, in1)) { ir_node *n = (ir_node*) node; /* true in == out -> need complement compare and exchange true and default in */ - ir_node *t = get_irn_n(n, idx_left); - set_irn_n(n, idx_left, get_irn_n(n, idx_right)); - set_irn_n(n, idx_right, t); + ir_node *t = get_irn_n(n, 2); + set_irn_n(n, 2, get_irn_n(n, 3)); + set_irn_n(n, 3, t); pnc = get_negated_pnc(pnc, get_irn_mode(node)); } else { /* out is different from in: need copy default -> out */ - if (is_PsiCondCMov) { - be_emit_cstring(env, "\tmovl "); - ia32_emit_dest_register(env, node, 2); - be_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } else { - be_emit_cstring(env, "\tmovl "); - ia32_emit_source_register(env, node, 3); - be_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } - be_emit_finish_line_gas(env, node); - } - - if (is_PsiCondCMov) { - be_emit_cstring(env, "\tcmov"); - ia32_emit_cmp_suffix(env, pnc); - be_emit_cstring(env, "l "); - ia32_emit_source_register(env, node, 1); - be_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } else { - be_emit_cstring(env, "\tcmov"); - ia32_emit_cmp_suffix(env, pnc); - be_emit_cstring(env, "l "); - ia32_emit_source_register(env, node, 2); + be_emit_cstring(env, "\tmovl "); + ia32_emit_source_register(env, node, n_ia32_CmpCMov_val_false); be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); + be_emit_finish_line_gas(env, node); } - be_emit_finish_line_gas(env, node); -} -static void emit_ia32_CmpCMov(ia32_emit_env_t *env, const ir_node *node) { - CMov_emitter(env, node); + be_emit_cstring(env, "\tcmov"); + ia32_emit_cmp_suffix(env, pnc); + be_emit_cstring(env, "l "); + ia32_emit_source_register(env, node, n_ia32_CmpCMov_val_true); + be_emit_cstring(env, ", "); + ia32_emit_dest_register(env, node, 0); + be_emit_finish_line_gas(env, node); } -static void emit_ia32_PsiCondCMov(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_CmpCMov(ia32_emit_env_t *env, const ir_node *node) +{ CMov_emitter(env, node); } -static void emit_ia32_xCmpCMov(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_xCmpCMov(ia32_emit_env_t *env, const ir_node *node) +{ CMov_emitter(env, node); } -static void Set_emitter(ia32_emit_env_t *env, const ir_node *node, ir_mode *mode) { - int pnc = get_ia32_pncode(node); +static +void Set_emitter(ia32_emit_env_t *env, const ir_node *node) +{ + long pnc = get_ia32_pncode(node); const char *reg8bit; const arch_register_t *out; @@ -935,16 +1038,25 @@ static void Set_emitter(ia32_emit_env_t *env, const ir_node *node, ir_mode *mode reg8bit = ia32_get_mapped_reg_name(env->isa->regs_8bit, out); if (is_ia32_CmpSet(node)) { - be_emit_cstring(env, "\tcmp "); - ia32_emit_binop(env, node); + long pncr = pnc & ~ia32_pn_Cmp_Unsigned; + ir_node *cmp_right = get_irn_n(node, n_ia32_CmpSet_cmp_right); + + if( (pncr == pn_Cmp_Eq || pncr == pn_Cmp_Lg) + && is_ia32_Immediate(cmp_right) + && is_ia32_Immediate_0(cmp_right)) { + be_emit_cstring(env, "\ttest "); + ia32_emit_source_register(env, node, n_ia32_CmpSet_cmp_left); + be_emit_cstring(env, ", "); + ia32_emit_source_register(env, node, n_ia32_CmpSet_cmp_left); + } else { + be_emit_cstring(env, "\tcmp "); + ia32_emit_binop(env, node); + } } else if (is_ia32_xCmpSet(node)) { be_emit_cstring(env, "\tucomis"); - ia32_emit_mode_suffix(env, get_irn_mode(get_irn_n(node, 2))); + ia32_emit_mode_suffix_mode(env, get_irn_mode(get_irn_n(node, 2))); be_emit_char(env, ' '); ia32_emit_binop(env, node); - } else if (is_ia32_PsiCondSet(node)) { - be_emit_cstring(env, "\tcmp $0, "); - ia32_emit_source_register(env, node, 0); } else { assert(0 && "unsupported Set"); } @@ -962,19 +1074,18 @@ static void Set_emitter(ia32_emit_env_t *env, const ir_node *node, ir_mode *mode be_emit_finish_line_gas(env, node); } -static void emit_ia32_CmpSet(ia32_emit_env_t *env, const ir_node *node) { - Set_emitter(env, node, get_irn_mode(get_irn_n(node, 2))); -} - -static void emit_ia32_PsiCondSet(ia32_emit_env_t *env, const ir_node *node) { - Set_emitter(env, node, get_irn_mode(get_irn_n(node, 0))); +static +void emit_ia32_CmpSet(ia32_emit_env_t *env, const ir_node *node) { + Set_emitter(env, node); } -static void emit_ia32_xCmpSet(ia32_emit_env_t *env, const ir_node *node) { - Set_emitter(env, node, get_irn_mode(get_irn_n(node, 2))); +static +void emit_ia32_xCmpSet(ia32_emit_env_t *env, const ir_node *node) { + Set_emitter(env, node); } -static void emit_ia32_xCmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_xCmp(ia32_emit_env_t *env, const ir_node *node) { int sse_pnc = -1; long pnc = get_ia32_pncode(node); long unord = pnc & pn_Cmp_Uo; @@ -1075,9 +1186,9 @@ typedef struct _branch_t { /* jump table for switch generation */ typedef struct _jmp_tbl_t { ir_node *defProj; /**< default target */ - int min_value; /**< smallest switch case */ - int max_value; /**< largest switch case */ - int num_branches; /**< number of jumps */ + long min_value; /**< smallest switch case */ + long max_value; /**< largest switch case */ + long num_branches; /**< number of jumps */ char *label; /**< label of the jump table */ branch_t *branches; /**< jump array */ } jmp_tbl_t; @@ -1085,7 +1196,8 @@ typedef struct _jmp_tbl_t { /** * Compare two variables of type branch_t. Used to sort all switch cases */ -static int ia32_cmp_branch_t(const void *a, const void *b) { +static +int ia32_cmp_branch_t(const void *a, const void *b) { branch_t *b1 = (branch_t *)a; branch_t *b2 = (branch_t *)b; @@ -1100,7 +1212,8 @@ static int ia32_cmp_branch_t(const void *a, const void *b) { * possible otherwise a cmp-jmp cascade). Port from * cggg ia32 backend */ -static void emit_ia32_SwitchJmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_SwitchJmp(ia32_emit_env_t *env, const ir_node *node) { unsigned long interval; int last_value, i; long pnc; @@ -1206,7 +1319,8 @@ static void emit_ia32_SwitchJmp(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code for a unconditional jump. */ -static void emit_Jmp(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_Jmp(ia32_emit_env_t *env, const ir_node *node) { ir_node *block, *next_block; /* for now, the code works for scheduled and non-schedules blocks */ @@ -1225,6 +1339,170 @@ static void emit_Jmp(ia32_emit_env_t *env, const ir_node *node) { be_emit_finish_line_gas(env, node); } +static +void emit_ia32_Immediate(ia32_emit_env_t *env, const ir_node *node) +{ + const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node); + + be_emit_char(env, '$'); + if(attr->symconst != NULL) { + ident *id = get_entity_ld_ident(attr->symconst); + + if(attr->attr.data.am_sc_sign) + be_emit_char(env, '-'); + be_emit_ident(env, id); + } + if(attr->symconst == NULL || attr->offset != 0) { + if(attr->symconst != NULL) + be_emit_char(env, '+'); + be_emit_irprintf(env->emit, "%d", attr->offset); + } +} + +static +const char* emit_asm_operand(ia32_emit_env_t *env, const ir_node *node, + const char *s) +{ + const arch_register_t *reg; + const char *reg_name; + char c; + char modifier = 0; + int num = -1; + const ia32_attr_t *attr; + int n_outs; + int p; + + assert(*s == '%'); + c = *(++s); + + /* parse modifiers */ + switch(c) { + case 0: + ir_fprintf(stderr, "Warning: asm text (%+F) ends with %\n", node); + be_emit_char(env, '%'); + return s + 1; + case '%': + be_emit_char(env, '%'); + return s + 1; + case 'w': + case 'b': + case 'h': + modifier = c; + ++s; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + ir_fprintf(stderr, "Warning: asm text (%+F) contains unknown modifier " + "'%c' for asm op\n", node, c); + ++s; + break; + } + + /* parse number */ + sscanf(s, "%d%n", &num, &p); + if(num < 0) { + ir_fprintf(stderr, "Warning: Couldn't parse assembler operand (%+F)\n", + node); + return s; + } else { + s += p; + } + + /* get register */ + attr = get_ia32_attr_const(node); + n_outs = ARR_LEN(attr->slots); + if(num < n_outs) { + reg = get_out_reg(env, node, num); + } else { + ir_node *pred; + int in = num - n_outs; + if(in >= get_irn_arity(node)) { + ir_fprintf(stderr, "Warning: Invalid input %d specified in asm " + "op (%+F)\n", num, node); + return s; + } + pred = get_irn_n(node, in); + /* might be an immediate value */ + if(is_ia32_Immediate(pred)) { + emit_ia32_Immediate(env, pred); + return s; + } + reg = get_in_reg(env, node, in); + } + if(reg == NULL) { + ir_fprintf(stderr, "Warning: no register assigned for %d asm op " + "(%+F)\n", num, node); + return s; + } + + /* emit it */ + be_emit_char(env, '%'); + switch(modifier) { + case 0: + reg_name = arch_register_get_name(reg); + break; + case 'b': + reg_name = ia32_get_mapped_reg_name(env->isa->regs_8bit, reg); + break; + case 'h': + reg_name = ia32_get_mapped_reg_name(env->isa->regs_8bit_high, reg); + break; + case 'w': + reg_name = ia32_get_mapped_reg_name(env->isa->regs_16bit, reg); + break; + default: + panic("Invalid asm op modifier"); + } + be_emit_string(env, reg_name); + + return s; +} + +/** + * Emits code for an ASM pseudo op. + */ +static +void emit_ia32_Asm(ia32_emit_env_t *env, const ir_node *node) +{ + const void *gen_attr = get_irn_generic_attr_const(node); + const ia32_asm_attr_t *attr + = CONST_CAST_IA32_ATTR(ia32_asm_attr_t, gen_attr); + ident *asm_text = attr->asm_text; + const char *s = get_id_str(asm_text); + + be_emit_cstring(env, "# Begin ASM \t"); + be_emit_finish_line_gas(env, node); + + if (s[0] != '\t') + be_emit_char(env, '\t'); + + while(*s != 0) { + if(*s == '%') { + s = emit_asm_operand(env, node, s); + continue; + } else { + be_emit_char(env, *s); + } + ++s; + } + + be_emit_char(env, '\n'); + be_emit_write_line(env); + + be_emit_cstring(env, "# End ASM\n"); + be_emit_write_line(env); +} + /********************************** * _____ ____ * / ____| | _ \ @@ -1239,7 +1517,8 @@ static void emit_Jmp(ia32_emit_env_t *env, const ir_node *node) { /** * Emit movsb/w instructions to make mov count divideable by 4 */ -static void emit_CopyB_prolog(ia32_emit_env_t *env, int rem) { +static +void emit_CopyB_prolog(ia32_emit_env_t *env, int rem) { be_emit_cstring(env, "\tcld"); be_emit_finish_line_gas(env, NULL); @@ -1264,7 +1543,8 @@ static void emit_CopyB_prolog(ia32_emit_env_t *env, int rem) { /** * Emit rep movsd instruction for memcopy. */ -static void emit_ia32_CopyB(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_CopyB(ia32_emit_env_t *env, const ir_node *node) { tarval *tv = get_ia32_Immop_tarval(node); int rem = get_tarval_long(tv); @@ -1277,7 +1557,8 @@ static void emit_ia32_CopyB(ia32_emit_env_t *env, const ir_node *node) { /** * Emits unrolled memcopy. */ -static void emit_ia32_CopyB_i(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_CopyB_i(ia32_emit_env_t *env, const ir_node *node) { tarval *tv = get_ia32_Immop_tarval(node); int size = get_tarval_long(tv); @@ -1305,7 +1586,8 @@ static void emit_ia32_CopyB_i(ia32_emit_env_t *env, const ir_node *node) { /** * Emit code for conversions (I, FP), (FP, I) and (FP, FP). */ -static void emit_ia32_Conv_with_FP(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_Conv_with_FP(ia32_emit_env_t *env, const ir_node *node) { ir_mode *ls_mode = get_ia32_ls_mode(node); int ls_bits = get_mode_size_bits(ls_mode); @@ -1350,22 +1632,26 @@ static void emit_ia32_Conv_with_FP(ia32_emit_env_t *env, const ir_node *node) { be_emit_finish_line_gas(env, node); } -static void emit_ia32_Conv_I2FP(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_Conv_I2FP(ia32_emit_env_t *env, const ir_node *node) { emit_ia32_Conv_with_FP(env, node); } -static void emit_ia32_Conv_FP2I(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_Conv_FP2I(ia32_emit_env_t *env, const ir_node *node) { emit_ia32_Conv_with_FP(env, node); } -static void emit_ia32_Conv_FP2FP(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_Conv_FP2FP(ia32_emit_env_t *env, const ir_node *node) { emit_ia32_Conv_with_FP(env, node); } /** * Emits code for an Int conversion. */ -static void emit_ia32_Conv_I2I(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_Conv_I2I(ia32_emit_env_t *env, const ir_node *node) { const char *sign_suffix; ir_mode *smaller_mode = get_ia32_ls_mode(node); int smaller_bits = get_mode_size_bits(smaller_mode); @@ -1386,35 +1672,23 @@ static void emit_ia32_Conv_I2I(ia32_emit_env_t *env, const ir_node *node) { switch(get_ia32_op_type(node)) { case ia32_Normal: - in_reg = get_in_reg(node, 2); - out_reg = get_out_reg(node, 0); + in_reg = get_in_reg(env, node, 2); + out_reg = get_out_reg(env, node, 0); if (REGS_ARE_EQUAL(in_reg, &ia32_gp_regs[REG_EAX]) && REGS_ARE_EQUAL(out_reg, in_reg) && - signed_mode) + signed_mode && + smaller_bits == 16) { /* argument and result are both in EAX and */ - /* signedness is ok: -> use converts */ - if (smaller_bits == 8) { - be_emit_cstring(env, "\tcbtw"); - } else if (smaller_bits == 16) { - be_emit_cstring(env, "\tcwtl"); - } else { - assert(0); - } - } else if (REGS_ARE_EQUAL(out_reg, in_reg) && !signed_mode) { - /* argument and result are in the same register */ - /* and signedness is ok: -> use and with mask */ - int mask = (1 << smaller_bits) - 1; - be_emit_cstring(env, "\tandl $0x"); - be_emit_irprintf(env->emit, "%x, ", mask); - ia32_emit_dest_register(env, node, 0); + /* signedness is ok: -> use the smaller cwtl opcode */ + be_emit_cstring(env, "\tcwtl"); } else { const char *sreg = ia32_get_reg_name_for_mode(env, smaller_mode, in_reg); be_emit_cstring(env, "\tmov"); be_emit_string(env, sign_suffix); - ia32_emit_mode_suffix(env, smaller_mode); + ia32_emit_mode_suffix_mode(env, smaller_mode); be_emit_cstring(env, "l %"); be_emit_string(env, sreg); be_emit_cstring(env, ", "); @@ -1424,7 +1698,7 @@ static void emit_ia32_Conv_I2I(ia32_emit_env_t *env, const ir_node *node) { case ia32_AddrModeS: { be_emit_cstring(env, "\tmov"); be_emit_string(env, sign_suffix); - ia32_emit_mode_suffix(env, smaller_mode); + ia32_emit_mode_suffix_mode(env, smaller_mode); be_emit_cstring(env, "l %"); ia32_emit_am(env, node); be_emit_cstring(env, ", "); @@ -1458,11 +1732,13 @@ void emit_ia32_Conv_I2I8Bit(ia32_emit_env_t *env, const ir_node *node) { /** * Emits a backend call */ -static void emit_be_Call(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_Call(ia32_emit_env_t *env, const ir_node *node) { ir_entity *ent = be_Call_get_entity(node); be_emit_cstring(env, "\tcall "); if (ent) { + set_entity_backend_marked(ent, 1); be_emit_string(env, get_entity_ld_name(ent)); } else { be_emit_char(env, '*'); @@ -1474,7 +1750,8 @@ static void emit_be_Call(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code to increase stack pointer. */ -static void emit_be_IncSP(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_IncSP(ia32_emit_env_t *env, const ir_node *node) { int offs = be_get_IncSP_offset(node); if (offs == 0) @@ -1495,7 +1772,8 @@ static void emit_be_IncSP(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code to set stack pointer. */ -static void emit_be_SetSP(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_SetSP(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, "\tmovl "); ia32_emit_source_register(env, node, 2); be_emit_cstring(env, ", "); @@ -1506,14 +1784,18 @@ static void emit_be_SetSP(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code for Copy/CopyKeep. */ -static void Copy_emitter(ia32_emit_env_t *env, const ir_node *node, const ir_node *op) { +static +void Copy_emitter(ia32_emit_env_t *env, const ir_node *node, const ir_node *op) +{ const arch_env_t *aenv = env->arch_env; + ir_mode *mode; if (REGS_ARE_EQUAL(arch_get_irn_register(aenv, node), arch_get_irn_register(aenv, op)) || arch_register_type_is(arch_get_irn_register(aenv, op), virtual)) return; - if (mode_is_float(get_irn_mode(node))) { + mode = get_irn_mode(node); + if (mode == mode_E) { be_emit_cstring(env, "\tmovsd "); ia32_emit_source_register(env, node, 0); be_emit_cstring(env, ", "); @@ -1527,18 +1809,21 @@ static void Copy_emitter(ia32_emit_env_t *env, const ir_node *node, const ir_nod be_emit_finish_line_gas(env, node); } -static void emit_be_Copy(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_Copy(ia32_emit_env_t *env, const ir_node *node) { Copy_emitter(env, node, be_get_Copy_op(node)); } -static void emit_be_CopyKeep(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_CopyKeep(ia32_emit_env_t *env, const ir_node *node) { Copy_emitter(env, node, be_get_CopyKeep_op(node)); } /** * Emits code for exchange. */ -static void emit_be_Perm(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_Perm(ia32_emit_env_t *env, const ir_node *node) { const arch_register_t *in1, *in2; const arch_register_class_t *cls1, *cls2; @@ -1551,26 +1836,11 @@ static void emit_be_Perm(ia32_emit_env_t *env, const ir_node *node) { assert(cls1 == cls2 && "Register class mismatch at Perm"); if (cls1 == &ia32_reg_classes[CLASS_ia32_gp]) { -#if 0 - if(emit_env->isa->opt_arch == arch_athlon) { - // xchg commands are Vector path on athlons and therefore stall the DirectPath pipeline - // it is often beneficial to use the 3 xor trick instead of an xchg - cmnt_buf[0] = 0; - lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "xor %1S, %2S", irn, irn); - IA32_DO_EMIT(irn); - lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "xor %2S, %1S", irn, irn); - IA32_DO_EMIT(irn); - lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "xor %1S, %2S", irn, irn); - } else { -#endif - be_emit_cstring(env, "\txchg "); - ia32_emit_source_register(env, node, 1); - be_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - be_emit_finish_line_gas(env, node); -#if 0 - } -#endif + be_emit_cstring(env, "\txchg "); + ia32_emit_source_register(env, node, 1); + be_emit_cstring(env, ", "); + ia32_emit_source_register(env, node, 0); + be_emit_finish_line_gas(env, node); } else if (cls1 == &ia32_reg_classes[CLASS_ia32_xmm]) { be_emit_cstring(env, "\txorpd "); ia32_emit_source_register(env, node, 1); @@ -1599,11 +1869,12 @@ static void emit_be_Perm(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code for Constant loading. */ -static void emit_ia32_Const(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_Const(ia32_emit_env_t *env, const ir_node *node) { ia32_immop_type_t imm_tp = get_ia32_immop_type(node); if (imm_tp == ia32_ImmSymConst) { - be_emit_cstring(env, "\tmovl $"); + be_emit_cstring(env, "\tmovl "); ia32_emit_immediate(env, node); be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); @@ -1622,7 +1893,7 @@ static void emit_ia32_Const(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } else { - be_emit_cstring(env, "\tmovl $"); + be_emit_cstring(env, "\tmovl "); ia32_emit_immediate(env, node); be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); @@ -1634,18 +1905,25 @@ static void emit_ia32_Const(ia32_emit_env_t *env, const ir_node *node) { /** * Emits code to load the TLS base */ -static void emit_ia32_LdTls(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_ia32_LdTls(ia32_emit_env_t *env, const ir_node *node) { be_emit_cstring(env, "\tmovl %gs:0, "); ia32_emit_dest_register(env, node, 0); be_emit_finish_line_gas(env, node); } -static void emit_be_Return(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_be_Return(ia32_emit_env_t *env, const ir_node *node) +{ be_emit_cstring(env, "\tret"); be_emit_finish_line_gas(env, node); } -static void emit_Nothing(ia32_emit_env_t *env, const ir_node *node) { +static +void emit_Nothing(ia32_emit_env_t *env, const ir_node *node) +{ + (void) env; + (void) node; } @@ -1663,7 +1941,8 @@ static void emit_Nothing(ia32_emit_env_t *env, const ir_node *node) { * Enters the emitter functions for handled nodes into the generic * pointer of an opcode. */ -static void ia32_register_emitters(void) { +static +void ia32_register_emitters(void) { #define IA32_EMIT2(a,b) op_ia32_##a->ops.generic = (op_func)emit_ia32_##b #define IA32_EMIT(a) IA32_EMIT2(a,a) @@ -1679,14 +1958,13 @@ static void ia32_register_emitters(void) { ia32_register_spec_emitters(); /* other ia32 emitter functions */ + IA32_EMIT(Asm); IA32_EMIT(CondJmp); IA32_EMIT(TestJmp); IA32_EMIT(CJmp); IA32_EMIT(CJmpAM); IA32_EMIT(CmpCMov); - IA32_EMIT(PsiCondCMov); IA32_EMIT(CmpSet); - IA32_EMIT(PsiCondSet); IA32_EMIT(SwitchJmp); IA32_EMIT(CopyB); IA32_EMIT(CopyB_i); @@ -1741,7 +2019,8 @@ static unsigned num = -1; /** * Emit the debug support for node node. */ -static void ia32_emit_dbg(ia32_emit_env_t *env, const ir_node *node) { +static +void ia32_emit_dbg(ia32_emit_env_t *env, const ir_node *node) { dbg_info *db = get_irn_dbg_info(node); unsigned lineno; const char *fname = be_retrieve_dbg_info(db, &lineno); @@ -1773,11 +2052,11 @@ typedef void (*emit_func_ptr) (ia32_emit_env_t *, const ir_node *); /** * Emits code for a node. */ -static void ia32_emit_node(ia32_emit_env_t *env, const ir_node *node) { +static +void ia32_emit_node(ia32_emit_env_t *env, const ir_node *node) { ir_op *op = get_irn_op(node); - DEBUG_ONLY(firm_dbg_module_t *mod = env->mod;) - DBG((mod, LEVEL_1, "emitting code for %+F\n", node)); + DBG((dbg, LEVEL_1, "emitting code for %+F\n", node)); if (op->ops.generic) { emit_func_ptr func = (emit_func_ptr) op->ops.generic; @@ -1792,7 +2071,8 @@ static void ia32_emit_node(ia32_emit_env_t *env, const ir_node *node) { /** * Emits gas alignment directives */ -static void ia32_emit_alignment(ia32_emit_env_t *env, unsigned align, unsigned skip) { +static +void ia32_emit_alignment(ia32_emit_env_t *env, unsigned align, unsigned skip) { be_emit_cstring(env, "\t.p2align "); be_emit_irprintf(env->emit, "%u,,%u\n", align, skip); be_emit_write_line(env); @@ -1801,7 +2081,8 @@ static void ia32_emit_alignment(ia32_emit_env_t *env, unsigned align, unsigned s /** * Emits gas alignment directives for Functions depended on cpu architecture. */ -static void ia32_emit_align_func(ia32_emit_env_t *env, cpu_support cpu) { +static +void ia32_emit_align_func(ia32_emit_env_t *env, cpu_support cpu) { unsigned align; unsigned maximum_skip; @@ -1825,7 +2106,8 @@ static void ia32_emit_align_func(ia32_emit_env_t *env, cpu_support cpu) { /** * Emits gas alignment directives for Labels depended on cpu architecture. */ -static void ia32_emit_align_label(ia32_emit_env_t *env, cpu_support cpu) { +static +void ia32_emit_align_label(ia32_emit_env_t *env, cpu_support cpu) { unsigned align; unsigned maximum_skip; switch (cpu) { @@ -1845,11 +2127,21 @@ static void ia32_emit_align_label(ia32_emit_env_t *env, cpu_support cpu) { ia32_emit_alignment(env, align, maximum_skip); } -static int is_first_loop_block(ia32_emit_env_t *env, ir_node *block, ir_node *prev_block) { - ir_exec_freq *exec_freq = env->cg->birg->exec_freq; - double block_freq, prev_freq; +/** + * Test wether a block should be aligned. + * For cpus in the P4/Athlon class it is usefull to align jump labels to + * 16 bytes. However we should only do that if the alignment nops before the + * label aren't executed more often than we have jumps to the label. + */ +static +int should_align_block(ia32_emit_env_t *env, ir_node *block, ir_node *prev) { static const double DELTA = .0001; - cpu_support cpu = env->isa->opt_arch; + ir_exec_freq *exec_freq = env->cg->birg->exec_freq; + double block_freq; + double prev_freq = 0; /**< execfreq of the fallthrough block */ + double jmp_freq = 0; /**< execfreq of all non-fallthrough blocks */ + cpu_support cpu = env->isa->opt_arch; + int i, n_cfgpreds; if(exec_freq == NULL) return 0; @@ -1857,88 +2149,73 @@ static int is_first_loop_block(ia32_emit_env_t *env, ir_node *block, ir_node *pr return 0; block_freq = get_block_execfreq(exec_freq, block); - prev_freq = get_block_execfreq(exec_freq, prev_block); - - if(block_freq < DELTA || prev_freq < DELTA) + if(block_freq < DELTA) return 0; - block_freq /= prev_freq; + n_cfgpreds = get_Block_n_cfgpreds(block); + for(i = 0; i < n_cfgpreds; ++i) { + ir_node *pred = get_Block_cfgpred_block(block, i); + double pred_freq = get_block_execfreq(exec_freq, pred); + + if(pred == prev) { + prev_freq += pred_freq; + } else { + jmp_freq += pred_freq; + } + } + + if(prev_freq < DELTA && !(jmp_freq < DELTA)) + return 1; + + jmp_freq /= prev_freq; switch (cpu) { case arch_athlon: case arch_athlon_64: case arch_k6: - return block_freq > 3; + return jmp_freq > 3; default: - break; + return jmp_freq > 2; } - - return block_freq > 2; } -/** - * Walks over the nodes in a block connected by scheduling edges - * and emits code for each node. - */ -static void ia32_gen_block(ia32_emit_env_t *env, ir_node *block, ir_node *last_block) { - ir_graph *irg = get_irn_irg(block); - ir_node *start_block = get_irg_start_block(irg); - int need_label = 1; - const ir_node *node; - int i; - - assert(is_Block(block)); - - if (block == start_block) +static +void ia32_emit_block_header(ia32_emit_env_t *env, ir_node *block, ir_node *prev) +{ + int n_cfgpreds; + int need_label; + int i, arity; + ir_exec_freq *exec_freq = env->cg->birg->exec_freq; + + need_label = 1; + n_cfgpreds = get_Block_n_cfgpreds(block); + if (n_cfgpreds == 0) { need_label = 0; - - if (need_label && get_irn_arity(block) == 1) { - ir_node *pred_block = get_Block_cfgpred_block(block, 0); - - if (pred_block == last_block && get_irn_n_edges_kind(pred_block, EDGE_KIND_BLOCK) <= 2) + } else if (n_cfgpreds == 1) { + ir_node *pred = get_Block_cfgpred(block, 0); + ir_node *pred_block = get_nodes_block(pred); + + /* we don't need labels for fallthrough blocks, however switch-jmps + * are no fallthroughs */ + if(pred_block == prev && + !(is_Proj(pred) && is_ia32_SwitchJmp(get_Proj_pred(pred)))) { need_label = 0; - } - - /* special case: if one of our cfg preds is a switch-jmp we need a label, */ - /* otherwise there might be jump table entries jumping to */ - /* non-existent (omitted) labels */ - for (i = get_Block_n_cfgpreds(block) - 1; i >= 0; --i) { - ir_node *pred = get_Block_cfgpred(block, i); - - if (is_Proj(pred)) { - assert(get_irn_mode(pred) == mode_X); - if (is_ia32_SwitchJmp(get_Proj_pred(pred))) { - need_label = 1; - break; - } + } else { + need_label = 1; } + } else { + need_label = 1; } - if (need_label) { - int i, arity; - int align = 1; - ir_exec_freq *exec_freq = env->cg->birg->exec_freq; - - /* align the loop headers */ - if (! is_first_loop_block(env, block, last_block)) { - /* align blocks where the previous block has no fallthrough */ - arity = get_irn_arity(block); - - for (i = 0; i < arity; ++i) { - ir_node *predblock = get_Block_cfgpred_block(block, i); - - if (predblock == last_block) { - align = 0; - break; - } - } - } + if (should_align_block(env, block, prev)) { + assert(need_label); + ia32_emit_align_label(env, env->isa->opt_arch); + } - if (align) - ia32_emit_align_label(env, env->isa->opt_arch); + if(need_label) { + ia32_emit_block_name(env, block); + be_emit_char(env, ':'); - be_emit_cstring(env, BLOCK_PREFIX); - be_emit_irprintf(env->emit, "%d:", get_irn_node_nr(block)); be_emit_pad_comment(env); be_emit_cstring(env, " /* preds:"); @@ -1948,13 +2225,29 @@ static void ia32_gen_block(ia32_emit_env_t *env, ir_node *block, ir_node *last_b ir_node *predblock = get_Block_cfgpred_block(block, i); be_emit_irprintf(env->emit, " %d", get_irn_node_nr(predblock)); } - - if (exec_freq != NULL) { - be_emit_irprintf(env->emit, " freq: %f", get_block_execfreq(exec_freq, block)); - } - be_emit_cstring(env, " */\n"); - be_emit_write_line(env); + } else { + be_emit_cstring(env, "\t/* "); + ia32_emit_block_name(env, block); + be_emit_cstring(env, ": "); } + if (exec_freq != NULL) { + be_emit_irprintf(env->emit, " freq: %f", + get_block_execfreq(exec_freq, block)); + } + be_emit_cstring(env, " */\n"); + be_emit_write_line(env); +} + +/** + * Walks over the nodes in a block connected by scheduling edges + * and emits code for each node. + */ +static +void ia32_gen_block(ia32_emit_env_t *env, ir_node *block, ir_node *last_block) +{ + const ir_node *node; + + ia32_emit_block_header(env, block, last_block); /* emit the contents of the block */ ia32_emit_dbg(env, block); @@ -1966,7 +2259,8 @@ static void ia32_gen_block(ia32_emit_env_t *env, ir_node *block, ir_node *last_b /** * Emits code for function start. */ -static void ia32_emit_func_prolog(ia32_emit_env_t *env, ir_graph *irg) { +static +void ia32_emit_func_prolog(ia32_emit_env_t *env, ir_graph *irg) { ir_entity *irg_ent = get_irg_entity(irg); const char *irg_name = get_entity_ld_name(irg_ent); cpu_support cpu = env->isa->opt_arch; @@ -1991,7 +2285,8 @@ static void ia32_emit_func_prolog(ia32_emit_env_t *env, ir_graph *irg) { /** * Emits code for function end */ -static void ia32_emit_func_epilog(ia32_emit_env_t *env, ir_graph *irg) { +static +void ia32_emit_func_epilog(ia32_emit_env_t *env, ir_graph *irg) { const char *irg_name = get_entity_ld_name(get_irg_entity(irg)); const be_irg_t *birg = env->cg->birg; @@ -2005,9 +2300,12 @@ static void ia32_emit_func_epilog(ia32_emit_env_t *env, ir_graph *irg) { * Block-walker: * Sets labels for control flow nodes (jump target) */ -static void ia32_gen_labels(ir_node *block, void *data) { +static +void ia32_gen_labels(ir_node *block, void *data) +{ ir_node *pred; int n = get_Block_n_cfgpreds(block); + (void) data; for (n--; n >= 0; n--) { pred = get_Block_cfgpred(block, n); @@ -2015,6 +2313,16 @@ static void ia32_gen_labels(ir_node *block, void *data) { } } +/** + * Emit an exception label if the current instruction can fail. + */ +void ia32_emit_exc_label(ia32_emit_env_t *env, const ir_node *node) { + if (get_ia32_exc_label(node)) { + be_emit_irprintf(env->emit, ".EXL%u\n", 0); + be_emit_write_line(env); + } +} + /** * Main driver. Emits the code for one routine. */ @@ -2028,10 +2336,6 @@ void ia32_gen_routine(ia32_code_gen_t *cg, ir_graph *irg) { env.emit = &env.isa->emit; env.arch_env = cg->arch_env; env.cg = cg; - FIRM_DBG_REGISTER(env.mod, "firm.be.ia32.emitter"); - - /* set the global arch_env (needed by print hooks) */ - arch_env = cg->arch_env; ia32_register_emitters(); @@ -2054,3 +2358,8 @@ void ia32_gen_routine(ia32_code_gen_t *cg, ir_graph *irg) { ia32_emit_func_epilog(&env, irg); } + +void ia32_init_emitter(void) +{ + FIRM_DBG_REGISTER(dbg, "firm.be.ia32.emitter"); +}