- inline_method moves copied constants to start block yet
[libfirm] / ir / opt / opt_inline.c
index ac7581a..00fb19c 100644 (file)
@@ -726,8 +726,7 @@ void survive_dce_register_irn(survive_dce_t *sd, ir_node **place) {
  * inlined procedure. The new entities must be in the link field of
  * the entities.
  */
-static INLINE void
-copy_node_inline(ir_node *n, void *env) {
+static void copy_node_inline(ir_node *n, void *env) {
        ir_node *nn;
        ir_type *frame_tp = (ir_type *)env;
 
@@ -744,6 +743,26 @@ copy_node_inline(ir_node *n, void *env) {
        }
 }
 
+/**
+ * Copies new predecessors of old node and move constants to
+ * the Start Block.
+ */
+static void copy_preds_inline(ir_node *n, void *env) {
+       ir_node *nn;
+
+       copy_preds(n, env);
+       nn = skip_Id(get_new_node(n));
+       if (is_irn_constlike(nn)) {
+               /* move Constants into the start block */
+               set_nodes_block(nn, get_irg_start_block(current_ir_graph));
+
+               n = identify_remember(current_ir_graph->value_table, nn);
+               if (nn != n) {
+                       exchange(nn, n);
+               }
+       }
+}
+
 /**
  * Walker: checks if P_value_arg_base is used.
  */
@@ -821,13 +840,14 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
        ir_node             *pre_call;
        ir_node             *post_call, *post_bl;
        ir_node             *in[pn_Start_max];
-       ir_node             *end, *end_bl;
+       ir_node             *end, *end_bl, *block;
        ir_node             **res_pred;
        ir_node             **cf_pred;
+       ir_node             **args_in;
        ir_node             *ret, *phi;
-       int                 arity, n_ret, n_exc, n_res, i, n, j, rem_opt, irn_arity;
+       int                 arity, n_ret, n_exc, n_res, i, n, j, rem_opt, irn_arity, n_params;
        enum exc_mode       exc_handling;
-       ir_type             *called_frame, *curr_frame;
+       ir_type             *called_frame, *curr_frame, *mtp, *ctp;
        ir_entity           *ent;
        ir_graph            *rem, *irg;
        irg_inline_property prop = get_irg_inline_property(called_graph);
@@ -837,35 +857,39 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
 
        ent = get_irg_entity(called_graph);
 
-       /* Do not inline variadic functions. */
-       if (get_method_variadicity(get_entity_type(ent)) == variadicity_variadic) {
-               /* Arg, KR functions are marked as variadic one's, so check further */
-               ir_type *mtp     = get_entity_type(ent);
-               ir_type *ctp     = get_Call_type(call);
-               int     n_params = get_method_n_params(mtp);
-               int     i;
-
-               /* This is too strong, but probably ok. Function calls with a wrong number of
-                  parameters should not be inlined. */
-               if (n_params != get_method_n_params(ctp))
-                       return 0;
-
-               /* check types: for K&R calls, this was not done by the compiler. Again, this is
-                  too strong, but ok for now. */
-               for (i = n_params - 1; i >= 0; --i) {
-                       ir_type *param_tp = get_method_param_type(mtp, i);
-                       ir_type *arg_tp   = get_method_param_type(ctp, i);
+       mtp = get_entity_type(ent);
+       ctp = get_Call_type(call);
+       if (get_method_n_params(mtp) > get_method_n_params(ctp)) {
+               /* this is a bad feature of C: without a prototype, we can can call a function with less
+               parameters than needed. Currently we don't support this, although it would be
+               to use Unknown than. */
+               return 0;
+       }
 
-                       if (param_tp != arg_tp)
+       /* Argh, compiling C has some bad consequences:
+          the call type AND the method type might be different.
+          It is implementation defendant what happens in that case.
+          We support inlining, if the bitsize of the types matches AND
+          the same arithmetic is used. */
+       n_params = get_method_n_params(mtp);
+       for (i = n_params - 1; i >= 0; --i) {
+               ir_type *param_tp = get_method_param_type(mtp, i);
+               ir_type *arg_tp   = get_method_param_type(ctp, i);
+
+               if (param_tp != arg_tp) {
+                       ir_mode *pmode = get_type_mode(param_tp);
+                       ir_mode *amode = get_type_mode(arg_tp);
+
+                       if (pmode == NULL || amode == NULL)
                                return 0;
+                       if (get_mode_size_bits(pmode) != get_mode_size_bits(amode))
+                               return 0;
+                       if (get_mode_arithmetic(pmode) != get_mode_arithmetic(amode))
+                               return 0;
+                       /* otherwise we can simply "reinterpret" the bits */
                }
-               DB((dbg, LEVEL_1, "Inlining allowed for variadic function %+F\n", called_graph));
-               /* types match, fine: when the frame is access, the inliner stops at can_inline() */
        }
 
-       assert(get_method_n_params(get_entity_type(ent)) ==
-              get_method_n_params(get_Call_type(call)));
-
        irg = get_irn_irg(call);
 
        /*
@@ -926,6 +950,21 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
                else            {                exc_handling = exc_no_handler; } /* !Mproj && !Xproj   */
        }
 
+       /* create the argument tuple */
+       NEW_ARR_A(ir_type *, args_in, n_params);
+
+       block = get_nodes_block(call);
+       for (i = n_params - 1; i >= 0; --i) {
+               ir_node *arg      = get_Call_param(call, i);
+               ir_type *param_tp = get_method_param_type(mtp, i);
+               ir_mode *mode     = get_type_mode(param_tp);
+
+               if (mode != get_irn_mode(arg)) {
+                       arg = new_r_Conv(irg, block, arg, mode);
+               }
+               args_in[i] = arg;
+       }
+
        /* --
           the procedure and later replaces the Start node of the called graph.
           Post_call is the old Call node and collects the results of the called
@@ -937,7 +976,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
        in[pn_Start_M]                = get_Call_mem(call);
        in[pn_Start_P_frame_base]     = get_irg_frame(irg);
        in[pn_Start_P_tls]            = get_irg_tls(irg);
-       in[pn_Start_T_args]           = new_Tuple(get_Call_n_params(call), get_Call_param_arr(call));
+       in[pn_Start_T_args]           = new_Tuple(n_params, args_in);
        /* in[pn_Start_P_value_arg_base] = ??? */
        assert(pn_Start_P_value_arg_base == pn_Start_max - 1 && "pn_Start_P_value_arg_base not supported, fix");
        pre_call = new_Tuple(pn_Start_max - 1, in);
@@ -952,7 +991,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
        /* Visited flags in calling irg must be >= flag in called irg.
           Else walker and arity computation will not work. */
        if (get_irg_visited(irg) <= get_irg_visited(called_graph))
-               set_irg_visited(irg, get_irg_visited(called_graph)+1);
+               set_irg_visited(irg, get_irg_visited(called_graph) + 1);
        if (get_irg_block_visited(irg) < get_irg_block_visited(called_graph))
                set_irg_block_visited(irg, get_irg_block_visited(called_graph));
        /* Set pre_call as new Start node in link field of the start node of
@@ -989,7 +1028,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
        /* -- Performing dead node elimination inlines the graph -- */
        /* Copies the nodes to the obstack of current_ir_graph. Updates links to new
           entities. */
-       irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds,
+       irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds_inline,
                 get_irg_frame_type(called_graph));
 
        /* Repair called_graph */
@@ -1303,6 +1342,7 @@ typedef struct {
        int n_callers_orig;      /**< for statistics */
        unsigned got_inline:1;   /**< Set, if at least one call inside this graph was inlined. */
        unsigned local_vars:1;   /**< Set, if a inlined function gets the address of an inlined variable. */
+       unsigned recursive:1;    /**< Set, if this function is self recursive. */
        call_entry *call_head;   /**< The head of the list of all call nodes in this graph. */
        call_entry *call_tail;   /**< The tail of the list of all call nodes in this graph .*/
        unsigned *local_weights; /**< Once allocated, the beneficial weight for transmitting local addresses. */
@@ -1324,6 +1364,7 @@ static inline_irg_env *alloc_inline_irg_env(void) {
        env->n_callers_orig    = 0;
        env->got_inline        = 0;
        env->local_vars        = 0;
+       env->recursive         = 0;
        env->local_weights     = NULL;
        return env;
 }
@@ -1382,6 +1423,8 @@ static void collect_calls2(ir_node *call, void *ctx) {
                        ++callee_env->n_callers;
                        ++callee_env->n_callers_orig;
                }
+               if (callee == current_ir_graph)
+                       x->recursive = 1;
 
                /* link it in the list of possible inlinable entries */
                entry = obstack_alloc(&temp_obst, sizeof(*entry));
@@ -1643,7 +1686,7 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
                                        callee_env = alloc_inline_irg_env();
                                        set_irg_link(copy, callee_env);
 
-                                       assure_cf_loop(irg);
+                                       assure_cf_loop(copy);
                                        wenv.x              = callee_env;
                                        wenv.ignore_callers = 1;
                                        irg_walk_graph(copy, NULL, collect_calls2, &wenv);
@@ -1845,14 +1888,19 @@ static unsigned get_method_local_adress_weight(ir_graph *callee, int pos) {
 }
 
 /**
- * calculate a benefice value for inlining the given call.
+ * Calculate a benefice value for inlining the given call.
+ *
+ * @param call       the call node we have to inspect
+ * @param callee     the called graph
+ * @param local_adr  set after return if an address of a local variable is
+ *                   transmitted as a parameter
  */
 static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local_adr) {
        ir_entity *ent = get_irg_entity(callee);
        ir_node   *frame_ptr;
        ir_type   *mtp;
        int       weight = 0;
-       int       i, n_params;
+       int       i, n_params, all_const;
        unsigned  cc, v;
 
        inline_irg_env *curr_env, *callee_env;
@@ -1881,21 +1929,27 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local
 
        /* constant parameters improve the benefice */
        frame_ptr = get_irg_frame(current_ir_graph);
+       all_const = 1;
        for (i = 0; i < n_params; ++i) {
                ir_node *param = get_Call_param(call, i);
 
-               if (is_Const(param) || is_SymConst(param))
+               if (is_Const(param)) {
                        weight += get_method_param_weight(ent, i);
-               else if (is_Sel(param) && get_Sel_ptr(param) == frame_ptr) {
-                       /*
-                        * An address of a local variable is transmitted. After inlining,
-                        * scalar_replacement might be able to remove the local variable,
-                        * so honor this.
-                        */
-                       v = get_method_local_adress_weight(callee, i);
-                       weight += v;
-                       if (v > 0)
-                               *local_adr = 1;
+               } else {
+                       all_const = 0;
+                       if (is_SymConst(param))
+                               weight += get_method_param_weight(ent, i);
+                       else if (is_Sel(param) && get_Sel_ptr(param) == frame_ptr) {
+                               /*
+                                * An address of a local variable is transmitted. After inlining,
+                                * scalar_replacement might be able to remove the local variable,
+                                * so honor this.
+                                */
+                               v = get_method_local_adress_weight(callee, i);
+                               weight += v;
+                               if (v > 0)
+                                       *local_adr = 1;
+                       }
                }
        }
 
@@ -1912,14 +1966,14 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local
 
        /* reduce the benefice if the current function is already big */
        curr_env = get_irg_link(current_ir_graph);
-       weight -= curr_env->n_nodes / 100;
+       weight -= curr_env->n_nodes / 50;
 
        /* give a bonus for functions with one block */
        if (callee_env->n_blocks == 1)
                weight = weight * 3 / 2;
 
-       /* and one for small functions: we want them to be inlined in mostly every case */
-       else if (callee_env->n_nodes < 20)
+       /* and one for small non-recursive functions: we want them to be inlined in mostly every case */
+       else if (callee_env->n_nodes < 20 && !callee_env->recursive)
                weight += 5000;
 
        /* and finally for leaves: they do not increase the register pressure
@@ -1927,6 +1981,19 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local
        else if (callee_env->n_call_nodes == 0)
                weight += 25;
 
+       /*
+        * Reduce the weight for recursive function IFF not all arguments are const.
+        * inlining recursive functions is rarely good.
+        */
+       if (callee_env->recursive && !all_const)
+               weight -= 500;
+
+       /*
+        * All arguments constant is probably a good sign, give an extra bonus
+        */
+       if (all_const)
+               weight += 100;
+
        return weight;
 }