X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fia32%2Fbearch_ia32.c;h=714c49566a543979cdb64810d13849d00d057579;hb=004f9b10d631414720ea9c4a86d3f89b742a027e;hp=8a596abd3f4b96836281a5b706b8cee14656e30b;hpb=a74914e4a230de22c6f7fbb4a314d542bd055ed3;p=libfirm diff --git a/ir/be/ia32/bearch_ia32.c b/ir/be/ia32/bearch_ia32.c index 8a596abd3..714c49566 100644 --- a/ir/be/ia32/bearch_ia32.c +++ b/ir/be/ia32/bearch_ia32.c @@ -1,6 +1,6 @@ /** * This is the main ia32 firm backend driver. - * + * @author Christian Wuerdig * $Id$ */ @@ -230,6 +230,12 @@ static arch_irn_class_t ia32_classify(const void *self, const ir_node *irn) { irn = my_skip_proj(irn); if (is_cfop(irn)) return arch_irn_class_branch; + else if (is_ia32_Cnst(irn)) + return arch_irn_class_const; + else if (is_ia32_Ld(irn)) + return arch_irn_class_load; + else if (is_ia32_St(irn) || is_ia32_Store8Bit(irn)) + return arch_irn_class_store; else if (is_ia32_irn(irn)) return arch_irn_class_normal; else @@ -290,6 +296,12 @@ static void *ia32_abi_init(const be_abi_call_t *call, const arch_env_t *aenv, ir return env; } +/** + * Put all registers which are saved by the prologue/epilogue in a set. + * + * @param self The callback object. + * @param s The result set. + */ static void ia32_abi_dont_save_regs(void *self, pset *s) { ia32_abi_env_t *env = self; @@ -298,10 +310,12 @@ static void ia32_abi_dont_save_regs(void *self, pset *s) } /** - * Generate the prologue. + * Generate the routine prologue. + * * @param self The callback object. * @param mem A pointer to the mem node. Update this if you define new memory. - * @param reg_map A mapping mapping all callee_save/ignore/parameter registers to their defining nodes. + * @param reg_map A map mapping all callee_save/ignore/parameter registers to their defining nodes. + * * @return The register which shall be used as a stack frame base. * * All nodes which define registers in @p reg_map must keep @p reg_map current. @@ -315,17 +329,16 @@ static const arch_register_t *ia32_abi_prologue(void *self, ir_node **mem, pmap ir_node *bl = get_irg_start_block(env->irg); ir_node *curr_sp = be_abi_reg_map_get(reg_map, env->isa->sp); ir_node *curr_bp = be_abi_reg_map_get(reg_map, env->isa->bp); - ir_node *curr_no_reg = be_abi_reg_map_get(reg_map, &ia32_gp_regs[REG_GP_NOREG]); - ir_node *store_bp; + ir_node *push; /* push ebp */ - curr_sp = be_new_IncSP(env->isa->sp, env->irg, bl, curr_sp, *mem, reg_size, be_stack_dir_expand); - store_bp = new_rd_ia32_Store(NULL, env->irg, bl, curr_sp, curr_no_reg, curr_bp, *mem, mode_T); - set_ia32_am_support(store_bp, ia32_am_Dest); - set_ia32_am_flavour(store_bp, ia32_B); - set_ia32_op_type(store_bp, ia32_AddrModeD); - set_ia32_ls_mode(store_bp, env->isa->bp->reg_class->mode); - *mem = new_r_Proj(env->irg, bl, store_bp, mode_M, 0); + push = new_rd_ia32_Push(NULL, env->irg, bl, curr_sp, curr_bp, *mem); + curr_sp = new_r_Proj(env->irg, bl, push, get_irn_mode(curr_sp), pn_ia32_Push_stack); + *mem = new_r_Proj(env->irg, bl, push, mode_M, pn_ia32_Push_M); + + /* the push must have SP out register */ + arch_set_irn_register(env->aenv, curr_sp, env->isa->sp); + set_ia32_flags(push, arch_irn_flags_ignore); /* move esp to ebp */ curr_bp = be_new_Copy(env->isa->bp->reg_class, env->irg, bl, curr_sp); @@ -333,6 +346,12 @@ static const arch_register_t *ia32_abi_prologue(void *self, ir_node **mem, pmap arch_set_irn_register(env->aenv, curr_bp, env->isa->bp); be_node_set_flags(curr_bp, BE_OUT_POS(0), arch_irn_flags_ignore); + /* beware: the copy must be done before any other sp use */ + curr_sp = be_new_CopyKeep_single(env->isa->sp->reg_class, env->irg, bl, curr_sp, curr_bp, get_irn_mode(curr_sp)); + be_set_constr_single_reg(curr_sp, BE_OUT_POS(0), env->isa->sp); + arch_set_irn_register(env->aenv, curr_sp, env->isa->sp); + be_node_set_flags(curr_sp, BE_OUT_POS(0), arch_irn_flags_ignore); + be_abi_reg_map_set(reg_map, env->isa->sp, curr_sp); be_abi_reg_map_set(reg_map, env->isa->bp, curr_bp); @@ -342,37 +361,57 @@ static const arch_register_t *ia32_abi_prologue(void *self, ir_node **mem, pmap return env->isa->sp; } +/** + * Generate the routine epilogue. + * @param self The callback object. + * @param bl The block for the epilog + * @param mem A pointer to the mem node. Update this if you define new memory. + * @param reg_map A map mapping all callee_save/ignore/parameter registers to their defining nodes. + * @return The register which shall be used as a stack frame base. + * + * All nodes which define registers in @p reg_map must keep @p reg_map current. + */ static void ia32_abi_epilogue(void *self, ir_node *bl, ir_node **mem, pmap *reg_map) { ia32_abi_env_t *env = self; ir_node *curr_sp = be_abi_reg_map_get(reg_map, env->isa->sp); ir_node *curr_bp = be_abi_reg_map_get(reg_map, env->isa->bp); - ir_node *curr_no_reg = be_abi_reg_map_get(reg_map, &ia32_gp_regs[REG_GP_NOREG]); if (env->flags.try_omit_fp) { /* simply remove the stack frame here */ curr_sp = be_new_IncSP(env->isa->sp, env->irg, bl, curr_sp, *mem, BE_STACK_FRAME_SIZE, be_stack_dir_shrink); } - else { - ir_node *load_bp; + const ia32_isa_t *isa = (ia32_isa_t *)env->isa; ir_mode *mode_bp = env->isa->bp->reg_class->mode; int reg_size = get_mode_size_bytes(env->isa->bp->reg_class->mode); - /* copy ebp to esp */ - curr_sp = be_new_SetSP(env->isa->sp, env->irg, bl, curr_sp, curr_bp, *mem); - - /* pop ebp */ - load_bp = new_rd_ia32_Load(NULL, env->irg, bl, curr_sp, curr_no_reg, *mem, mode_T); - set_ia32_am_support(load_bp, ia32_am_Source); - set_ia32_am_flavour(load_bp, ia32_B); - set_ia32_op_type(load_bp, ia32_AddrModeS); - set_ia32_ls_mode(load_bp, mode_bp); - curr_bp = new_r_Proj(env->irg, bl, load_bp, mode_bp, 0); - *mem = new_r_Proj(env->irg, bl, load_bp, mode_M, 1); - arch_set_irn_register(env->aenv, curr_bp, env->isa->bp); + /* gcc always emits a leave at the end of a routine */ + if (1 || ARCH_AMD(isa->opt_arch)) { + ir_node *leave; + + /* leave */ + leave = new_rd_ia32_Leave(NULL, env->irg, bl, curr_sp, *mem); + set_ia32_flags(leave, arch_irn_flags_ignore); + curr_bp = new_r_Proj(current_ir_graph, bl, leave, mode_bp, pn_ia32_Leave_frame); + curr_sp = new_r_Proj(current_ir_graph, bl, leave, get_irn_mode(curr_sp), pn_ia32_Leave_stack); + *mem = new_r_Proj(current_ir_graph, bl, leave, mode_M, pn_ia32_Leave_M); + } + else { + ir_node *pop; + + /* copy ebp to esp */ + curr_sp = be_new_SetSP(env->isa->sp, env->irg, bl, curr_sp, curr_bp, *mem); - curr_sp = be_new_IncSP(env->isa->sp, env->irg, bl, curr_sp, *mem, reg_size, be_stack_dir_shrink); + /* pop ebp */ + pop = new_rd_ia32_Pop(NULL, env->irg, bl, curr_sp, *mem); + set_ia32_flags(pop, arch_irn_flags_ignore); + curr_bp = new_r_Proj(current_ir_graph, bl, pop, mode_bp, pn_ia32_Pop_res); + curr_sp = new_r_Proj(current_ir_graph, bl, pop, get_irn_mode(curr_sp), pn_ia32_Pop_stack); + *mem = new_r_Proj(current_ir_graph, bl, pop, mode_M, pn_ia32_Pop_M); + } + arch_set_irn_register(env->aenv, curr_sp, env->isa->sp); + arch_set_irn_register(env->aenv, curr_bp, env->isa->bp); } be_abi_reg_map_set(reg_map, env->isa->sp, curr_sp); @@ -417,6 +456,176 @@ static ir_type *ia32_abi_get_between_type(void *self) return env->flags.try_omit_fp ? omit_fp_between_type : between_type; } +/** + * Get the estimated cycle count for @p irn. + * + * @param self The this pointer. + * @param irn The node. + * + * @return The estimated cycle count for this operation + */ +static int ia32_get_op_estimated_cost(const void *self, const ir_node *irn) +{ + int cost; + switch (get_ia32_irn_opcode(irn)) { + case iro_ia32_xDiv: + case iro_ia32_DivMod: + cost = 8; + break; + + case iro_ia32_xLoad: + case iro_ia32_l_Load: + case iro_ia32_Load: + case iro_ia32_Push: + case iro_ia32_Pop: + cost = 10; + break; + + case iro_ia32_xStore: + case iro_ia32_l_Store: + case iro_ia32_Store: + case iro_ia32_Store8Bit: + cost = 50; + break; + + case iro_ia32_MulS: + case iro_ia32_Mul: + case iro_ia32_Mulh: + case iro_ia32_xMul: + case iro_ia32_l_MulS: + case iro_ia32_l_Mul: + cost = 2; + break; + + default: + cost = 1; + } + + return cost; +} + +/** + * Returns the inverse operation if @p irn, recalculating the argument at position @p i. + * + * @param irn The original operation + * @param i Index of the argument we want the inverse operation to yield + * @param inverse struct to be filled with the resulting inverse op + * @param obstack The obstack to use for allocation of the returned nodes array + * @return The inverse operation or NULL if operation invertible + */ +static arch_inverse_t *ia32_get_inverse(const void *self, const ir_node *irn, int i, arch_inverse_t *inverse, struct obstack *obst) { + ir_graph *irg; + ir_mode *mode; + ir_node *block, *noreg, *nomem; + int pnc; + + /* we cannot invert non-ia32 irns */ + if (! is_ia32_irn(irn)) + return NULL; + + /* operand must always be a real operand (not base, index or mem) */ + if (i != 2 && i != 3) + return NULL; + + /* we don't invert address mode operations */ + if (get_ia32_op_type(irn) != ia32_Normal) + return NULL; + + irg = get_irn_irg(irn); + block = get_nodes_block(irn); + mode = get_ia32_res_mode(irn); + noreg = get_irn_n(irn, 0); + nomem = new_r_NoMem(irg); + + /* initialize structure */ + inverse->nodes = obstack_alloc(obst, 2 * sizeof(inverse->nodes[0])); + inverse->costs = 0; + inverse->n = 2; + + switch (get_ia32_irn_opcode(irn)) { + case iro_ia32_Add: + if (get_ia32_immop_type(irn) == ia32_ImmConst) { + /* we have an add with a const here */ + /* invers == add with negated const */ + inverse->nodes[0] = new_rd_ia32_Add(NULL, irg, block, noreg, noreg, get_irn_n(irn, i), noreg, nomem); + pnc = pn_ia32_Add_res; + inverse->costs += 1; + copy_ia32_Immop_attr(inverse->nodes[0], (ir_node *)irn); + set_ia32_Immop_tarval(inverse->nodes[0], tarval_neg(get_ia32_Immop_tarval(irn))); + set_ia32_commutative(inverse->nodes[0]); + } + else if (get_ia32_immop_type(irn) == ia32_ImmSymConst) { + /* we have an add with a symconst here */ + /* invers == sub with const */ + inverse->nodes[0] = new_rd_ia32_Sub(NULL, irg, block, noreg, noreg, get_irn_n(irn, i), noreg, nomem); + pnc = pn_ia32_Sub_res; + inverse->costs += 5; + copy_ia32_Immop_attr(inverse->nodes[0], (ir_node *)irn); + } + else { + /* normal add: inverse == sub */ + inverse->nodes[0] = new_rd_ia32_Sub(NULL, irg, block, noreg, noreg, (ir_node *)irn, get_irn_n(irn, i ^ 1), nomem); + pnc = pn_ia32_Sub_res; + inverse->costs += 5; + } + break; + case iro_ia32_Sub: + if (get_ia32_immop_type(irn) != ia32_ImmNone) { + /* we have a sub with a const/symconst here */ + /* invers == add with this const */ + inverse->nodes[0] = new_rd_ia32_Add(NULL, irg, block, noreg, noreg, get_irn_n(irn, i), noreg, nomem); + pnc = pn_ia32_Add_res; + inverse->costs += (get_ia32_immop_type(irn) == ia32_ImmSymConst) ? 5 : 1; + copy_ia32_Immop_attr(inverse->nodes[0], (ir_node *)irn); + } + else { + /* normal sub */ + if (i == 2) { + inverse->nodes[0] = new_rd_ia32_Add(NULL, irg, block, noreg, noreg, (ir_node *)irn, get_irn_n(irn, 3), nomem); + } + else { + inverse->nodes[0] = new_rd_ia32_Sub(NULL, irg, block, noreg, noreg, get_irn_n(irn, 2), (ir_node *)irn, nomem); + } + pnc = pn_ia32_Sub_res; + inverse->costs += 1; + } + break; + case iro_ia32_Eor: + if (get_ia32_immop_type(irn) != ia32_ImmNone) { + /* xor with const: inverse = xor */ + inverse->nodes[0] = new_rd_ia32_Eor(NULL, irg, block, noreg, noreg, get_irn_n(irn, i), noreg, nomem); + pnc = pn_ia32_Eor_res; + inverse->costs += (get_ia32_immop_type(irn) == ia32_ImmSymConst) ? 5 : 1; + copy_ia32_Immop_attr(inverse->nodes[0], (ir_node *)irn); + } + else { + /* normal xor */ + inverse->nodes[0] = new_rd_ia32_Eor(NULL, irg, block, noreg, noreg, (ir_node *)irn, get_irn_n(irn, i), nomem); + pnc = pn_ia32_Eor_res; + inverse->costs += 1; + } + break; + case iro_ia32_Not: + inverse->nodes[0] = new_rd_ia32_Not(NULL, irg, block, noreg, noreg, get_irn_n(irn, i), nomem); + pnc = pn_ia32_Not_res; + inverse->costs += 1; + break; + case iro_ia32_Minus: + inverse->nodes[0] = new_rd_ia32_Minus(NULL, irg, block, noreg, noreg, get_irn_n(irn, i), nomem); + pnc = pn_ia32_Minus_res; + inverse->costs += 1; + break; + default: + /* inverse operation not supported */ + return NULL; + } + + set_ia32_res_mode(inverse->nodes[0], mode); + inverse->nodes[1] = new_r_Proj(irg, block, inverse->nodes[0], mode, pnc); + + return inverse; +} + static const be_abi_callbacks_t ia32_abi_callbacks = { ia32_abi_init, free, @@ -435,7 +644,9 @@ static const arch_irn_ops_if_t ia32_irn_ops_if = { ia32_classify, ia32_get_flags, ia32_get_frame_entity, - ia32_set_stack_bias + ia32_set_stack_bias, + ia32_get_inverse, + ia32_get_op_estimated_cost }; ia32_irn_ops_t ia32_irn_ops = { @@ -462,27 +673,36 @@ ia32_irn_ops_t ia32_irn_ops = { */ static void ia32_prepare_graph(void *self) { ia32_code_gen_t *cg = self; + dom_front_info_t *dom; DEBUG_ONLY(firm_dbg_module_t *old_mod = cg->mod;) FIRM_DBG_REGISTER(cg->mod, "firm.be.ia32.transform"); - ia32_register_transformers(); - irg_walk_blkwise_graph(cg->irg, ia32_place_consts_set_modes, ia32_transform_node, cg); - be_dump(cg->irg, "-transformed", dump_ir_block_graph_sched); - if (cg->opt & IA32_OPT_DOAM) { - edges_deactivate(cg->irg); - //dead_node_elimination(cg->irg); - edges_activate(cg->irg); + /* 1st: transform constants and psi condition trees */ + irg_walk_blkwise_graph(cg->irg, ia32_place_consts_set_modes, ia32_transform_psi_cond_tree, cg); - FIRM_DBG_REGISTER(cg->mod, "firm.be.ia32.am"); - - irg_walk_blkwise_graph(cg->irg, NULL, ia32_optimize_am, cg); - be_dump(cg->irg, "-am", dump_ir_block_graph_sched); - } + /* 2nd: transform all remaining nodes */ + ia32_register_transformers(); + dom = be_compute_dominance_frontiers(cg->irg); + irg_walk_blkwise_graph(cg->irg, NULL, ia32_transform_node, cg); + be_free_dominance_frontiers(dom); + be_dump(cg->irg, "-transformed", dump_ir_block_graph_sched); + /* 3rd: optimize address mode */ + FIRM_DBG_REGISTER(cg->mod, "firm.be.ia32.am"); + ia32_optimize_addressmode(cg); + be_dump(cg->irg, "-am", dump_ir_block_graph_sched); DEBUG_ONLY(cg->mod = old_mod;) } +static INLINE int need_constraint_copy(ir_node *irn) { + return \ + ! is_ia32_Lea(irn) && \ + ! is_ia32_Conv_I2I(irn) && \ + ! is_ia32_Conv_I2I8Bit(irn) && \ + ! is_ia32_CmpCMov(irn) && \ + ! is_ia32_CmpSet(irn); +} /** * Insert copies for all ia32 nodes where the should_be_same requirement @@ -508,7 +728,8 @@ static void ia32_finish_node(ir_node *irn, void *env) { block = get_nodes_block(irn); /* check all OUT requirements, if there is a should_be_same */ - if (op_tp == ia32_Normal && ! is_ia32_Lea(irn) && ! is_ia32_Conv_I2I(irn) && ! is_ia32_Conv_I2I8Bit(irn)) { + if ((op_tp == ia32_Normal || op_tp == ia32_AddrModeS) && need_constraint_copy(irn)) + { for (i = 0; i < n_res; i++) { if (arch_register_req_is(&(reqs[i]->req), should_be_same)) { /* get in and out register */ @@ -558,14 +779,16 @@ insert_copy: } } - /* If we have a CondJmp with immediate, we need to */ + /* If we have a CondJmp/CmpSet/xCmpSet with immediate, we need to */ /* check if it's the right operand, otherwise we have */ /* to change it, as CMP doesn't support immediate as */ /* left operands. */ - if (is_ia32_CondJmp(irn) && (is_ia32_ImmConst(irn) || is_ia32_ImmSymConst(irn)) && op_tp == ia32_AddrModeS) { - long pnc = get_negated_pnc(get_ia32_pncode(irn), get_ia32_res_mode(irn)); + if ((is_ia32_CondJmp(irn) || is_ia32_CmpSet(irn) || is_ia32_xCmpSet(irn)) && + (is_ia32_ImmConst(irn) || is_ia32_ImmSymConst(irn)) && + op_tp == ia32_AddrModeS) + { set_ia32_op_type(irn, ia32_AddrModeD); - set_ia32_pncode(irn, pnc); + set_ia32_pncode(irn, get_inversed_pnc(get_ia32_pncode(irn))); } /* check if there is a sub which need to be transformed */ @@ -637,12 +860,12 @@ static void transform_to_Load(ia32_transform_env_t *env) { if (mode_is_float(mode)) { if (USE_SSE2(env->cg)) - new_op = new_rd_ia32_fLoad(env->dbg, env->irg, env->block, ptr, noreg, mem, mode_T); + new_op = new_rd_ia32_xLoad(env->dbg, env->irg, env->block, ptr, noreg, mem); else - new_op = new_rd_ia32_vfld(env->dbg, env->irg, env->block, ptr, noreg, mem, mode_T); + new_op = new_rd_ia32_vfld(env->dbg, env->irg, env->block, ptr, noreg, mem); } else { - new_op = new_rd_ia32_Load(env->dbg, env->irg, env->block, ptr, noreg, mem, mode_T); + new_op = new_rd_ia32_Load(env->dbg, env->irg, env->block, ptr, noreg, mem); } set_ia32_am_support(new_op, ia32_am_Source); @@ -667,7 +890,7 @@ static void transform_to_Load(ia32_transform_env_t *env) { reg = arch_get_irn_register(env->cg->arch_env, irn); arch_set_irn_register(env->cg->arch_env, new_op, reg); - SET_IA32_ORIG_NODE(new_op, ia32_get_old_node_name(env->cg, new_op)); + SET_IA32_ORIG_NODE(new_op, ia32_get_old_node_name(env->cg, irn)); exchange(irn, proj); } @@ -692,15 +915,15 @@ static void transform_to_Store(ia32_transform_env_t *env) { if (mode_is_float(mode)) { if (USE_SSE2(env->cg)) - new_op = new_rd_ia32_fStore(env->dbg, env->irg, env->block, ptr, noreg, val, nomem, mode_T); + new_op = new_rd_ia32_xStore(env->dbg, env->irg, env->block, ptr, noreg, val, nomem); else - new_op = new_rd_ia32_vfst(env->dbg, env->irg, env->block, ptr, noreg, val, nomem, mode_T); + new_op = new_rd_ia32_vfst(env->dbg, env->irg, env->block, ptr, noreg, val, nomem); } else if (get_mode_size_bits(mode) == 8) { - new_op = new_rd_ia32_Store8Bit(env->dbg, env->irg, env->block, ptr, noreg, val, nomem, mode_T); + new_op = new_rd_ia32_Store8Bit(env->dbg, env->irg, env->block, ptr, noreg, val, nomem); } else { - new_op = new_rd_ia32_Store(env->dbg, env->irg, env->block, ptr, noreg, val, nomem, mode_T); + new_op = new_rd_ia32_Store(env->dbg, env->irg, env->block, ptr, noreg, val, nomem); } set_ia32_am_support(new_op, ia32_am_Dest); @@ -712,7 +935,7 @@ static void transform_to_Store(ia32_transform_env_t *env) { DBG_OPT_SPILL2ST(irn, new_op); - proj = new_rd_Proj(env->dbg, env->irg, env->block, new_op, mode_M, 0); + proj = new_rd_Proj(env->dbg, env->irg, env->block, new_op, mode_M, pn_ia32_Store_M); if (sched_point) { sched_add_after(sched_point, new_op); @@ -721,7 +944,7 @@ static void transform_to_Store(ia32_transform_env_t *env) { sched_remove(irn); } - SET_IA32_ORIG_NODE(new_op, ia32_get_old_node_name(env->cg, new_op)); + SET_IA32_ORIG_NODE(new_op, ia32_get_old_node_name(env->cg, irn)); exchange(irn, proj); } @@ -787,7 +1010,7 @@ static void ia32_after_ra(void *self) { irg_block_walk_graph(cg->irg, NULL, ia32_after_ra_walker, self); /* if we do x87 code generation, rewrite all the virtual instructions and registers */ - if (cg->used_fp == fp_x87) { + if (cg->used_fp == fp_x87 || cg->force_sim) { x87_simulate_graph(cg->arch_env, cg->irg, cg->blk_sched); } } @@ -864,13 +1087,6 @@ static void *ia32_cg_init(const be_irg_t *birg) { } #endif /* NDEBUG */ - isa->num_codegens++; - - if (isa->num_codegens > 1) - cg->emit_decls = 0; - else - cg->emit_decls = 1; - cur_reg_set = cg->reg_set; ia32_irn_ops.cg = cg; @@ -896,17 +1112,20 @@ static void *ia32_cg_init(const be_irg_t *birg) { * arguments. */ static ia32_isa_t ia32_isa_template = { - &ia32_isa_if, /* isa interface implementation */ - &ia32_gp_regs[REG_ESP], /* stack pointer register */ - &ia32_gp_regs[REG_EBP], /* base pointer register */ - -1, /* stack direction */ - 0, /* number of code generator objects so far */ + { + &ia32_isa_if, /* isa interface implementation */ + &ia32_gp_regs[REG_ESP], /* stack pointer register */ + &ia32_gp_regs[REG_EBP], /* base pointer register */ + -1, /* stack direction */ + }, NULL, /* 16bit register names */ NULL, /* 8bit register names */ NULL, /* types */ NULL, /* tv_ents */ - (0 | + (0 | + IA32_OPT_INCDEC | /* optimize add 1, sub 1 into inc/dec default: on */ IA32_OPT_DOAM | /* optimize address mode default: on */ + IA32_OPT_LEA | /* optimize for LEAs default: on */ IA32_OPT_PLACECNST | /* place constants immediately before instructions, default: on */ IA32_OPT_IMMOPS | /* operations can use immediates, default: on */ IA32_OPT_EXTBB), /* use extended basic block scheduling, default: on */ @@ -935,7 +1154,16 @@ static void *ia32_init(FILE *file_handle) { ia32_register_init(isa); ia32_create_opcodes(); - ia32_register_copy_attr_func(); + + if ((ARCH_INTEL(isa->arch) && isa->arch < arch_pentium_4) || + (ARCH_AMD(isa->arch) && isa->arch < arch_athlon)) + /* no SSE2 for these cpu's */ + isa->fp_kind = fp_x87; + + if (ARCH_INTEL(isa->opt_arch) && isa->opt_arch >= arch_pentium_4) { + /* Pentium 4 don't like inc and dec instructions */ + isa->opt &= ~IA32_OPT_INCDEC; + } isa->regs_16bit = pmap_create(); isa->regs_8bit = pmap_create(); @@ -964,6 +1192,8 @@ static void *ia32_init(FILE *file_handle) { isa->name_obst_size = 0; #endif /* NDEBUG */ + ia32_handle_intrinsics(); + ia32_switch_section(NULL, NO_SECTION); fprintf(isa->out, "\t.intel_syntax\n"); inited = 1; @@ -1000,11 +1230,11 @@ static void ia32_done(void *self) { * Return the number of register classes for this architecture. * We report always these: * - the general purpose registers - * - the floating point register set (depending on the unit used for FP) - * - MMX/SSE registers (currently not supported) + * - the SSE floating point register set + * - the virtual floating point registers */ static int ia32_get_n_reg_class(const void *self) { - return 2; + return 3; } /** @@ -1012,10 +1242,13 @@ static int ia32_get_n_reg_class(const void *self) { */ static const arch_register_class_t *ia32_get_reg_class(const void *self, int i) { const ia32_isa_t *isa = self; - assert(i >= 0 && i < 2 && "Invalid ia32 register class requested."); + assert(i >= 0 && i < 3 && "Invalid ia32 register class requested."); if (i == 0) return &ia32_reg_classes[CLASS_ia32_gp]; - return USE_SSE2(isa) ? &ia32_reg_classes[CLASS_ia32_xmm] : &ia32_reg_classes[CLASS_ia32_vfp]; + else if (i == 1) + return &ia32_reg_classes[CLASS_ia32_xmm]; + else + return &ia32_reg_classes[CLASS_ia32_vfp]; } /** @@ -1052,9 +1285,11 @@ static void ia32_get_call_abi(const void *self, ir_type *method_type, be_abi_cal const arch_register_t *reg; be_abi_call_flags_t call_flags = be_abi_call_get_flags(abi); + unsigned use_push = !IS_P6_ARCH(isa->opt_arch); + /* set abi flags for calls */ call_flags.bits.left_to_right = 0; /* always last arg first on stack */ - call_flags.bits.store_args_sequential = 0; /* use stores instead of push */ + call_flags.bits.store_args_sequential = use_push; /* call_flags.bits.try_omit_fp not changed: can handle both settings */ call_flags.bits.fp_free = 0; /* the frame pointer is fixed in IA32 */ call_flags.bits.call_has_imm = 1; /* IA32 calls can have immediate address */ @@ -1175,6 +1410,32 @@ static int ia32_get_reg_class_alignment(const void *self, const arch_register_cl return bytes; } +static ia32_intrinsic_env_t intrinsic_env = { NULL, NULL }; + +/** + * Returns the libFirm configuration parameter for this backend. + */ +static const backend_params *ia32_get_libfirm_params(void) { + static const arch_dep_params_t ad = { + 1, /* also use subs */ + 4, /* maximum shifts */ + 31, /* maximum shift amount */ + + 1, /* allow Mulhs */ + 1, /* allow Mulus */ + 32 /* Mulh allowed up to 32 bit */ + }; + static backend_params p = { + NULL, /* no additional opcodes */ + NULL, /* will be set later */ + 1, /* need dword lowering */ + ia32_create_intrinsic_fkt, + &intrinsic_env, /* context for ia32_create_intrinsic_fkt */ + }; + + p.dep_param = &ad; + return &p; +} #ifdef WITH_LIBCORE /* instruction set architectures. */ @@ -1220,15 +1481,26 @@ static lc_opt_enum_int_var_t fp_unit_var = { &ia32_isa_template.fp_kind, fp_unit_items }; +static const lc_opt_enum_int_items_t gas_items[] = { + { "linux", ASM_LINUX_GAS }, + { "mingw", ASM_MINGW_GAS }, + { NULL, 0 } +}; + +static lc_opt_enum_int_var_t gas_var = { + (int *)&asm_flavour, gas_items +}; + static const lc_opt_table_entry_t ia32_options[] = { - LC_OPT_ENT_ENUM_INT("arch", "select the instruction architecture", &arch_var), - LC_OPT_ENT_ENUM_INT("opt", "optimize for instruction architecture", &opt_arch_var), - LC_OPT_ENT_ENUM_INT("fpunit", "select the floating point unit", &fp_unit_var), - LC_OPT_ENT_BIT("incdec", "optimize for inc/dec", &ia32_isa_template.opt, IA32_OPT_INCDEC), - LC_OPT_ENT_NEGBIT("noaddrmode", "do not use address mode", &ia32_isa_template.opt, IA32_OPT_DOAM), + LC_OPT_ENT_ENUM_INT("arch", "select the instruction architecture", &arch_var), + LC_OPT_ENT_ENUM_INT("opt", "optimize for instruction architecture", &opt_arch_var), + LC_OPT_ENT_ENUM_INT("fpunit", "select the floating point unit", &fp_unit_var), + LC_OPT_ENT_NEGBIT("noaddrmode", "do not use address mode", &ia32_isa_template.opt, IA32_OPT_DOAM), + LC_OPT_ENT_NEGBIT("nolea", "do not optimize for LEAs", &ia32_isa_template.opt, IA32_OPT_LEA), LC_OPT_ENT_NEGBIT("noplacecnst", "do not place constants", &ia32_isa_template.opt, IA32_OPT_PLACECNST), - LC_OPT_ENT_NEGBIT("noimmop", "no operations with immediates", &ia32_isa_template.opt, IA32_OPT_IMMOPS), - LC_OPT_ENT_NEGBIT("noextbb", "do not use extended basic block scheduling", &ia32_isa_template.opt, IA32_OPT_EXTBB), + LC_OPT_ENT_NEGBIT("noimmop", "no operations with immediates", &ia32_isa_template.opt, IA32_OPT_IMMOPS), + LC_OPT_ENT_NEGBIT("noextbb", "do not use extended basic block scheduling", &ia32_isa_template.opt, IA32_OPT_EXTBB), + LC_OPT_ENT_ENUM_INT("gasmode", "set the GAS compatibility mode", &gas_var), { NULL } }; @@ -1242,9 +1514,11 @@ static const lc_opt_table_entry_t ia32_options[] = { * ia32-fpunit=unit select floating point unit (x87 or SSE2) * ia32-incdec optimize for inc/dec * ia32-noaddrmode do not use address mode + * ia32-nolea do not optimize for LEAs * ia32-noplacecnst do not place constants, * ia32-noimmop no operations with immediates * ia32-noextbb do not use extended basic block scheduling + * ia32-gasmode set the GAS compatibility mode */ static void ia32_register_options(lc_opt_entry_t *ent) { @@ -1264,6 +1538,7 @@ const arch_isa_if_t ia32_isa_if = { ia32_get_code_generator_if, ia32_get_list_sched_selector, ia32_get_reg_class_alignment, + ia32_get_libfirm_params, #ifdef WITH_LIBCORE ia32_register_options #endif