X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;ds=sidebyside;f=ir%2Fbe%2Fia32%2Fia32_emitter.c;h=7cdb818406429683d81214d55fba989fffd63da1;hb=4e809225145abac9fd89758b6226012f3e8f7b1e;hp=6f9a4cee59bf03c3ea30fbe6b6cfd9afa56ea21c;hpb=c3738205be025c8520d4338dfe5f44de42199d66;p=libfirm diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index 6f9a4cee5..7cdb81840 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -42,6 +42,7 @@ #include "execfreq.h" #include "error.h" #include "raw_bitset.h" +#include "dbginfo.h" #include "../besched_t.h" #include "../benode_t.h" @@ -57,6 +58,7 @@ #include "ia32_nodes_attr.h" #include "ia32_new_nodes.h" #include "ia32_map_regs.h" +#include "ia32_architecture.h" #include "bearch_ia32_t.h" DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;) @@ -196,7 +198,7 @@ static void emit_register(const arch_register_t *reg, const ir_mode *mode) emit_16bit_register(reg); return; } else { - assert(size == 32); + assert(mode_is_float(mode) || size == 32); } } @@ -257,7 +259,8 @@ static void ia32_emit_mode_suffix_mode(const ir_mode *mode) switch(get_mode_size_bits(mode)) { case 32: be_emit_char('s'); return; case 64: be_emit_char('l'); return; - case 80: be_emit_char('t'); return; + case 80: + case 96: be_emit_char('t'); return; } } else { assert(mode_is_int(mode) || mode_is_reference(mode)); @@ -285,7 +288,9 @@ void ia32_emit_mode_suffix(const ir_node *node) void ia32_emit_x87_mode_suffix(const ir_node *node) { ir_mode *mode = get_ia32_ls_mode(node); - if(mode != NULL) + assert(mode != NULL); + /* we only need to emit the mode on address mode */ + if(get_ia32_op_type(node) != ia32_Normal) ia32_emit_mode_suffix_mode(mode); } @@ -490,6 +495,23 @@ void ia32_emit_unop(const ir_node *node, int pos) { } } +static void ia32_emit_entity(ir_entity *entity) +{ + ident *id; + + set_entity_backend_marked(entity, 1); + id = get_entity_ld_ident(entity); + be_emit_ident(id); + + if(get_entity_owner(entity) == get_tls_type()) { + if (get_entity_visibility(entity) == visibility_external_allocated) { + be_emit_cstring("@INDNTPOFF"); + } else { + be_emit_cstring("@NTPOFF"); + } + } +} + /** * Emits address mode. */ @@ -506,21 +528,9 @@ void ia32_emit_am(const ir_node *node) { /* emit offset */ 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('-'); - be_emit_ident(id); - - if(get_entity_owner(ent) == get_tls_type()) { - if (get_entity_visibility(ent) == visibility_external_allocated) { - be_emit_cstring("@INDNTPOFF"); - } else { - be_emit_cstring("@NTPOFF"); - } - } + ia32_emit_entity(ent); } if(offs != 0) { @@ -561,6 +571,26 @@ void ia32_emit_am(const ir_node *node) { } } +static void emit_ia32_IMul(const ir_node *node) +{ + ir_node *left = get_irn_n(node, n_ia32_IMul_left); + const arch_register_t *out_reg = get_out_reg(node, pn_ia32_IMul_res); + + be_emit_cstring("\timul"); + ia32_emit_mode_suffix(node); + be_emit_char(' '); + + ia32_emit_binop(node); + + /* do we need the 3-address form? */ + if(is_ia32_NoReg_GP(left) || + get_in_reg(node, n_ia32_IMul_left) != out_reg) { + be_emit_cstring(", "); + emit_register(out_reg, get_ia32_ls_mode(node)); + } + be_emit_finish_line_gas(node); +} + /************************************************* * _ _ _ * (_) | | | @@ -675,21 +705,24 @@ static int determine_final_pnc(const ir_node *node, int flags_pos, } flags_attr = get_ia32_attr_const(cmp); - if(flags_attr->data.cmp_flipped) + if(flags_attr->data.ins_permuted) pnc = get_mirrored_pnc(pnc); pnc |= ia32_pn_Cmp_float; - } else if(is_ia32_Ucomi(flags)) { + } else if(is_ia32_Ucomi(flags) || is_ia32_Fucomi(flags) + || is_ia32_Fucompi(flags)) { flags_attr = get_ia32_attr_const(flags); - if(flags_attr->data.cmp_flipped) + if(flags_attr->data.ins_permuted) pnc = get_mirrored_pnc(pnc); pnc |= ia32_pn_Cmp_float; } else { +#if 0 assert(is_ia32_Cmp(flags) || is_ia32_Test(flags) || is_ia32_Cmp8Bit(flags) || is_ia32_Test8Bit(flags)); +#endif flags_attr = get_ia32_attr_const(flags); - if(flags_attr->data.cmp_flipped) + if(flags_attr->data.ins_permuted) pnc = get_mirrored_pnc(pnc); if(flags_attr->data.cmp_unsigned) pnc |= ia32_pn_Cmp_unsigned; @@ -718,9 +751,19 @@ static void ia32_emit_cmp_suffix(int pnc) void ia32_emit_cmp_suffix_node(const ir_node *node, int flags_pos) { - pn_Cmp pnc = get_ia32_pncode(node); + const ia32_attr_t *attr = get_ia32_attr_const(node); + + pn_Cmp pnc = get_ia32_condcode(node); pnc = determine_final_pnc(node, flags_pos, pnc); + if(attr->data.ins_permuted) { + if(pnc & ia32_pn_Cmp_float) { + pnc = get_negated_pnc(pnc, mode_F); + } else { + pnc = get_negated_pnc(pnc, mode_Iu); + } + } + ia32_emit_cmp_suffix(pnc); } @@ -794,7 +837,7 @@ static void emit_ia32_Jcc(const ir_node *node) const ir_node *proj_false; const ir_node *block; const ir_node *next_block; - pn_Cmp pnc = get_ia32_pncode(node); + pn_Cmp pnc = get_ia32_condcode(node); pnc = determine_final_pnc(node, 0, pnc); @@ -880,10 +923,12 @@ emit_jcc: static void emit_ia32_CMov(const ir_node *node) { - const arch_register_t *out = arch_get_irn_register(arch_env, node); + const ia32_attr_t *attr = get_ia32_attr_const(node); + int ins_permuted = attr->data.ins_permuted; + const arch_register_t *out = arch_get_irn_register(arch_env, node); + pn_Cmp pnc = get_ia32_condcode(node); const arch_register_t *in_true; const arch_register_t *in_false; - pn_Cmp pnc = get_ia32_pncode(node); pnc = determine_final_pnc(node, n_ia32_CMov_eflags, pnc); @@ -898,8 +943,9 @@ static void emit_ia32_CMov(const ir_node *node) } else if(out == in_true) { const arch_register_t *tmp; - /* swap left/right and negate pnc */ - pnc = get_negated_pnc(pnc, mode_Iu); + assert(get_ia32_op_type(node) == ia32_Normal); + + ins_permuted = !ins_permuted; tmp = in_true; in_true = in_false; @@ -913,6 +959,16 @@ static void emit_ia32_CMov(const ir_node *node) be_emit_finish_line_gas(node); } + if(ins_permuted) { + if(pnc & ia32_pn_Cmp_float) { + pnc = get_negated_pnc(pnc, mode_F); + } else { + pnc = get_negated_pnc(pnc, mode_Iu); + } + } + + /* TODO: handling of Nans isn't correct yet */ + be_emit_cstring("\tcmov"); ia32_emit_cmp_suffix(pnc); be_emit_char(' '); @@ -972,11 +1028,12 @@ 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(const ir_node *node) { +static void emit_ia32_SwitchJmp(const ir_node *node) +{ unsigned long interval; int last_value, i; long pnc; + long default_pn; jmp_tbl_t tbl; ir_node *proj; const ir_edge_t *edge; @@ -985,11 +1042,12 @@ void emit_ia32_SwitchJmp(const ir_node *node) { tbl.label = xmalloc(SNPRINTF_BUF_LEN); tbl.label = get_unique_label(tbl.label, SNPRINTF_BUF_LEN, ".TBL_"); tbl.defProj = NULL; - tbl.num_branches = get_irn_n_edges(node); + tbl.num_branches = get_irn_n_edges(node) - 1; tbl.branches = xcalloc(tbl.num_branches, sizeof(tbl.branches[0])); tbl.min_value = INT_MAX; tbl.max_value = INT_MIN; + default_pn = get_ia32_condcode(node); i = 0; /* go over all proj's and collect them */ foreach_out_edge(node, edge) { @@ -998,21 +1056,22 @@ void emit_ia32_SwitchJmp(const ir_node *node) { pnc = get_Proj_proj(proj); - /* create branch entry */ - tbl.branches[i].target = proj; - tbl.branches[i].value = pnc; - - tbl.min_value = pnc < tbl.min_value ? pnc : tbl.min_value; - tbl.max_value = pnc > tbl.max_value ? pnc : tbl.max_value; - /* check for default proj */ - if (pnc == get_ia32_pncode(node)) { - assert(tbl.defProj == NULL && "found two defProjs at SwitchJmp"); + if (pnc == default_pn) { + assert(tbl.defProj == NULL && "found two default Projs at SwitchJmp"); tbl.defProj = proj; + } else { + tbl.min_value = pnc < tbl.min_value ? pnc : tbl.min_value; + tbl.max_value = pnc > tbl.max_value ? pnc : tbl.max_value; + + /* create branch entry */ + tbl.branches[i].target = proj; + tbl.branches[i].value = pnc; + ++i; } - i++; } + assert(i == tbl.num_branches); /* sort the branches by their number */ qsort(tbl.branches, tbl.num_branches, sizeof(tbl.branches[0]), ia32_cmp_branch_t); @@ -1105,11 +1164,9 @@ static void emit_ia32_Immediate(const ir_node *node) be_emit_char('$'); if(attr->symconst != NULL) { - ident *id = get_entity_ld_ident(attr->symconst); - - if(attr->attr.data.am_sc_sign) + if(attr->sc_sign) be_emit_char('-'); - be_emit_ident(id); + ia32_emit_entity(attr->symconst); } if(attr->symconst == NULL || attr->offset != 0) { if(attr->symconst != NULL) { @@ -1120,15 +1177,26 @@ static void emit_ia32_Immediate(const ir_node *node) } } +/** + * Emit an inline assembler operand. + * + * @param node the ia32_ASM node + * @param s points to the operand (a %c) + * + * @return pointer to the first char in s NOT in the current operand + */ static const char* emit_asm_operand(const ir_node *node, const char *s) { + const ia32_attr_t *ia32_attr = get_ia32_attr_const(node); + const ia32_asm_attr_t *attr = CONST_CAST_IA32_ATTR(ia32_asm_attr_t, + ia32_attr); const arch_register_t *reg; + const ia32_asm_reg_t *asm_regs = attr->register_map; + const ia32_asm_reg_t *asm_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 == '%'); @@ -1177,26 +1245,26 @@ static const char* emit_asm_operand(const ir_node *node, const char *s) s += p; } + if(num < 0 || num >= ARR_LEN(asm_regs)) { + ir_fprintf(stderr, "Error: Custom assembler references invalid " + "input/output (%+F)\n", node); + return s; + } + asm_reg = & asm_regs[num]; + assert(asm_reg->valid); + /* get register */ - attr = get_ia32_attr_const(node); - n_outs = ARR_LEN(attr->slots); - if(num < n_outs) { - reg = get_out_reg(node, num); + if(asm_reg->use_input == 0) { + reg = get_out_reg(node, asm_reg->inout_pos); } 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); + ir_node *pred = get_irn_n(node, asm_reg->inout_pos); + /* might be an immediate value */ if(is_ia32_Immediate(pred)) { emit_ia32_Immediate(pred); return s; } - reg = get_in_reg(node, in); + reg = get_in_reg(node, asm_reg->inout_pos); } if(reg == NULL) { ir_fprintf(stderr, "Warning: no register assigned for %d asm op " @@ -1204,25 +1272,34 @@ static const char* emit_asm_operand(const ir_node *node, const char *s) return s; } + if(asm_reg->memory) { + be_emit_char('('); + } + /* emit it */ - be_emit_char('%'); - switch(modifier) { - case 0: - reg_name = arch_register_get_name(reg); - break; - case 'b': - reg_name = ia32_get_mapped_reg_name(isa->regs_8bit, reg); - break; - case 'h': - reg_name = ia32_get_mapped_reg_name(isa->regs_8bit_high, reg); - break; - case 'w': - reg_name = ia32_get_mapped_reg_name(isa->regs_16bit, reg); - break; - default: - panic("Invalid asm op modifier"); + if(modifier != 0) { + be_emit_char('%'); + switch(modifier) { + case 'b': + reg_name = ia32_get_mapped_reg_name(isa->regs_8bit, reg); + break; + case 'h': + reg_name = ia32_get_mapped_reg_name(isa->regs_8bit_high, reg); + break; + case 'w': + reg_name = ia32_get_mapped_reg_name(isa->regs_16bit, reg); + break; + default: + panic("Invalid asm op modifier"); + } + be_emit_string(reg_name); + } else { + emit_register(reg, asm_reg->mode); + } + + if(asm_reg->memory) { + be_emit_char(')'); } - be_emit_string(reg_name); return s; } @@ -1275,11 +1352,11 @@ static void emit_ia32_Asm(const ir_node *node) /** * Emit movsb/w instructions to make mov count divideable by 4 */ -static void emit_CopyB_prolog(int rem) { +static void emit_CopyB_prolog(unsigned size) { be_emit_cstring("\tcld"); be_emit_finish_line_gas(NULL); - switch(rem) { + switch (size) { case 1: be_emit_cstring("\tmovsb"); be_emit_finish_line_gas(NULL); @@ -1302,9 +1379,9 @@ static void emit_CopyB_prolog(int rem) { */ static void emit_ia32_CopyB(const ir_node *node) { - int rem = get_ia32_pncode(node); + unsigned size = get_ia32_copyb_size(node); - emit_CopyB_prolog(rem); + emit_CopyB_prolog(size); be_emit_cstring("\trep movsd"); be_emit_finish_line_gas(node); @@ -1315,7 +1392,7 @@ static void emit_ia32_CopyB(const ir_node *node) */ static void emit_ia32_CopyB_i(const ir_node *node) { - int size = get_ia32_pncode(node); + unsigned size = get_ia32_copyb_size(node); emit_CopyB_prolog(size & 0x3); @@ -1463,14 +1540,6 @@ static void emit_ia32_Conv_I2I(const ir_node *node) be_emit_finish_line_gas(node); } -/** - * Emits code for an 8Bit Int conversion. - */ -static void emit_ia32_Conv_I2I8Bit(const ir_node *node) -{ - emit_ia32_Conv_I2I(node); -} - /******************************************* * _ _ @@ -1491,8 +1560,7 @@ static void emit_be_Call(const ir_node *node) be_emit_cstring("\tcall "); if (ent) { - set_entity_backend_marked(ent, 1); - be_emit_string(get_entity_ld_name(ent)); + ia32_emit_entity(ent); } else { const arch_register_t *reg = get_in_reg(node, be_pos_Call_ptr); be_emit_char('*'); @@ -1621,21 +1689,10 @@ static void emit_be_Perm(const ir_node *node) */ static void emit_ia32_Const(const ir_node *node) { - const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node); - - /* a zero? */ - if(attr->symconst == NULL && attr->offset == 0) { - assert(get_ia32_flags(node) & arch_irn_flags_modify_flags); - be_emit_cstring("\txorl "); - ia32_emit_dest_register(node, 0); - be_emit_cstring(", "); - ia32_emit_dest_register(node, 0); - } else { - be_emit_cstring("\tmovl "); - emit_ia32_Immediate(node); - be_emit_cstring(", "); - ia32_emit_dest_register(node, 0); - } + be_emit_cstring("\tmovl "); + emit_ia32_Immediate(node); + be_emit_cstring(", "); + ia32_emit_dest_register(node, 0); be_emit_finish_line_gas(node); } @@ -1762,7 +1819,13 @@ zero_neg: static void emit_be_Return(const ir_node *node) { + unsigned pop; be_emit_cstring("\tret"); + + pop = be_Return_get_pop(node); + if(pop > 0) { + be_emit_irprintf(" $%d", pop); + } be_emit_finish_line_gas(node); } @@ -1805,6 +1868,7 @@ void ia32_register_emitters(void) { /* other ia32 emitter functions */ IA32_EMIT(Asm); IA32_EMIT(CMov); + IA32_EMIT(IMul); IA32_EMIT(SwitchJmp); IA32_EMIT(CopyB); IA32_EMIT(CopyB_i); @@ -1812,7 +1876,7 @@ void ia32_register_emitters(void) { IA32_EMIT(Conv_FP2I); IA32_EMIT(Conv_FP2FP); IA32_EMIT(Conv_I2I); - IA32_EMIT(Conv_I2I8Bit); + IA32_EMIT2(Conv_I2I8Bit, Conv_I2I); IA32_EMIT(Const); IA32_EMIT(LdTls); IA32_EMIT(Minus64Bit); @@ -1854,7 +1918,7 @@ static void ia32_emit_dbg(const ir_node *node) { dbg_info *db = get_irn_dbg_info(node); unsigned lineno; - const char *fname = be_retrieve_dbg_info(db, &lineno); + const char *fname = ir_retrieve_dbg_info(db, &lineno); if (! cg->birg->main_env->options->stabs_debug_support) return; @@ -1913,55 +1977,27 @@ static void ia32_emit_alignment(unsigned align, unsigned skip) /** * Emits gas alignment directives for Functions depended on cpu architecture. */ -static void ia32_emit_align_func(cpu_support cpu) +static void ia32_emit_align_func(void) { - unsigned align; - unsigned maximum_skip; + unsigned align = ia32_cg_config.function_alignment; + unsigned maximum_skip = (1 << align) - 1; - switch (cpu) { - case arch_i386: - align = 2; - break; - case arch_i486: - align = 4; - break; - case arch_k6: - align = 5; - break; - default: - align = 4; - } - maximum_skip = (1 << align) - 1; ia32_emit_alignment(align, maximum_skip); } /** * Emits gas alignment directives for Labels depended on cpu architecture. */ -static void ia32_emit_align_label(cpu_support cpu) +static void ia32_emit_align_label(void) { - unsigned align; unsigned maximum_skip; - - switch (cpu) { - case arch_i386: - align = 2; - break; - case arch_i486: - align = 4; - break; - case arch_k6: - align = 5; - break; - default: - align = 4; - } - maximum_skip = (1 << align) - 1; + unsigned align = ia32_cg_config.label_alignment; + unsigned maximum_skip = (1 << align) - 1; ia32_emit_alignment(align, maximum_skip); } /** * Test wether a block should be aligned. - * For cpus in the P4/Athlon class it is usefull to align jump labels to + * For cpus in the P4/Athlon class it is useful 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. */ @@ -1972,12 +2008,11 @@ static int should_align_block(ir_node *block, ir_node *prev) 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 = isa->opt_arch; int i, n_cfgpreds; if(exec_freq == NULL) return 0; - if(cpu == arch_i386 || cpu == arch_i486) + if(ia32_cg_config.label_alignment_factor <= 0) return 0; block_freq = get_block_execfreq(exec_freq, block); @@ -2001,14 +2036,7 @@ static int should_align_block(ir_node *block, ir_node *prev) jmp_freq /= prev_freq; - switch (cpu) { - case arch_athlon: - case arch_athlon_64: - case arch_k6: - return jmp_freq > 3; - default: - return jmp_freq > 2; - } + return jmp_freq > ia32_cg_config.label_alignment_factor; } static void ia32_emit_block_header(ir_node *block, ir_node *prev) @@ -2023,7 +2051,7 @@ static void ia32_emit_block_header(ir_node *block, ir_node *prev) if (should_align_block(block, prev)) { assert(need_label); - ia32_emit_align_label(isa->opt_arch); + ia32_emit_align_label(); } if(need_label) { @@ -2076,13 +2104,18 @@ static void ia32_emit_func_prolog(ir_graph *irg) { ir_entity *irg_ent = get_irg_entity(irg); const char *irg_name = get_entity_ld_name(irg_ent); - cpu_support cpu = isa->opt_arch; const be_irg_t *birg = cg->birg; + /* write the begin line (used by scripts processing the assembler... */ be_emit_write_line(); + be_emit_cstring("# -- Begin "); + be_emit_string(irg_name); + be_emit_char('\n'); + be_emit_write_line(); + be_gas_emit_switch_section(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(cpu); + ia32_emit_align_func(); if (get_entity_visibility(irg_ent) == visibility_external_visible) { be_emit_cstring(".global "); be_emit_string(irg_name); @@ -2105,6 +2138,12 @@ static void ia32_emit_func_epilog(ir_graph *irg) ia32_emit_function_size(irg_name); be_dbg_method_end(birg->main_env->db_handle); + + be_emit_cstring("# -- End "); + be_emit_string(irg_name); + be_emit_char('\n'); + be_emit_write_line(); + be_emit_char('\n'); be_emit_write_line(); }