- gcc ignores RW==1 for non 3DNow!
[libfirm] / ir / be / beabi.c
index 880a3c3..2e7aad1 100644 (file)
@@ -44,6 +44,7 @@
 #include "irtools.h"
 #include "raw_bitset.h"
 #include "error.h"
+#include "pset_new.h"
 
 #include "be.h"
 #include "beabi.h"
@@ -427,9 +428,6 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
        int n_params               = get_method_n_params(call_tp);
        ir_node *curr_mem          = get_Call_mem(irn);
        ir_node *bl                = get_nodes_block(irn);
-       pset *results              = pset_new_ptr(8);
-       pset *caller_save          = pset_new_ptr(8);
-       pset *states               = pset_new_ptr(2);
        int stack_size             = 0;
        int stack_dir              = arch_env_stack_dir(arch_env);
        const arch_register_t *sp  = arch_env_sp(arch_env);
@@ -445,6 +443,8 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
        int n_stack_params = 0;
        int n_ins;
 
+       pset_new_t              destroyed_regs, states;
+       pset_new_iterator_t     iter;
        ir_node                *low_call;
        ir_node               **in;
        ir_node               **res_projs;
@@ -453,10 +453,12 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
        const ir_edge_t        *edge;
        int                    *reg_param_idxs;
        int                    *stack_param_idx;
-       int                     i;
-       int                     n;
+       int                     i, n, destroy_all_regs;
        dbg_info               *dbgi;
 
+       pset_new_init(&destroyed_regs);
+       pset_new_init(&states);
+
        /* Let the isa fill out the abi description for that call node. */
        arch_env_get_call_abi(arch_env, call_tp, call);
 
@@ -562,7 +564,7 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
                        if (is_atomic_type(param_type)) {
                                ir_node *store;
                                ir_node *mem_input = do_seq ? curr_mem : new_NoMem();
-                               store = new_rd_Store(dbgi, irg, bl, mem_input, addr, param);
+                               store = new_rd_Store(dbgi, irg, bl, mem_input, addr, param, 0);
                                mem = new_r_Proj(irg, bl, store, mode_M, pn_Store_M);
                        }
 
@@ -596,24 +598,45 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
                obstack_free(obst, in);
        }
 
