added missing function
[libfirm] / ir / be / beabi.c
index 9651cd9..886321f 100644 (file)
@@ -49,9 +49,10 @@ struct _be_abi_irg_t {
        ir_node             *init_sp;      /**< The node representing the stack pointer
                                                                             at the start of the function. */
 
+       ir_node             *reg_params;
+
        pset                *stack_ops;    /**< Contains all nodes modifying the stack pointer. */
-       pset                *ignore_regs;  /**< Contains all registers which shall be ignored
-                                                                            during register allocation. */
+       pmap                *regs;
 
        int start_block_bias;
 
@@ -148,6 +149,7 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
        const arch_register_t *sp = arch_isa_sp(isa);
        ir_mode *mach_mode        = sp->reg_class->mode;
        struct obstack *obst      = &env->obst;
+       ir_node *no_mem           = get_irg_no_mem(irg);
 
        ir_node *res_proj = NULL;
        int curr_res_proj = -1;
@@ -165,9 +167,13 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
        /* Let the isa fill out the abi description for that call node. */
        arch_isa_get_call_abi(isa, mt, call);
 
+       assert(get_method_variadicity(mt) == variadicity_non_variadic);
+
        /* Insert code to put the stack arguments on the stack. */
-       for(i = get_irn_arity(irn); i >= 0; --i) {
-               if(is_on_stack(call, i)) {
+       /* TODO: Vargargs */
+       for(i = 0, n = get_Call_n_params(irn); i < n; ++i) {
+               be_abi_call_arg_t *arg = get_call_arg(call, 0, i);
+               if(arg && !arg->in_reg) {
                        stack_size += get_type_size_bytes(get_method_param_type(mt, i));
                        obstack_int_grow(obst, i);
                        n_pos++;
@@ -176,7 +182,7 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
        pos = obstack_finish(obst);
 
        /* Collect all arguments which are passed in registers. */
-       for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
+       for(i = 0, n = get_Call_n_params(irn); i < n; ++i) {
                be_abi_call_arg_t *arg = get_call_arg(call, 0, i);
                if(arg && arg->in_reg) {
                        obstack_int_grow(obst, i);
@@ -206,13 +212,14 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
                 * moving the stack pointer along the stack's direction.
                 */
                if(stack_dir < 0 && !do_seq) {
-                       curr_sp = be_new_IncSP(sp, irg, bl, curr_sp, stack_size, be_stack_dir_along);
+                       curr_sp = be_new_IncSP(sp, irg, bl, curr_sp, no_mem, stack_size, be_stack_dir_along);
                        pset_insert_ptr(env->stack_ops, curr_sp);
                }
 
+               assert(mode_is_reference(mach_mode) && "machine mode must be pointer");
                for(i = 0; i < n_pos; ++i) {
                        int p            = pos[i];
-                       ir_node *param   = get_irn_n(irn, p);
+                       ir_node *param   = get_Call_param(irn, p);
                        ir_node *addr    = curr_sp;
                        ir_node *mem     = NULL;
                        type *param_type = get_method_param_type(mt, p);
@@ -220,12 +227,12 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
 
                        /* Make the expression to compute the argument's offset. */
                        if(curr_ofs > 0) {
-                               addr = new_r_Const_long(irg, bl, mach_mode, curr_ofs);
+                               addr = new_r_Const_long(irg, bl, mode_Is, curr_ofs);
                                addr = new_r_Add(irg, bl, curr_sp, addr, mach_mode);
                        }
 
                        /* Insert a store for primitive arguments. */
-                       if(is_Primitive_type(param_type)) {
+                       if(is_atomic_type(param_type)) {
                                mem = new_r_Store(irg, bl, curr_mem, addr, param);
                                mem = new_r_Proj(irg, bl, mem, mode_M, pn_Store_M);
                        }
@@ -248,7 +255,7 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
                        */
                        if(do_seq) {
                                curr_ofs = 0;
-                               curr_sp  = be_new_IncSP(sp, irg, bl, curr_sp, param_size, be_stack_dir_along);
+                               curr_sp  = be_new_IncSP(sp, irg, bl, curr_sp, no_mem, param_size, be_stack_dir_along);
                                curr_mem = mem;
 
                                /*
@@ -270,8 +277,15 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
        }
 
        /* Collect caller save registers */
-       for(i = 0; env->birg->main_env->caller_save[i]; ++i)
-               pset_insert_ptr(caller_save, env->birg->main_env->caller_save[i]);
+       for(i = 0, n = arch_isa_get_n_reg_class(isa); i < n; ++i) {
+               int j;
+               const arch_register_class_t *cls = arch_isa_get_reg_class(isa, 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);
+               }
+       }
 
        /* search the greatest result proj number */
        foreach_out_edge(irn, edge) {
@@ -324,13 +338,14 @@ static void adjust_call(be_abi_irg_t *env, ir_node *irn)
 
                /* Make a Proj for the stack pointer. */
                sp_proj     = new_r_Proj(irg, bl, res_proj, sp->reg_class->mode, curr_res_proj++);
-               last_inc_sp = be_new_IncSP(sp, irg, bl, sp_proj, stack_size, be_stack_dir_against);
+               last_inc_sp = be_new_IncSP(sp, irg, bl, sp_proj, no_mem, stack_size, be_stack_dir_against);
                pset_insert_ptr(env->stack_ops, last_inc_sp);
        }
 
        /* at last make the backend call node and set its register requirements. */
        for(i = 0; i < n_low_args; ++i)
-               obstack_ptr_grow(obst, get_irn_n(irn, low_args[i]));
+               obstack_ptr_grow(obst, get_Call_param(irn, low_args[i]));
+
        in = obstack_finish(obst);
        low_call = be_new_Call(irg, bl, curr_mem, curr_sp, get_Call_ptr(irn), curr_res_proj, n_low_args, in);
        obstack_free(obst, in);
@@ -383,25 +398,125 @@ static void collect_return_walker(ir_node *irn, void *data)
        }
 }
 
+static ir_node *setup_frame(be_abi_irg_t *env)
+{
+       const arch_isa_t *isa = env->birg->main_env->arch_env->isa;
+       const arch_register_t *sp = isa->sp;
+       const arch_register_t *bp = isa->bp;
+       ir_graph *irg      = env->birg->irg;
+       ir_node *bl        = get_irg_start_block(irg);
+       ir_node *no_mem    = get_irg_no_mem(irg);
+       ir_node *old_frame = get_irg_frame(irg);
+       int store_old_fp   = 1;
+       int omit_fp        = env->omit_fp;
+       ir_node *stack     = pmap_get(env->regs, (void *) sp);
+       ir_node *frame     = pmap_get(env->regs, (void *) bp);
+
+       int stack_nr       = get_Proj_proj(stack);
+
+       if(omit_fp) {
+               stack = be_new_IncSP(sp, irg, bl, stack, no_mem, BE_STACK_FRAME_SIZE, be_stack_dir_along);
+               frame = stack;
+       }
+
+       else {
+               if(store_old_fp) {
+                       ir_node *irn;
+
+                       irn   = new_r_Store(irg, bl, get_irg_initial_mem(irg), stack, frame);
+                       irn   = new_r_Proj(irg, bl, irn, mode_M, pn_Store_M);
+                       stack = be_new_IncSP(sp, irg, bl, stack, irn, get_mode_size_bytes(bp->reg_class->mode), be_stack_dir_along);
+               }
+
+               frame = be_new_Copy(bp->reg_class, irg, bl, stack);
+
+               be_node_set_flags(frame, -1, arch_irn_flags_dont_spill);
+               if(env->dedicated_fp) {
+                       be_set_constr_single_reg(frame, -1, bp);
+                       be_node_set_flags(frame, -1, arch_irn_flags_ignore);
+               }
+
+               stack = be_new_IncSP(sp, irg, bl, stack, no_mem, BE_STACK_FRAME_SIZE, be_stack_dir_along);
+       }
+
+       be_node_set_flags(env->reg_params, -(stack_nr + 1), arch_irn_flags_ignore);
+       env->init_sp = stack;
+       set_irg_frame(irg, frame);
+       edges_reroute(old_frame, frame, irg);
+
+       return frame;
+}
+
+static void clearup_frame(be_abi_irg_t *env, ir_node *bl, struct obstack *obst)
+{
+       const arch_isa_t *isa = env->birg->main_env->arch_env->isa;
+       const arch_register_t *sp = isa->sp;
+       const arch_register_t *bp = isa->bp;
+       ir_graph *irg      = env->birg->irg;
+       ir_node *no_mem    = get_irg_no_mem(irg);
+       ir_node *frame     = get_irg_frame(irg);
+       ir_node *stack     = env->init_sp;
+       int store_old_fp   = 1;
+
+       pmap_entry *ent;
+
+
+       if(env->omit_fp) {
+               stack = be_new_IncSP(sp, irg, bl, stack, no_mem, BE_STACK_FRAME_SIZE, be_stack_dir_against);
+       }
+
+       else {
+               stack = be_new_Copy(sp->reg_class, irg, bl, frame);
+
+               if(store_old_fp) {
+                       ir_mode *mode = sp->reg_class->mode;
+                       ir_node *irn;
+
+                       stack = be_new_IncSP(sp, irg, bl, stack, no_mem, get_mode_size_bytes(mode), be_stack_dir_against);
+                       irn   = new_r_Load(irg, bl, no_mem, stack, mode);
+                       irn   = new_r_Proj(irg, bl, irn, mode, pn_Load_res);
+                       frame = be_new_Copy(bp->reg_class, irg, bl, irn);
+               }
+
+               if(env->dedicated_fp) {
+                       be_set_constr_single_reg(frame, -1, bp);
+               }
+
+       }
+
+       pmap_foreach(env->regs, ent) {
+               const arch_register_t *reg = ent->key;
+               ir_node *irn               = ent->value;
+
+               if(reg == sp)
+                       irn = stack;
+               else if(reg == bp)
+                       irn = frame;
+
+               obstack_ptr_grow(obst, irn);
+       }
+}
+
 /**
  * Modify the irg itself and the frame type.
  */
 static void modify_irg(be_abi_irg_t *env)
 {
+       firm_dbg_module_t *dbg    = env->dbg;
        be_abi_call_t *call       = be_abi_call_new();
        const arch_isa_t *isa     = env->birg->main_env->arch_env->isa;
+       const arch_register_t *sp = arch_isa_sp(isa);
        ir_graph *irg             = env->birg->irg;
        ir_node *bl               = get_irg_start_block(irg);
        ir_node *end              = get_irg_end_block(irg);
        ir_node *arg_tuple        = get_irg_args(irg);
+       ir_node *no_mem           = get_irg_no_mem(irg);
        type *method_type         = get_entity_type(get_irg_entity(irg));
        int n_params              = get_method_n_params(method_type);
-       const arch_register_t *sp = arch_isa_sp(isa);
 
-       int max_arg = 0;
-       int reg_params_nr = 0;
-       ir_node *proj_sp = NULL;
-       int arg_offset = 0;
+       int max_arg               = 0;
+       int reg_params_nr         = 0;
+       int arg_offset            = 0;
 
        int i, j, n;
 
@@ -410,10 +525,9 @@ static void modify_irg(be_abi_irg_t *env)
        ir_node **args, **args_repl;
        const ir_edge_t *edge;
 
-       pmap *regs = pmap_create();
        pmap_entry *ent;
 
-       firm_dbg_module_t *dbg = env->dbg;
+       env->regs = pmap_create();
 
        DBG((dbg, LEVEL_1, "introducing abi on %+F\n", irg));
 
@@ -445,7 +559,7 @@ static void modify_irg(be_abi_irg_t *env)
                be_abi_call_arg_t *arg = get_call_arg(call, 0, i);
                if(arg->in_reg) {
                        assert(arg->reg != sp && "cannot use stack pointer as parameter register");
-                       pmap_insert(regs, (void *) arg->reg, NULL);
+                       pmap_insert(env->regs, (void *) arg->reg, NULL);
                        DBG((dbg, LEVEL_2, "\targ #%d -> reg %s\n", i, arg->reg->name));
                }
        }
@@ -456,36 +570,62 @@ static void modify_irg(be_abi_irg_t *env)
                for(j = 0; j < cls->n_regs; ++j) {
                        const arch_register_t *reg = &cls->regs[j];
                        if(arch_register_type_is(reg, callee_save))
-                               pmap_insert(regs, (void *) reg, NULL);
+                               pmap_insert(env->regs, (void *) reg, NULL);
                }
        }
 
-       /* The stack pointer must also be saved but not necessarily be marked as callee save */
-       pmap_insert(regs, (void *) sp, NULL);
-
+       pmap_insert(env->regs, (void *) sp, NULL);
+       pmap_insert(env->regs, (void *) isa->bp, NULL);
        reg_params_bl = get_irg_start_block(irg);
-       reg_params    = be_new_RegParams(irg, reg_params_bl, pmap_count(regs));
+       env->reg_params = reg_params = be_new_RegParams(irg, reg_params_bl, pmap_count(env->regs));
        reg_params_nr = 0;
 
        /*
         * make proj nodes for the callee save registers.
         * memorize them, since Return nodes get those as inputs.
         */
-       for(ent = pmap_first(regs); ent; ent = pmap_next(regs)) {
+       for(ent = pmap_first(env->regs); ent; ent = pmap_next(env->regs)) {
                arch_register_t *reg = ent->key;
+               int pos = -(reg_params_nr + 1);
                ent->value = new_r_Proj(irg, reg_params_bl, reg_params, reg->reg_class->mode, reg_params_nr);
-               be_set_constr_single_reg(reg_params, -(reg_params_nr + 1), reg);
+               be_set_constr_single_reg(reg_params, pos, reg);
+
+               /*
+                * If the register is an ignore register,
+                * The Proj for that register shall also be ignored during register allocation.
+                */
+               if(arch_register_type_is(reg, ignore))
+                       be_node_set_flags(reg_params, pos, arch_irn_flags_ignore);
+
                reg_params_nr++;
 
                DBG((dbg, LEVEL_2, "\tregister save proj #%d -> reg %s\n", reg_params_nr - 1, reg->name));
        }
 
+       /* Insert the code to set up the stack frame */
+       frame_pointer = setup_frame(env);
+
+#if 0
        proj_sp = pmap_get(regs, (void *) sp);
+       proj_bp = pmap_get(regs, (void *) bp);
        assert(proj_sp != NULL && "There must be a Proj for the stack pointer");
+       assert(proj_sp != NULL && "There must be a Proj for the base pointer");
+
+       /* Set the Proj for the stack pointer to ignore. */
+       be_node_set_flags(reg_params, -(get_Proj_proj(proj_sp) + 1), arch_irn_flags_ignore);
+
+       /*
+        * If a frame pointer is needed and the frame pointer is in a dedicated register,
+        * also exclude that from register allocation by setting the corresponding
+        * Proj to ignore.
+        */
+       if(!env->omit_fp && env->dedicated_fp)
+               be_node_set_flags(reg_params, -(get_Proj_proj(proj_bp) + 1), arch_irn_flags_ignore);
+
 
        if(env->omit_fp) {
                /* This is the stack pointer add/sub which allocates the frame. remind it for later fix up. */
-               env->init_sp  = be_new_IncSP(sp, irg, reg_params_bl, proj_sp, 0, be_stack_dir_along);
+               env->init_sp  = be_new_IncSP(sp, irg, reg_params_bl, proj_sp, no_mem, 0, be_stack_dir_along);
                frame_pointer = env->init_sp;
        }
 
@@ -497,6 +637,7 @@ static void modify_irg(be_abi_irg_t *env)
        /* Set the new frame pointer. */
        exchange(get_irg_frame(irg), frame_pointer);
        set_irg_frame(irg, frame_pointer);
+#endif
 
        /* compute the start offset for the stack parameters. */
        {
@@ -538,7 +679,8 @@ static void modify_irg(be_abi_irg_t *env)
                                        if(inc_dir < 0)
                                                arg_offset -= size;
 
-                                       if(is_atomic_type(param_type)) {
+                                       /* For atomic parameters which are actually used, we create a StackParam node. */
+                                       if(is_atomic_type(param_type) && get_irn_n_edges(args[i]) > 0) {
                                                ir_mode *mode                    = get_type_mode(param_type);
                                                const arch_register_class_t *cls = arch_isa_get_reg_class_for_mode(isa, mode);
                                                args_repl[i] = be_new_StackParam(cls, irg, reg_params_bl, mode, frame_pointer, arg_offset);
@@ -571,49 +713,45 @@ static void modify_irg(be_abi_irg_t *env)
                ir_node *irn = get_irn_n(end, i);
 
                if(get_irn_opcode(irn) == iro_Return) {
-                       ir_node *bl = get_nodes_block(irn);
+                       ir_node *bl   = get_nodes_block(irn);
+                       int n_res     = get_Return_n_ress(irn);
+                       pmap *reg_map = pmap_create_ex(n_res);
                        ir_node *ret;
                        int i, n;
                        ir_node **in;
 
                        /* collect all arguments of the return */
-                       for(i = 0, n = get_irn_arity(irn); i < n; ++i)
-                               obstack_ptr_grow(&env->obst, get_irn_n(irn, i));
-
-                       /* Add the Proj nodes representing the caller save registers. */
-                       for(ent = pmap_first(regs); ent; ent = pmap_next(regs), ++n) {
-                               const arch_register_t *reg = ent->key;
-                               ir_node *irn               = ent->value;
-
-                               /*
-                                * If the register is the stack pointer,
-                                * add the fix up code. Either add the size of the stack
-                                * frame if we omitted the frame pointer or move the
-                                * frame pointer back to the stack register.
-                                */
-                               if(reg == sp) {
-                                       if(!env->omit_fp) {
-                                               irn  = be_new_Copy(sp->reg_class, irg, bl, frame_pointer);
-                                               be_set_constr_single_reg(irn, -1, sp);
-                                       }
+                       for(i = 0; i < n_res; ++i) {
+                               ir_node *res           = get_Return_res(irn, i);
+                               be_abi_call_arg_t *arg = get_call_arg(call, 1, i);
 
-                                       else
-                                               irn = be_new_IncSP(sp, irg, bl, proj_sp, 0, be_stack_dir_against);
-                               }
-                               obstack_ptr_grow(&env->obst, irn);
+                               assert(arg->in_reg && "return value must be passed in register");
+                               pmap_insert(reg_map, res, (void *) arg->reg);
+                               obstack_ptr_grow(&env->obst, res);
                        }
 
+                       /* generate the clean up code and add additional parameters to the return. */
+                       clearup_frame(env, bl, &env->obst);
+
                        /* The in array for the new back end return is now ready. */
+                       n   = obstack_object_size(&env->obst) / sizeof(in[0]);
                        in  = obstack_finish(&env->obst);
                        ret = be_new_Return(irg, bl, n, in);
-                       edges_reroute(irn, ret, irg);
+
+                       /* Set the constraints for some arguments of the return. */
+                       for(i = 0; i < n; i++) {
+                               const arch_register_t *reg = pmap_get(reg_map, in[i]);
+                               if(reg != NULL)
+                                       be_set_constr_single_reg(ret, i, reg);
+                       }
+                       exchange(irn, ret);
                        obstack_free(&env->obst, in);
+                       pmap_destroy(reg_map);
                }
        }
 
        obstack_free(&env->obst, args);
        be_abi_call_free(call);
-       pmap_destroy(regs);
 }
 
 static void collect_alloca_walker(ir_node *irn, void *data)
@@ -656,7 +794,7 @@ be_abi_irg_t *be_abi_introduce(be_irg_t *birg)
        for(i = 0; stack_allocs[i] != NULL; ++i)
                implement_stack_alloc(env, stack_allocs[i]);
 
-       irg_walk_graph(env->birg->irg, NULL, adjust_call_walker, &env);
+       irg_walk_graph(env->birg->irg, NULL, adjust_call_walker, env);
        return env;
 }
 
@@ -745,3 +883,10 @@ void be_abi_free(be_abi_irg_t *env)
        obstack_free(&env->obst, NULL);
        free(env);
 }
+
+ir_node *be_abi_get_callee_save_irn(be_abi_irg_t *abi, const arch_register_t *reg)
+{
+       assert(arch_register_type_is(reg, callee_save));
+       assert(pmap_contains(abi->regs, (void *) reg));
+       return pmap_get(abi->regs, (void *) reg);
+}