X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fia32%2Fia32_emitter.c;h=ad6a9df2dc17db7e11bc63631be716775a16ddd6;hb=d9b6f3a9505efb7dc1f692d2f7958a575532bdbf;hp=7acb26a78b3322a629e57566d1b8f02be2dbf1f1;hpb=eb798733d50fbfb32b12ae9cb2f989d8b86f0d12;p=libfirm diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index 7acb26a78..ad6a9df2d 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -1,10 +1,30 @@ +/* + * 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 +#include "config.h" #endif #include @@ -21,11 +41,15 @@ #include "iredges_t.h" #include "execfreq.h" #include "error.h" +#include "raw_bitset.h" #include "../besched_t.h" #include "../benode_t.h" #include "../beabi.h" #include "../be_dbgout.h" +#include "../beemitter.h" +#include "../begnuas.h" +#include "../beirg_t.h" #include "ia32_emitter.h" #include "gen_ia32_emitter.h" @@ -35,92 +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; - -/** by default, we generate assembler code for the Linux gas */ -asm_flavour_t asm_flavour = ASM_LINUX_GAS; - -/** - * Switch to a new section - */ -void ia32_switch_section(FILE *F, section_t sec) { - static section_t curr_sec = NO_SECTION; - static const char *text[ASM_MAX][SECTION_MAX] = { - { - ".section\t.text", - ".section\t.data", - ".section\t.rodata", - ".section\t.bss", - ".section\t.tbss,\"awT\",@nobits", - ".section\t.ctors,\"aw\",@progbits" - }, - { - ".section\t.text", - ".section\t.data", - ".section .rdata,\"dr\"", - ".section\t.bss", - ".section\t.tbss,\"awT\",@nobits", - ".section\t.ctors,\"aw\",@progbits" - } - }; - - if (curr_sec == sec) - return; - - curr_sec = sec; - switch (sec) { - - case NO_SECTION: - break; - - case SECTION_TEXT: - case SECTION_DATA: - case SECTION_RODATA: - case SECTION_COMMON: - case SECTION_TLS: - case SECTION_CTOR: - fprintf(F, "\t%s\n", text[asm_flavour][sec]); - break; - - default: - break; - } -} - -static void ia32_dump_function_object(FILE *F, const char *name) -{ - switch (asm_flavour) { - case ASM_LINUX_GAS: - fprintf(F, "\t.type\t%s, @function\n", name); - break; - case ASM_MINGW_GAS: - fprintf(F, "\t.def\t%s;\t.scl\t2;\t.type\t32;\t.endef\n", name); - break; - default: - break; - } -} - -static void ia32_dump_function_size(FILE *F, const char *name) -{ - switch (asm_flavour) { - case ASM_LINUX_GAS: - fprintf(F, "\t.size\t%s, .-%s\n", name, name); - break; - default: - break; - } -} - /** * 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; @@ -136,24 +88,18 @@ static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { /* in case of a joker register: just return a valid register */ if (arch_register_type_is(reg, joker)) { - arch_register_req_t req; - const arch_register_req_t *p_req; + const arch_register_req_t *req; /* ask for the requirements */ - p_req = arch_get_register_req(arch_env, &req, irn, pos); + req = arch_get_register_req(arch_env, irn, pos); - if (arch_register_req_is(p_req, limited)) { + if (arch_register_req_is(req, limited)) { /* in case of limited requirements: get the first allowed register */ - - bitset_t *bs = bitset_alloca(arch_register_class_n_regs(p_req->cls)); - int idx; - - p_req->limited(p_req->limited_env, bs); - idx = bitset_next_set(bs, 0); - reg = arch_register_for_index(p_req->cls, idx); + unsigned idx = rbitset_next(req->limited, 0, 1); + reg = arch_register_for_index(req->cls, idx); } else { /* otherwise get first register in class */ - reg = arch_register_for_index(p_req->cls, 0); + reg = arch_register_for_index(req->cls, 0); } } @@ -163,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 */ @@ -193,20 +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]; - int len = tarval_snprintf(buf, sizeof(buf), tv); - assert(len); - 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: @@ -214,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'; @@ -232,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) || @@ -242,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); @@ -253,37 +197,11 @@ static const char *ia32_get_reg_name_for_mode(ia32_emit_env_t *env, ir_mode *mod } } -#if 0 -/** - * Determines the SSE suffix depending on the mode. - */ -static int ia32_print_mode_suffix(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) -{ - ir_node *irn = arg->v_ptr; - ir_mode *mode = get_ia32_ls_mode(irn); - - if (mode_is_float(mode)) { - return lc_appendable_chadd(app, get_mode_size_bits(mode) == 32 ? 's' : 'd'); - } else { - if(get_mode_size_bits(mode) == 32) - return 0; - - if(mode_is_signed(mode)) - lc_appendable_chadd(app, 's'); - else - lc_appendable_chadd(app, 'z'); - - lc_appendable_chadd(app, get_mode_suffix(mode)); - return 2; - } -} -#endif - /** * 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; @@ -300,79 +218,124 @@ static char *get_unique_label(char *buf, size_t buflen, const char *prefix) { * |_| |_| *************************************************************/ -void ia32_emit_ident(ia32_emit_env_t *env, ident *id) -{ - size_t len = get_id_strlen(id); - const char* str = get_id_str(id); - - ia32_emit_string_len(env, str, len); -} - -void ia32_emit_irprintf(ia32_emit_env_t *env, const char *fmt, ...) -{ - char buf[128]; - va_list ap; - - va_start(ap, fmt); - ir_vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - ia32_emit_string(env, buf); -} +// we have no C++ and can't define an implicit ia32_emit_env_t* cast to +// be_emit_env_t* so we cheat a bit... +#define be_emit_char(env,c) be_emit_char(env->emit,c) +#define be_emit_string(env,s) be_emit_string(env->emit,s) +#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)); - ia32_emit_char(env, '%'); - ia32_emit_string(env, reg_name); + be_emit_char(env, '%'); + be_emit_string(env, reg_name); } 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); - ia32_emit_char(env, '%'); - ia32_emit_string(env, reg_name); + be_emit_char(env, '%'); + be_emit_string(env, reg_name); } 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 < 7); - ia32_emit_char(env, '%'); - ia32_emit_string(env, attr->x87[pos]->name); + assert(pos < 3); + be_emit_char(env, '%'); + be_emit_string(env, attr->x87[pos]->name); } 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); + 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; + } + + assert(0); + be_emit_string(env, "BAD"); + return; +} + +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_mode(env, mode); +} + +static +char get_xmm_mode_suffix(ir_mode *mode) +{ + assert(mode_is_float(mode)); + switch(get_mode_size_bits(mode)) { + case 32: + return 's'; + case 64: + return 'd'; default: assert(0); - ia32_emit_string(env, "BAD"); - return; } + return '%'; +} - ia32_emit_ident(env, id); +void ia32_emit_xmm_mode_suffix(ia32_emit_env_t *env, const ir_node *node) +{ + ir_mode *mode = get_ia32_ls_mode(node); + assert(mode != NULL); + be_emit_char(env, 's'); + be_emit_char(env, get_xmm_mode_suffix(mode)); } -void ia32_emit_mode_suffix(ia32_emit_env_t *env, const ir_mode *mode) +void ia32_emit_xmm_mode_suffix_s(ia32_emit_env_t *env, const ir_node *node) { - ia32_emit_char(env, get_mode_suffix(mode)); + ir_mode *mode = get_ia32_ls_mode(node); + assert(mode != NULL); + be_emit_char(env, get_xmm_mode_suffix(mode)); } void ia32_emit_extend_suffix(ia32_emit_env_t *env, const ir_mode *mode) @@ -380,87 +343,139 @@ void ia32_emit_extend_suffix(ia32_emit_env_t *env, const ir_mode *mode) if(get_mode_size_bits(mode) == 32) return; if(mode_is_signed(mode)) { - ia32_emit_char(env, 's'); + be_emit_char(env, 's'); } else { - ia32_emit_char(env, 'z'); + be_emit_char(env, 'z'); } } +static +void ia32_emit_function_object(ia32_emit_env_t *env, const char *name) +{ + switch (be_gas_flavour) { + case GAS_FLAVOUR_NORMAL: + be_emit_cstring(env, "\t.type\t"); + be_emit_string(env, name); + be_emit_cstring(env, ", @function\n"); + be_emit_write_line(env); + break; + case GAS_FLAVOUR_MINGW: + be_emit_cstring(env, "\t.def\t"); + be_emit_string(env, name); + be_emit_cstring(env, ";\t.scl\t2;\t.type\t32;\t.endef\n"); + be_emit_write_line(env); + break; + default: + break; + } +} + +static +void ia32_emit_function_size(ia32_emit_env_t *env, const char *name) +{ + switch (be_gas_flavour) { + case GAS_FLAVOUR_NORMAL: + be_emit_cstring(env, "\t.size\t"); + be_emit_string(env, name); + be_emit_cstring(env, ", .-"); + be_emit_string(env, name); + be_emit_char(env, '\n'); + be_emit_write_line(env); + break; + default: + break; + } +} + + +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)) { - ia32_emit_char(env, '$'); - ia32_emit_immediate(env, node); - ia32_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); + int right_pos; + const ir_node *right_op; - if (is_ia32_emit_cl(node)) { - assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in) && "shift operation needs ecx"); - in_name = "cl"; - } - - ia32_emit_char(env, '%'); - ia32_emit_string(env, in_name); - ia32_emit_cstring(env, ", %"); - ia32_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); - ia32_emit_cstring(env, ", $"); - ia32_emit_immediate(env, node); - } else if (produces_result(node)) { - ia32_emit_am(env, node); - ia32_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } else { - ia32_emit_am(env, node); - ia32_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)) { - ia32_emit_char(env, '$'); - ia32_emit_immediate(env, node); - ia32_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); - ia32_emit_char(env, '%'); - ia32_emit_string(env, in_name); - ia32_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"); } } @@ -474,19 +489,19 @@ 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; out = out ? out : in1; - ia32_emit_char(env, '%'); - ia32_emit_string(env, arch_register_get_name(in)); - ia32_emit_cstring(env, ", %"); - ia32_emit_string(env, arch_register_get_name(out)); + be_emit_char(env, '%'); + be_emit_string(env, arch_register_get_name(in)); + be_emit_cstring(env, ", %"); + be_emit_string(env, arch_register_get_name(out)); } break; case ia32_AddrModeS: @@ -498,36 +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)) { - ia32_emit_char(env, '$'); - ia32_emit_immediate(env, node); - } else { - if (is_ia32_IMul(node) || is_ia32_Mulh(node)) { - /* MulS and Mulh implicitly multiply by EAX */ - ia32_emit_source_register(env, node, 3); - } else if(is_ia32_IDiv(node)) { - ia32_emit_source_register(env, node, 1); - } 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"); } } @@ -535,108 +553,66 @@ 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)) - ia32_emit_char(env, '-'); - ia32_emit_ident(env, id); + 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) { - ia32_emit_irprintf(env, "%+d", offs); + if(ent != NULL) { + be_emit_irprintf(env->emit, "%+d", offs); } else { - ia32_emit_irprintf(env, "%d", offs); + be_emit_irprintf(env->emit, "%d", offs); } } - if (am_flav & (ia32_B | ia32_I)) { - ia32_emit_char(env, '('); + 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) { - ia32_emit_char(env, ','); + if (has_index) { + int scale; + be_emit_char(env, ','); ia32_emit_source_register(env, node, 1); - if (am_flav & ia32_S) { - ia32_emit_irprintf(env, ",%d", 1 << get_ia32_am_scale(node)); + scale = get_ia32_am_scale(node); + if (scale > 0) { + be_emit_irprintf(env->emit, ",%d", 1 << get_ia32_am_scale(node)); } } - ia32_emit_char(env, ')'); + be_emit_char(env, ')'); } } -#if 0 -/** - * Formated print of commands and comments. - */ -static void ia32_fprintf_format(FILE *F, const ir_node *irn, char *cmd_buf, char *cmnt_buf) { - unsigned lineno; - const char *name = irn ? be_retrieve_dbg_info(get_irn_dbg_info((ir_node *)irn), &lineno) : NULL; - - if (name) - fprintf(F, "\t%-35s %-60s /* %s:%u */\n", cmd_buf, cmnt_buf, name, lineno); - else - fprintf(F, "\t%-35s %-60s\n", cmd_buf, cmnt_buf); -} -#endif - -void ia32_write_line(ia32_emit_env_t *env) -{ - char *finished_line = obstack_finish(env->obst); - - fwrite(finished_line, env->linelength, 1, env->out); - env->linelength = 0; - obstack_free(env->obst, finished_line); -} - -void ia32_pad_comment(ia32_emit_env_t *env) -{ - while(env->linelength <= 30) { - ia32_emit_char(env, ' '); - } - ia32_emit_cstring(env, " "); -} - -void ia32_emit_finish_line(ia32_emit_env_t *env, const ir_node *node) -{ - dbg_info *dbg; - const char *sourcefile; - unsigned lineno; - - if(node == NULL) { - ia32_emit_char(env, '\n'); - ia32_write_line(env); - return; - } - - ia32_pad_comment(env); - ia32_emit_cstring(env, "/* "); - ia32_emit_irprintf(env, "%+F ", node); - - dbg = get_irn_dbg_info(node); - sourcefile = be_retrieve_dbg_info(dbg, &lineno); - if(sourcefile != NULL) { - ia32_emit_string(env, sourcefile); - ia32_emit_irprintf(env, ":%u", lineno); - } - ia32_emit_cstring(env, " */\n"); - ia32_write_line(env); -} - - /************************************************* * _ _ _ * (_) | | | @@ -661,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 }, /* < */ @@ -683,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 }, /* < */ @@ -697,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)); @@ -711,25 +690,36 @@ static const char *get_cmp_suffix(int cmp_code) void ia32_emit_cmp_suffix(ia32_emit_env_t *env, long pnc) { - ia32_emit_string(env, get_cmp_suffix(pnc)); + be_emit_string(env, get_cmp_suffix(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); - ia32_emit_cstring(env, BLOCK_PREFIX); - ia32_emit_irprintf(env, "%d", get_irn_node_nr(block)); + ia32_emit_block_name(env, block); } /** Return the next block in Block schedule */ @@ -740,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; @@ -762,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; @@ -795,37 +787,38 @@ static void finish_CondJmp(ia32_emit_env_t *env, const ir_node *node, /* in case of unordered compare, check for parity */ if (pnc & pn_Cmp_Uo) { - ia32_emit_cstring(env, "\tjp "); + be_emit_cstring(env, "\tjp "); ia32_emit_cfop_target(env, proj_true); - ia32_emit_finish_line(env, proj_true); + be_emit_finish_line_gas(env, proj_true); } - ia32_emit_cstring(env, "\tj"); + be_emit_cstring(env, "\tj"); ia32_emit_cmp_suffix(env, pnc); - ia32_emit_char(env, ' '); + be_emit_char(env, ' '); ia32_emit_cfop_target(env, proj_true); - ia32_emit_finish_line(env, proj_true); + be_emit_finish_line_gas(env, proj_true); /* the second Proj might be a fallthrough */ if (get_cfop_target_block(proj_false) != next_block) { - ia32_emit_cstring(env, "\tjmp "); + be_emit_cstring(env, "\tjmp "); ia32_emit_cfop_target(env, proj_false); - ia32_emit_finish_line(env, proj_false); + be_emit_finish_line_gas(env, proj_false); } else { - ia32_emit_cstring(env, "\t/* fallthrough to"); + be_emit_cstring(env, "\t/* fallthrough to "); ia32_emit_cfop_target(env, proj_false); - ia32_emit_cstring(env, " */"); - ia32_emit_finish_line(env, proj_false); + be_emit_cstring(env, " */"); + be_emit_finish_line_gas(env, proj_false); } } /** * Emits code for conditional jump. */ -static void CondJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { - ia32_emit_cstring(env, "\tcmp "); +static +void CondJmp_emitter(ia32_emit_env_t *env, const ir_node *node) { + be_emit_cstring(env, "\tcmp "); ia32_emit_binop(env, node); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); finish_CondJmp(env, node, mode_Iu, get_ia32_pncode(node)); } @@ -833,47 +826,43 @@ 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)) { - ia32_emit_cstring(env, "\ttest $"); - ia32_emit_immediate(env, node); - ia32_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - ia32_emit_finish_line(env, node); - } else { - ia32_emit_cstring(env, "\ttest "); - ia32_emit_source_register(env, node, 1); - ia32_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - ia32_emit_finish_line(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) { - ia32_emit_cstring(env, "/* omitted redundant test */"); - ia32_emit_finish_line(env, 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) { - ia32_emit_cstring(env, "/* omitted redundant test/cmp */"); - ia32_emit_finish_line(env, 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); finish_CondJmp(env, node, mode_Is, get_ia32_pncode(node)); } @@ -881,12 +870,13 @@ 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) { - ia32_emit_cstring(env, "\tucomis"); - ia32_emit_mode_suffix(env, get_irn_mode(node)); - ia32_emit_char(env, ' '); +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, ' '); ia32_emit_binop(env, node); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); finish_CondJmp(env, node, mode_F, get_ia32_pncode(node)); } @@ -894,139 +884,153 @@ 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 = x87_attr->x87[0]->name; case iro_ia32_fcomJmp: default: - ia32_emit_cstring(env, "\tfucom "); + be_emit_cstring(env, "\tfucom "); break; case iro_ia32_fcomrpJmp: pnc = get_inversed_pnc(pnc); + reg = x87_attr->x87[0]->name; case iro_ia32_fcompJmp: - ia32_emit_cstring(env, "\tfucomp "); + be_emit_cstring(env, "\tfucomp "); break; case iro_ia32_fcomrppJmp: pnc = get_inversed_pnc(pnc); case iro_ia32_fcomppJmp: - ia32_emit_cstring(env, "\tfucompp "); + be_emit_cstring(env, "\tfucompp "); reg = ""; break; } if(reg[0] != '\0') { - ia32_emit_char(env, '%'); - ia32_emit_string(env, reg); + be_emit_char(env, '%'); + be_emit_string(env, reg); + } + be_emit_finish_line_gas(env, node); + + be_emit_cstring(env, "\tfnstsw %ax"); + be_emit_finish_line_gas(env, node); + be_emit_cstring(env, "\tsahf"); + be_emit_finish_line_gas(env, node); + + finish_CondJmp(env, node, mode_E, pnc); +} + +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); } - ia32_emit_finish_line(env, node); +} - ia32_emit_cstring(env, "\tfnstsw %ax"); - ia32_emit_finish_line(env, node); - ia32_emit_cstring(env, "\tsahf"); - ia32_emit_finish_line(env, node); +static +int is_ia32_Immediate_0(const ir_node *node) +{ + const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node); - finish_CondJmp(env, node, mode_D, pnc); + return attr->offset == 0 && attr->symconst == NULL; } -static void CMov_emitter(ia32_emit_env_t *env, const ir_node *node) { +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)) { - ia32_emit_cstring(env, "\tcmp "); - ia32_emit_source_register(env, node, 1); - ia32_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)) { - ia32_emit_cstring(env, "\tucomis"); - ia32_emit_mode_suffix(env, get_irn_mode(node)); - ia32_emit_char(env, ' '); + be_emit_cstring(env, "\tucomis"); + ia32_emit_mode_suffix_mode(env, get_irn_mode(node)); + be_emit_char(env, ' '); ia32_emit_source_register(env, node, 1); - ia32_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - } else if (is_PsiCondCMov) { - /* omit compare because flags are already set by And/Or */ - ia32_emit_cstring(env, "\ttest "); - ia32_emit_source_register(env, node, 0); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_source_register(env, node, 0); } else { assert(0 && "unsupported CMov"); } - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); if (REGS_ARE_EQUAL(out, in2)) { /* best case: default in == out -> do nothing */ } 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) { - ia32_emit_cstring(env, "\tmovl "); - ia32_emit_dest_register(env, node, 2); - ia32_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } else { - ia32_emit_cstring(env, "\tmovl "); - ia32_emit_source_register(env, node, 3); - ia32_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } - ia32_emit_finish_line(env, node); - } - - if (is_PsiCondCMov) { - ia32_emit_cstring(env, "\tcmov"); - ia32_emit_cmp_suffix(env, pnc); - ia32_emit_cstring(env, "l "); - ia32_emit_source_register(env, node, 1); - ia32_emit_cstring(env, ", "); - ia32_emit_dest_register(env, node, 0); - } else { - ia32_emit_cstring(env, "\tcmov"); - ia32_emit_cmp_suffix(env, pnc); - ia32_emit_cstring(env, "l "); - ia32_emit_source_register(env, node, 2); - ia32_emit_cstring(env, ", "); + 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); } - ia32_emit_finish_line(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; @@ -1034,46 +1038,54 @@ 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)) { - ia32_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)) { - ia32_emit_cstring(env, "\tucomis"); - ia32_emit_mode_suffix(env, get_irn_mode(get_irn_n(node, 2))); - ia32_emit_char(env, ' '); + be_emit_cstring(env, "\tucomis"); + 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)) { - ia32_emit_cstring(env, "\tcmp $0, "); - ia32_emit_source_register(env, node, 0); } else { assert(0 && "unsupported Set"); } - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); /* use mov to clear target because it doesn't affect the eflags */ - ia32_emit_cstring(env, "\tmovl $0, %"); - ia32_emit_string(env, arch_register_get_name(out)); - ia32_emit_finish_line(env, node); + be_emit_cstring(env, "\tmovl $0, %"); + be_emit_string(env, arch_register_get_name(out)); + be_emit_finish_line_gas(env, node); - ia32_emit_cstring(env, "\tset"); + be_emit_cstring(env, "\tset"); ia32_emit_cmp_suffix(env, pnc); - ia32_emit_cstring(env, " %"); - ia32_emit_string(env, reg8bit); - ia32_emit_finish_line(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))); + be_emit_cstring(env, " %"); + be_emit_string(env, reg8bit); + be_emit_finish_line_gas(env, node); } -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; @@ -1126,31 +1138,31 @@ static void emit_ia32_xCmp(ia32_emit_env_t *env, const ir_node *node) { - and result and stored result - cleanup stack */ - ia32_emit_cstring(env, "\tsubl $8, %esp"); - ia32_emit_finish_line(env, node); + be_emit_cstring(env, "\tsubl $8, %esp"); + be_emit_finish_line_gas(env, node); - ia32_emit_cstring(env, "\tcmpsd $3, "); + be_emit_cstring(env, "\tcmpsd $3, "); ia32_emit_binop(env, node); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); - ia32_emit_cstring(env, "\tmovsd "); + be_emit_cstring(env, "\tmovsd "); ia32_emit_dest_register(env, node, 0); - ia32_emit_cstring(env, ", (%esp)"); - ia32_emit_finish_line(env, node); + be_emit_cstring(env, ", (%esp)"); + be_emit_finish_line_gas(env, node); } - ia32_emit_cstring(env, "\tcmpsd "); - ia32_emit_irprintf(env, "%d, ", sse_pnc); + be_emit_cstring(env, "\tcmpsd "); + be_emit_irprintf(env->emit, "%d, ", sse_pnc); ia32_emit_binop(env, node); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); if (unord && sse_pnc != 3) { - ia32_emit_cstring(env, "\tandpd (%esp), "); + be_emit_cstring(env, "\tandpd (%esp), "); ia32_emit_dest_register(env, node, 0); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); - ia32_emit_cstring(env, "\taddl $8, %esp"); - ia32_emit_finish_line(env, node); + be_emit_cstring(env, "\taddl $8, %esp"); + be_emit_finish_line_gas(env, node); } } @@ -1174,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; @@ -1184,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; @@ -1199,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; @@ -1247,53 +1261,53 @@ static void emit_ia32_SwitchJmp(ia32_emit_env_t *env, const ir_node *node) { interval = tbl.max_value - tbl.min_value; /* emit the table */ - ia32_emit_cstring(env, "\tcmpl $"); - ia32_emit_irprintf(env, "%u, ", interval); + be_emit_cstring(env, "\tcmpl $"); + be_emit_irprintf(env->emit, "%u, ", interval); ia32_emit_source_register(env, node, 0); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); - ia32_emit_cstring(env, "\tja "); + be_emit_cstring(env, "\tja "); ia32_emit_cfop_target(env, tbl.defProj); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); if (tbl.num_branches > 1) { /* create table */ - ia32_emit_cstring(env, "\tjmp *"); - ia32_emit_string(env, tbl.label); - ia32_emit_cstring(env, "(,"); + be_emit_cstring(env, "\tjmp *"); + be_emit_string(env, tbl.label); + be_emit_cstring(env, "(,"); ia32_emit_source_register(env, node, 0); - ia32_emit_cstring(env, ",4)"); - ia32_emit_finish_line(env, node); + be_emit_cstring(env, ",4)"); + be_emit_finish_line_gas(env, node); - ia32_switch_section(env->out, SECTION_RODATA); - ia32_emit_cstring(env, "\t.align 4\n"); - ia32_write_line(env); + be_gas_emit_switch_section(env->emit, GAS_SECTION_RODATA); + be_emit_cstring(env, "\t.align 4\n"); + be_emit_write_line(env); - ia32_emit_string(env, tbl.label); - ia32_emit_cstring(env, ":\n"); - ia32_write_line(env); + be_emit_string(env, tbl.label); + be_emit_cstring(env, ":\n"); + be_emit_write_line(env); - ia32_emit_cstring(env, ".long "); + be_emit_cstring(env, ".long "); ia32_emit_cfop_target(env, tbl.branches[0].target); - ia32_emit_finish_line(env, NULL); + be_emit_finish_line_gas(env, NULL); last_value = tbl.branches[0].value; for (i = 1; i < tbl.num_branches; ++i) { while (++last_value < tbl.branches[i].value) { - ia32_emit_cstring(env, ".long "); + be_emit_cstring(env, ".long "); ia32_emit_cfop_target(env, tbl.defProj); - ia32_emit_finish_line(env, NULL); + be_emit_finish_line_gas(env, NULL); } - ia32_emit_cstring(env, ".long "); + be_emit_cstring(env, ".long "); ia32_emit_cfop_target(env, tbl.branches[i].target); - ia32_emit_finish_line(env, NULL); + be_emit_finish_line_gas(env, NULL); } - ia32_switch_section(env->out, SECTION_TEXT); + be_gas_emit_switch_section(env->emit, GAS_SECTION_TEXT); } else { /* one jump is enough */ - ia32_emit_cstring(env, "\tjmp "); + be_emit_cstring(env, "\tjmp "); ia32_emit_cfop_target(env, tbl.branches[0].target); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); } if (tbl.label) @@ -1305,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 */ @@ -1314,14 +1329,178 @@ static void emit_Jmp(ia32_emit_env_t *env, const ir_node *node) { /* we have a block schedule */ next_block = next_blk_sched(block); if (get_cfop_target_block(node) != next_block) { - ia32_emit_cstring(env, "\tjmp "); + be_emit_cstring(env, "\tjmp "); ia32_emit_cfop_target(env, node); } else { - ia32_emit_cstring(env, "\t/* fallthrough to "); + be_emit_cstring(env, "\t/* fallthrough to "); ia32_emit_cfop_target(env, node); - ia32_emit_cstring(env, " */"); + be_emit_cstring(env, " */"); + } + 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; } - ia32_emit_finish_line(env, node); + + be_emit_char(env, '\n'); + be_emit_write_line(env); + + be_emit_cstring(env, "# End ASM\n"); + be_emit_write_line(env); } /********************************** @@ -1338,24 +1517,25 @@ 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) { - ia32_emit_cstring(env, "\tcld"); - ia32_emit_finish_line(env, NULL); +static +void emit_CopyB_prolog(ia32_emit_env_t *env, int rem) { + be_emit_cstring(env, "\tcld"); + be_emit_finish_line_gas(env, NULL); switch(rem) { case 1: - ia32_emit_cstring(env, "\tmovsb"); - ia32_emit_finish_line(env, NULL); + be_emit_cstring(env, "\tmovsb"); + be_emit_finish_line_gas(env, NULL); break; case 2: - ia32_emit_cstring(env, "\tmovsw"); - ia32_emit_finish_line(env, NULL); + be_emit_cstring(env, "\tmovsw"); + be_emit_finish_line_gas(env, NULL); break; case 3: - ia32_emit_cstring(env, "\tmovsb"); - ia32_emit_finish_line(env, NULL); - ia32_emit_cstring(env, "\tmovsw"); - ia32_emit_finish_line(env, NULL); + be_emit_cstring(env, "\tmovsb"); + be_emit_finish_line_gas(env, NULL); + be_emit_cstring(env, "\tmovsw"); + be_emit_finish_line_gas(env, NULL); break; } } @@ -1363,20 +1543,22 @@ 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); emit_CopyB_prolog(env, rem); - ia32_emit_cstring(env, "\trep movsd"); - ia32_emit_finish_line(env, node); + be_emit_cstring(env, "\trep movsd"); + be_emit_finish_line_gas(env, 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); @@ -1384,8 +1566,8 @@ static void emit_ia32_CopyB_i(ia32_emit_env_t *env, const ir_node *node) { size >>= 2; while (size--) { - ia32_emit_cstring(env, "\tmovsd"); - ia32_emit_finish_line(env, NULL); + be_emit_cstring(env, "\tmovsd"); + be_emit_finish_line_gas(env, NULL); } } @@ -1404,66 +1586,72 @@ 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); - ia32_emit_cstring(env, "\tcvt"); + be_emit_cstring(env, "\tcvt"); if(is_ia32_Conv_I2FP(node)) { if(ls_bits == 32) { - ia32_emit_cstring(env, "si2ss"); + be_emit_cstring(env, "si2ss"); } else { - ia32_emit_cstring(env, "si2sd"); + be_emit_cstring(env, "si2sd"); } } else if(is_ia32_Conv_FP2I(node)) { if(ls_bits == 32) { - ia32_emit_cstring(env, "ss2si"); + be_emit_cstring(env, "ss2si"); } else { - ia32_emit_cstring(env, "sd2si"); + be_emit_cstring(env, "sd2si"); } } else { assert(is_ia32_Conv_FP2FP(node)); if(ls_bits == 32) { - ia32_emit_cstring(env, "sd2ss"); + be_emit_cstring(env, "sd2ss"); } else { - ia32_emit_cstring(env, "ss2sd"); + be_emit_cstring(env, "ss2sd"); } } + be_emit_char(env, ' '); switch(get_ia32_op_type(node)) { case ia32_Normal: - ia32_emit_dest_register(env, node, 0); - ia32_emit_cstring(env, ", "); ia32_emit_source_register(env, node, 2); + be_emit_cstring(env, ", "); + ia32_emit_dest_register(env, node, 0); break; case ia32_AddrModeS: ia32_emit_dest_register(env, node, 0); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_am(env, node); break; default: assert(0 && "unsupported op type for Conv"); } - ia32_emit_finish_line(env, 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); @@ -1484,55 +1672,43 @@ 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) { - ia32_emit_cstring(env, "\tcbtw"); - } else if (smaller_bits == 16) { - ia32_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; - ia32_emit_cstring(env, "\tandl $0x"); - ia32_emit_irprintf(env, "%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); - ia32_emit_cstring(env, "\tmov"); - ia32_emit_string(env, sign_suffix); - ia32_emit_mode_suffix(env, smaller_mode); - ia32_emit_cstring(env, "l %"); - ia32_emit_string(env, sreg); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, "\tmov"); + be_emit_string(env, sign_suffix); + ia32_emit_mode_suffix_mode(env, smaller_mode); + be_emit_cstring(env, "l %"); + be_emit_string(env, sreg); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } break; case ia32_AddrModeS: { - ia32_emit_cstring(env, "\tmov"); - ia32_emit_string(env, sign_suffix); - ia32_emit_mode_suffix(env, smaller_mode); - ia32_emit_cstring(env, "l %"); + be_emit_cstring(env, "\tmov"); + be_emit_string(env, sign_suffix); + ia32_emit_mode_suffix_mode(env, smaller_mode); + be_emit_cstring(env, "l %"); ia32_emit_am(env, node); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); break; } default: assert(0 && "unsupported op type for Conv"); } - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); } /** @@ -1556,87 +1732,98 @@ 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); - ia32_emit_cstring(env, "\tcall "); + be_emit_cstring(env, "\tcall "); if (ent) { - ia32_emit_string(env, get_entity_ld_name(ent)); + set_entity_backend_marked(ent, 1); + be_emit_string(env, get_entity_ld_name(ent)); } else { - ia32_emit_char(env, '*'); + be_emit_char(env, '*'); ia32_emit_dest_register(env, get_irn_n(node, be_pos_Call_ptr), 0); } - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, 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) return; if (offs > 0) { - ia32_emit_cstring(env, "\tsubl $"); - ia32_emit_irprintf(env, "%u, ", offs); + be_emit_cstring(env, "\tsubl $"); + be_emit_irprintf(env->emit, "%u, ", offs); ia32_emit_source_register(env, node, 0); } else { - ia32_emit_cstring(env, "\taddl $"); - ia32_emit_irprintf(env, "%u, ", -offs); + be_emit_cstring(env, "\taddl $"); + be_emit_irprintf(env->emit, "%u, ", -offs); ia32_emit_source_register(env, node, 0); } - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); } /** * Emits code to set stack pointer. */ -static void emit_be_SetSP(ia32_emit_env_t *env, const ir_node *node) { - ia32_emit_cstring(env, "\tmovl "); +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); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, 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))) { - ia32_emit_cstring(env, "\tmovsd "); + mode = get_irn_mode(node); + if (mode == mode_E) { + be_emit_cstring(env, "\tmovsd "); ia32_emit_source_register(env, node, 0); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } else { - ia32_emit_cstring(env, "\tmovl "); + be_emit_cstring(env, "\tmovl "); ia32_emit_source_register(env, node, 0); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } - ia32_emit_finish_line(env, node); + 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; @@ -1649,44 +1836,29 @@ 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 - ia32_emit_cstring(env, "\txchg "); - ia32_emit_source_register(env, node, 1); - ia32_emit_cstring(env, ", "); - ia32_emit_source_register(env, node, 0); - ia32_emit_finish_line(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]) { - ia32_emit_cstring(env, "\tpxorq "); + be_emit_cstring(env, "\txorpd "); ia32_emit_source_register(env, node, 1); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_source_register(env, node, 0); - ia32_emit_finish_line(env, NULL); + be_emit_finish_line_gas(env, NULL); - ia32_emit_cstring(env, "\tpxorq "); + be_emit_cstring(env, "\txorpd "); ia32_emit_source_register(env, node, 0); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_source_register(env, node, 1); - ia32_emit_finish_line(env, NULL); + be_emit_finish_line_gas(env, NULL); - ia32_emit_cstring(env, "\tpxorq "); + be_emit_cstring(env, "\txorpd "); ia32_emit_source_register(env, node, 1); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_source_register(env, node, 0); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); } else if (cls1 == &ia32_reg_classes[CLASS_ia32_vfp]) { /* is a NOP */ } else if (cls1 == &ia32_reg_classes[CLASS_ia32_st]) { @@ -1697,54 +1869,61 @@ 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) { - ir_mode *mode = get_irn_mode(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) { - ia32_emit_cstring(env, "\tmovl $"); + be_emit_cstring(env, "\tmovl "); ia32_emit_immediate(env, node); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } else { tarval *tv = get_ia32_Immop_tarval(node); - assert(mode == mode_Iu); + assert(get_irn_mode(node) == mode_Iu); /* beware: in some rare cases mode is mode_b which has no tarval_null() */ if (tarval_is_null(tv)) { if (env->isa->opt_arch == arch_pentium_4) { /* P4 prefers sub r, r, others xor r, r */ - ia32_emit_cstring(env, "\tsubl "); + be_emit_cstring(env, "\tsubl "); } else { - ia32_emit_cstring(env, "\txorl "); + be_emit_cstring(env, "\txorl "); } ia32_emit_dest_register(env, node, 0); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } else { - ia32_emit_cstring(env, "\tmovl $"); + be_emit_cstring(env, "\tmovl "); ia32_emit_immediate(env, node); - ia32_emit_cstring(env, ", "); + be_emit_cstring(env, ", "); ia32_emit_dest_register(env, node, 0); } } - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); } /** * Emits code to load the TLS base */ -static void emit_ia32_LdTls(ia32_emit_env_t *env, const ir_node *node) { - ia32_emit_cstring(env, "\tmovl %gs:0, "); +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); - ia32_emit_finish_line(env, node); + be_emit_finish_line_gas(env, node); } -static void emit_be_Return(ia32_emit_env_t *env, const ir_node *node) { - ia32_emit_cstring(env, "\tret"); - ia32_emit_finish_line(env, 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; } @@ -1762,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) @@ -1778,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); @@ -1840,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); @@ -1856,12 +2036,13 @@ static void ia32_emit_dbg(ia32_emit_env_t *env, const ir_node *node) { } if (last_line != lineno) { char name[64]; - FILE *F = env->out; snprintf(name, sizeof(name), ".LM%u", ++num); last_line = lineno; be_dbg_line(env->cg->birg->main_env->db_handle, lineno, name); - fprintf(F, "%s:\n", name); + be_emit_string(env, name); + be_emit_cstring(env, ":\n"); + be_emit_write_line(env); } } } @@ -1871,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; @@ -1890,16 +2071,18 @@ 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) { - ia32_emit_cstring(env, "\t.p2align "); - ia32_emit_irprintf(env, "%u,,%u\n", align, skip); - ia32_write_line(env); +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); } /** * 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; @@ -1923,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) { @@ -1943,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; @@ -1955,104 +2149,105 @@ 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, ':'); - ia32_emit_cstring(env, BLOCK_PREFIX); - ia32_emit_irprintf(env, "%d:", get_irn_node_nr(block)); - ia32_pad_comment(env); - ia32_emit_cstring(env, "\t/* preds:"); + be_emit_pad_comment(env); + be_emit_cstring(env, " /* preds:"); /* emit list of pred blocks in comment */ arity = get_irn_arity(block); for (i = 0; i < arity; ++i) { ir_node *predblock = get_Block_cfgpred_block(block, i); - ia32_emit_irprintf(env, " %d", get_irn_node_nr(predblock)); - } - - if (exec_freq != NULL) { - ia32_emit_irprintf(env, " freq: %f", get_block_execfreq(exec_freq, block)); + be_emit_irprintf(env->emit, " %d", get_irn_node_nr(predblock)); } - ia32_emit_cstring(env, " */\n"); - ia32_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); @@ -2064,44 +2259,53 @@ 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) { - FILE *F = env->out; +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; const be_irg_t *birg = env->cg->birg; - fprintf(F, "\n"); - ia32_switch_section(F, SECTION_TEXT); + be_emit_write_line(env); + be_gas_emit_switch_section(env->emit, GAS_SECTION_TEXT); be_dbg_method_begin(birg->main_env->db_handle, irg_ent, be_abi_get_stack_layout(birg->abi)); ia32_emit_align_func(env, cpu); if (get_entity_visibility(irg_ent) == visibility_external_visible) { - fprintf(F, ".globl %s\n", irg_name); + be_emit_cstring(env, ".global "); + be_emit_string(env, irg_name); + be_emit_char(env, '\n'); + be_emit_write_line(env); } - ia32_dump_function_object(F, irg_name); - fprintf(F, "%s:\n", irg_name); + ia32_emit_function_object(env, irg_name); + be_emit_string(env, irg_name); + be_emit_cstring(env, ":\n"); + be_emit_write_line(env); } /** * 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; - FILE *F = env->out; - ia32_dump_function_size(F, irg_name); + ia32_emit_function_size(env, irg_name); be_dbg_method_end(birg->main_env->db_handle); - fprintf(F, "\n"); + be_emit_char(env, '\n'); + be_emit_write_line(env); } /** * 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); @@ -2109,28 +2313,29 @@ 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. */ -void ia32_gen_routine(ia32_code_gen_t *cg, FILE *F, ir_graph *irg) { +void ia32_gen_routine(ia32_code_gen_t *cg, ir_graph *irg) { ia32_emit_env_t env; ir_node *block; ir_node *last_block = NULL; int i, n; - struct obstack obst; - - obstack_init(&obst); - env.out = F; + env.isa = (ia32_isa_t *)cg->arch_env->isa; + env.emit = &env.isa->emit; env.arch_env = cg->arch_env; env.cg = cg; - env.isa = (ia32_isa_t *)cg->arch_env->isa; - env.obst = &obst; - env.linelength = 0; - 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(); @@ -2153,3 +2358,8 @@ void ia32_gen_routine(ia32_code_gen_t *cg, FILE *F, ir_graph *irg) { ia32_emit_func_epilog(&env, irg); } + +void ia32_init_emitter(void) +{ + FIRM_DBG_REGISTER(dbg, "firm.be.ia32.emitter"); +}