-       /* Collect caller save registers */
+       /* check for the return_twice property */
+       destroy_all_regs = 0;
+       if (is_SymConst_addr_ent(call_ptr)) {
+               ir_entity *ent = get_SymConst_entity(call_ptr);
+
+               if (get_entity_additional_properties(ent) & mtp_property_returns_twice)
+                       destroy_all_regs = 1;
+       } else {
+               ir_type *call_tp = get_Call_type(irn);
+
+               if (get_method_additional_properties(call_tp) & mtp_property_returns_twice)
+                       destroy_all_regs = 1;
+       }
+
+       /* Put caller save into the destroyed set and state registers in the states set */
        for (i = 0, n = arch_env_get_n_reg_class(arch_env); i < n; ++i) {
                unsigned j;
                const arch_register_class_t *cls = arch_env_get_reg_class(arch_env, i);
                for (j = 0; j < cls->n_regs; ++j) {
                        const arch_register_t *reg = arch_register_for_index(cls, j);
-                       if (arch_register_type_is(reg, caller_save)) {
-                               pset_insert_ptr(caller_save, (void *) reg);
+
+                       if (destroy_all_regs || arch_register_type_is(reg, caller_save)) {
+                               if (! arch_register_type_is(reg, ignore))
+                                       pset_new_insert(&destroyed_regs, (void *) reg);
                        }
                        if (arch_register_type_is(reg, state)) {
-                               pset_insert_ptr(caller_save, (void*) reg);
-                               pset_insert_ptr(states, (void*) reg);
+                               pset_new_insert(&destroyed_regs, (void*) reg);
+                               pset_new_insert(&states, (void*) reg);
                        }
                }
        }
 
-       /* search the greatest result proj number */
+       if (destroy_all_regs) {
+               /* even if destroyed all is specified, neither SP nor FP are destroyed (else bad things will happen) */
+               pset_new_remove(&destroyed_regs, arch_env->sp);
+               pset_new_remove(&destroyed_regs, arch_env->bp);
+       }
 
+       /* search the largest result proj number */
        res_projs = ALLOCANZ(ir_node*, n_res);
 
        foreach_out_edge(irn, edge) {
@@ -647,7 +670,9 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
        for (i = 0; i < n_reg_params; ++i) {
                obstack_ptr_grow(obst, get_Call_param(irn, reg_param_idxs[i]));
        }
-       foreach_pset(states, reg) {
+
+       /* add state registers ins */
+       foreach_pset_new(&states, reg, iter) {
                const arch_register_class_t *cls = arch_register_get_class(reg);
 #if 0
                ir_node *regnode = be_abi_reg_map_get(env->regs, reg);
@@ -656,23 +681,26 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
                ir_node *regnode = new_rd_Unknown(irg, arch_register_class_mode(cls));
                obstack_ptr_grow(obst, regnode);
        }
-       n_ins = n_reg_params + pset_count(states);
+       n_ins = n_reg_params + pset_new_size(&states);
 
        in = obstack_finish(obst);
 
+       /* ins collected, build the call */
        if (env->call->flags.bits.call_has_imm && is_SymConst(call_ptr)) {
                /* direct call */
                low_call = be_new_Call(dbgi, irg, bl, curr_mem, curr_sp, curr_sp,
-                                      n_reg_results + pn_be_Call_first_res + pset_count(caller_save),
+                                      n_reg_results + pn_be_Call_first_res + pset_new_size(&destroyed_regs),
                                       n_ins, in, get_Call_type(irn));
                be_Call_set_entity(low_call, get_SymConst_entity(call_ptr));
        } else {
                /* indirect call */
                low_call = be_new_Call(dbgi, irg, bl, curr_mem, curr_sp, call_ptr,
-                                      n_reg_results + pn_be_Call_first_res + pset_count(caller_save),
+                                      n_reg_results + pn_be_Call_first_res + pset_new_size(&destroyed_regs),
                                       n_ins, in, get_Call_type(irn));
        }
        be_Call_set_pop(low_call, call->pop);
+
+       /* put the call into the list of all calls for later processing */
        ARR_APP1(ir_node *, env->calls, low_call);
 
        /* create new stack pointer */
@@ -682,6 +710,7 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
                        arch_register_req_type_ignore | arch_register_req_type_produces_sp);
        arch_set_irn_register(curr_sp, sp);
 
+       /* now handle results */
        for (i = 0; i < n_res; ++i) {
                int pn;
                ir_node           *proj = res_projs[i];
@@ -708,7 +737,7 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
                }
 
                if (arg->in_reg) {
-                       pset_remove_ptr(caller_save, arg->reg);
+                       pset_new_remove(&destroyed_regs, arg->reg);
                }
        }
 
@@ -750,22 +779,21 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
 
        /* Make additional projs for the caller save registers
           and the Keep node which keeps them alive. */
-       if (1 || pset_count(caller_save) + n_reg_results > 0) {
+       {
                const arch_register_t *reg;
                ir_node               **in, *keep;
                int                   i;
                int                   n = 0;
-               int                   curr_res_proj
-                       = pn_be_Call_first_res + n_reg_results;
+               int                   curr_res_proj = pn_be_Call_first_res + n_reg_results;
+               pset_new_iterator_t   iter;
 
                /* also keep the stack pointer */
                ++n;
                set_irn_link(curr_sp, (void*) sp);
                obstack_ptr_grow(obst, curr_sp);
 
-               for (reg = pset_first(caller_save); reg; reg = pset_next(caller_save), ++n) {
-                       ir_node *proj = new_r_Proj(irg, bl, low_call, reg->reg_class->mode,
-                                                  curr_res_proj);
+               foreach_pset_new(&destroyed_regs, reg, iter) {
+                       ir_node *proj = new_r_Proj(irg, bl, low_call, reg->reg_class->mode, curr_res_proj);
 
                        /* memorize the register in the link field. we need afterwards to set the register class of the keep correctly. */
                        be_set_constr_single_reg_out(low_call, curr_res_proj, reg, 0);
@@ -773,7 +801,8 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
 
                        set_irn_link(proj, (void*) reg);
                        obstack_ptr_grow(obst, proj);
-                       curr_res_proj++;
+                       ++curr_res_proj;
+                       ++n;
                }
 
                for (i = 0; i < n_reg_results; ++i) {
@@ -821,9 +850,9 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
 
        be_abi_call_free(call);
        obstack_free(obst, stack_param_idx);
-       del_pset(results);
-       del_pset(states);
-       del_pset(caller_save);
+
+       pset_new_destroy(&states);
+       pset_new_destroy(&destroyed_regs);
 
        return curr_sp;
 }
@@ -851,11 +880,11 @@ static ir_node *adjust_alloc_size(unsigned stack_alignment, ir_node *size,
 
                mode = get_irn_mode(size);
                tv   = new_tarval_from_long(stack_alignment-1, mode);
-               mask = new_r_Const(irg, mode, tv);
+               mask = new_r_Const(irg, tv);
                size = new_rd_Add(dbg, irg, block, size, mask, mode);
 
                tv   = new_tarval_from_long(-(long)stack_alignment, mode);
-               mask = new_r_Const(irg, mode, tv);
+               mask = new_r_Const(irg, tv);
                size = new_rd_And(dbg, irg, block, size, mask, mode);
        }
        return size;
@@ -917,7 +946,7 @@ static ir_node *adjust_alloc(be_abi_irg_t *env, ir_node *alloc, ir_node *curr_sp
                ir_mode *mode = get_irn_mode(size);
                tarval *tv    = new_tarval_from_long(get_type_size_bytes(type),
                                                     mode);
-               ir_node *cnst = new_rd_Const(dbg, irg, mode, tv);
+               ir_node *cnst = new_rd_Const(dbg, irg, tv);
                size          = new_rd_Mul(dbg, irg, block, size, cnst, mode);
        }
 
@@ -983,7 +1012,7 @@ static ir_node *adjust_free(be_abi_irg_t *env, ir_node *free, ir_node *curr_sp)
        /* we might need to multiply the size with the element size */
        if (type != firm_unknown_type && get_type_size_bytes(type) != 1) {
                tarval *tv = new_tarval_from_long(get_type_size_bytes(type), mode_Iu);
-               ir_node *cnst = new_rd_Const(dbg, irg, mode_Iu, tv);
+               ir_node *cnst = new_rd_Const(dbg, irg, tv);
                ir_node *mul = new_rd_Mul(dbg, irg, block, get_Free_size(free),
                                          cnst, mode_Iu);
                size = mul;
@@ -1173,7 +1202,7 @@ static void process_ops_in_block(ir_node *bl, void *data)
        }
 
        set_irn_link(bl, curr_sp);
-}  /* process_calls_in_block */
+}  /* process_ops_in_block */
 
 /**
  * Adjust all call nodes in the graph to the ABI conventions.
@@ -1587,7 +1616,7 @@ static void fix_address_of_parameter_access(be_abi_irg_t *env, ir_entity *value_
 
                        /* the backing store itself */
                        store = new_r_Store(irg, first_bl, mem, addr,
-                                           new_r_Proj(irg, args_bl, args, mode, i));
+                                           new_r_Proj(irg, args_bl, args, mode, i), 0);
                }
                /* the new memory Proj gets the last Proj from store */
                set_Proj_pred(nmem, store);
@@ -1619,38 +1648,32 @@ static void fix_address_of_parameter_access(be_abi_irg_t *env, ir_entity *value_
        }
 }
 
-#if 1
 /**
  * The start block has no jump, instead it has an initial exec Proj.
  * The backend wants to handle all blocks the same way, so we replace
  * the out cfg edge with a real jump.
  */
-static void fix_start_block(ir_node *block, void *env) {
-       int      *done = env;
-       int      i;
-       ir_node  *start_block;
-       ir_graph *irg;
+static void fix_start_block(ir_graph *irg) {
+       ir_node         *initial_X   = get_irg_initial_exec(irg);
+       ir_node         *start_block = get_irg_start_block(irg);
+       const ir_edge_t *edge;
 
-       /* we processed the start block, return */
-       if (*done)
-               return;
+       assert(is_Proj(initial_X));
 
-       irg         = get_irn_irg(block);
-       start_block = get_irg_start_block(irg);
+       foreach_out_edge(initial_X, edge) {
+               ir_node *block = get_edge_src_irn(edge);
 
-       for (i = get_Block_n_cfgpreds(block) - 1; i >= 0; --i) {
-               ir_node *pred       = get_Block_cfgpred(block, i);
-               ir_node *pred_block = get_nodes_block(pred);
+               if (is_Anchor(block))
+                       continue;
+               if (block != start_block) {
+                       ir_node *jmp = new_r_Jmp(irg, start_block);
 
-               /* ok, we are in the block, having start as cfg predecessor */
-               if (pred_block == start_block) {
-                       ir_node *jump = new_r_Jmp(irg, pred_block);
-                       set_Block_cfgpred(block, i, jump);
-                       *done = 1;
+                       set_Block_cfgpred(block, get_edge_src_pos(edge), jmp);
+                       return;
                }
        }
+       panic("Initial exec has no follow block");
 }
-#endif
 
 /**
  * Modify the irg itself and the frame type.
@@ -1867,8 +1890,7 @@ static void modify_irg(be_abi_irg_t *env)
                                        ir_mode *mode      = get_type_mode(param_type);
                                        ir_mode *load_mode = arg->load_mode;
 
-                                       ir_node *load = new_r_Load(irg, reg_params_bl, new_NoMem(), addr, load_mode);
-                                       set_irn_pinned(load, op_pin_state_floats);
+                                       ir_node *load = new_r_Load(irg, reg_params_bl, new_NoMem(), addr, load_mode, cons_floats);
                                        repl = new_r_Proj(irg, reg_params_bl, load, load_mode, pn_Load_res);
 
                                        if (mode != load_mode) {
@@ -1917,8 +1939,7 @@ static void modify_irg(be_abi_irg_t *env)
        obstack_free(&env->obst, args);
 
        /* handle start block here (place a jump in the block) */
-       i = 0;
-       irg_block_walk_graph(irg, fix_start_block, NULL, &i);
+       fix_start_block(irg);
 }
 
 /** Fix the state inputs of calls that still hang on unknowns */
@@ -2104,9 +2125,8 @@ static void fix_pic_symconsts(ir_node *node, void *data)
                /* we need an extra indirection for global data outside our current
                   module. The loads are always safe and can therefore float
                   and need no memory input */
-               load     = new_r_Load(irg, block, new_NoMem(), add, mode);
+               load     = new_r_Load(irg, block, new_NoMem(), add, mode, cons_floats);
                load_res = new_r_Proj(irg, block, load, mode, pn_Load_res);
-               set_irn_pinned(load, op_pin_state_floats);
 
                set_irn_n(node, i, load_res);
        }