- }
- phi = new_Phi(n_ret, cf_pred, get_irn_mode(cf_pred[0]));
- res_pred[j] = phi;
- set_irn_link(phi, get_irn_link(post_bl)); /* Conserve Phi-list for further inlinings */
- set_irn_link(post_bl, phi);
- }
- set_Tuple_pred(call, 2, new_Tuple(n_res, res_pred));
- } else {
- set_Tuple_pred(call, 2, new_Bad());
- }
- /* Finally the exception control flow. We need to add a Phi node to
- collect the memory containing the exception objects. Further we need
- to add another block to get a correct representation of this Phi. To
- this block we add a Jmp that resolves into the X output of the Call
- when the Call is turned into a tuple. */
- n_exc = 0;
- for (i = 0; i < arity; i++) {
- ir_node *ret;
- ret = get_irn_n(end_bl, i);
- if (is_fragile_op(skip_Proj(ret)) || (get_irn_op(skip_Proj(ret)) == op_Raise)) {
- cf_pred[n_exc] = ret;
- n_exc++;
- }
- }
- if (n_exc > 0) {
- new_Block(n_exc, cf_pred); /* whatch it: current_block is changed! */
- set_Tuple_pred(call, 1, new_Jmp());
- /* The Phi for the memories with the exception objects */
- n_exc = 0;
- for (i = 0; i < arity; i++) {
- ir_node *ret;
- ret = skip_Proj(get_irn_n(end_bl, i));
- if (get_irn_op(ret) == op_Call) {
- cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 3);
- n_exc++;
- } else if (is_fragile_op(ret)) {
- /* We rely that all cfops have the memory output at the same position. */
- cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 0);
- n_exc++;
- } else if (get_irn_op(ret) == op_Raise) {
- cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 1);
- n_exc++;
- }
- }
- set_Tuple_pred(call, 3, new_Phi(n_exc, cf_pred, mode_M));
- } else {
- set_Tuple_pred(call, 1, new_Bad());
- set_Tuple_pred(call, 3, new_Bad());
- }
- free(res_pred);
- free(cf_pred);
-
- /*** Correct the control flow to the end node.
- If the exception control flow from the Call directly branched to the
- end block we now have the following control flow predecessor pattern:
- ProjX -> Tuple -> Jmp.
- We must remove the Jmp along with it's empty block and add Jmp's
- predecessors as predecessors of this end block. ***/
- /* find the problematic predecessor of the end block. */
- end_bl = get_irg_end_block(current_ir_graph);
- for (i = 0; i < get_Block_n_cfgpreds(end_bl); i++) {
- cf_op = get_Block_cfgpred(end_bl, i);
- if (get_irn_op(cf_op) == op_Proj) {
- cf_op = get_Proj_pred(cf_op);
- if (get_irn_op(cf_op) == op_Tuple) {
- cf_op = get_Tuple_pred(cf_op, 1);
- assert(get_irn_op(cf_op) == op_Jmp);
- break;
- }
- }
- }
- /* repair */
- if (i < get_Block_n_cfgpreds(end_bl)) {
- bl = get_nodes_Block(cf_op);
- arity = get_Block_n_cfgpreds(end_bl) + get_Block_n_cfgpreds(bl) - 1;
- cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
- for (j = 0; j < i; j++)
- cf_pred[j] = get_Block_cfgpred(end_bl, j);
- for (j = j; j < i + get_Block_n_cfgpreds(bl); j++)
- cf_pred[j] = get_Block_cfgpred(bl, j-i);
- for (j = j; j < arity; j++)
- cf_pred[j] = get_Block_cfgpred(end_bl, j-get_Block_n_cfgpreds(bl) +1);
- set_irn_in(end_bl, arity, cf_pred);
- free(cf_pred);
- }
-
- /** Turn cse back on. **/
- set_optimize(rem_opt);
+}
+
+/**
+ * Walker: checks if P_value_arg_base is used.
+ */
+static void find_addr(ir_node *node, void *env) {
+ int *allow_inline = env;
+ if (is_Proj(node) && get_irn_op(get_Proj_pred(node)) == op_Start) {
+ if (get_Proj_proj(node) == pn_Start_P_value_arg_base)
+ *allow_inline = 0;
+ }
+}
+
+/**
+ * Check if we can inline a given call.
+ * Currently, we cannot inline two cases:
+ * - call with compound arguments
+ * - graphs that take the address of a parameter
+ *
+ * check these conditions here
+ */
+static int can_inline(ir_node *call, ir_graph *called_graph) {
+ ir_type *call_type = get_Call_type(call);
+ int params, ress, i, res;
+ assert(is_Method_type(call_type));
+
+ params = get_method_n_params(call_type);
+ ress = get_method_n_ress(call_type);
+
+ /* check parameters for compound arguments */
+ for (i = 0; i < params; ++i) {
+ ir_type *p_type = get_method_param_type(call_type, i);
+
+ if (is_compound_type(p_type))
+ return 0;
+ }
+
+ /* check results for compound arguments */
+ for (i = 0; i < ress; ++i) {
+ ir_type *r_type = get_method_res_type(call_type, i);
+
+ if (is_compound_type(r_type))
+ return 0;
+ }
+
+ res = 1;
+ irg_walk_graph(called_graph, find_addr, NULL, &res);
+
+ return res;
+}
+
+enum exc_mode {
+ exc_handler = 0, /**< There is a handler. */
+ exc_to_end = 1, /**< Branches to End. */
+ exc_no_handler = 2 /**< Exception handling not represented. */
+};
+
+/* Inlines a method at the given call site. */
+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 **res_pred;
+ ir_node **cf_pred;
+ ir_node *ret, *phi;
+ int arity, n_ret, n_exc, n_res, i, j, rem_opt, irn_arity;
+ enum exc_mode exc_handling;
+ ir_type *called_frame;
+ irg_inline_property prop = get_irg_inline_property(called_graph);
+
+ if ( (prop < irg_inline_forced) &&
+ (!get_opt_optimize() || !get_opt_inline() || (prop == irg_inline_forbidden))) return 0;
+
+ /* Do not inline variadic functions. */
+ if (get_method_variadicity(get_entity_type(get_irg_entity(called_graph))) == variadicity_variadic)
+ return 0;
+
+ assert(get_method_n_params(get_entity_type(get_irg_entity(called_graph))) ==
+ get_method_n_params(get_Call_type(call)));
+
+ /*
+ * currently, we cannot inline two cases:
+ * - call with compound arguments
+ * - graphs that take the address of a parameter
+ */
+ if (! can_inline(call, called_graph))
+ return 0;
+
+ /* -- Turn off optimizations, this can cause problems when allocating new nodes. -- */
+ rem_opt = get_opt_optimize();
+ set_optimize(0);
+
+ /* Handle graph state */
+ assert(get_irg_phase_state(current_ir_graph) != phase_building);
+ assert(get_irg_pinned(current_ir_graph) == op_pin_state_pinned);
+ assert(get_irg_pinned(called_graph) == op_pin_state_pinned);
+ set_irg_outs_inconsistent(current_ir_graph);
+ set_irg_extblk_inconsistent(current_ir_graph);
+ set_irg_doms_inconsistent(current_ir_graph);
+ set_irg_loopinfo_inconsistent(current_ir_graph);
+ set_irg_callee_info_state(current_ir_graph, irg_callee_info_inconsistent);
+
+ /* -- Check preconditions -- */
+ assert(is_Call(call));
+ /* @@@ does not work for InterfaceIII.java after cgana
+ assert(get_Call_type(call) == get_entity_type(get_irg_entity(called_graph)));
+ assert(smaller_type(get_entity_type(get_irg_entity(called_graph)),
+ get_Call_type(call)));
+ */
+ if (called_graph == current_ir_graph) {
+ set_optimize(rem_opt);
+ return 0;
+ }
+
+ /* here we know we WILL inline, so inform the statistics */
+ hook_inline(call, called_graph);
+
+ /* -- Decide how to handle exception control flow: Is there a handler
+ for the Call node, or do we branch directly to End on an exception?
+ exc_handling:
+ 0 There is a handler.
+ 1 Branches to End.
+ 2 Exception handling not represented in Firm. -- */
+ {
+ ir_node *proj, *Mproj = NULL, *Xproj = NULL;
+ for (proj = get_irn_link(call); proj; proj = get_irn_link(proj)) {
+ long proj_nr = get_Proj_proj(proj);
+ if (proj_nr == pn_Call_X_except) Xproj = proj;
+ if (proj_nr == pn_Call_M_except) Mproj = proj;
+ }
+ if (Mproj) { assert(Xproj); exc_handling = exc_handler; } /* Mproj */
+ else if (Xproj) { exc_handling = exc_to_end; } /* !Mproj && Xproj */
+ else { exc_handling = exc_no_handler; } /* !Mproj && !Xproj */
+ }
+
+ /* --
+ 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
+ graph. Both will end up being a tuple. -- */
+ post_bl = get_nodes_block(call);
+ set_irg_current_block(current_ir_graph, post_bl);
+ /* XxMxPxPxPxT of Start + parameter of Call */
+ in[pn_Start_X_initial_exec] = new_Jmp();
+ in[pn_Start_M] = get_Call_mem(call);
+ in[pn_Start_P_frame_base] = get_irg_frame(current_ir_graph);
+ in[pn_Start_P_globals] = get_irg_globals(current_ir_graph);
+ in[pn_Start_P_tls] = get_irg_tls(current_ir_graph);
+ in[pn_Start_T_args] = new_Tuple(get_Call_n_params(call), get_Call_param_arr(call));
+ /* 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);
+ post_call = call;
+
+ /* --
+ The new block gets the ins of the old block, pre_call and all its
+ predecessors and all Phi nodes. -- */
+ part_block(pre_call);
+
+ /* -- Prepare state for dead node elimination -- */
+ /* Visited flags in calling irg must be >= flag in called irg.
+ Else walker and arity computation will not work. */
+ if (get_irg_visited(current_ir_graph) <= get_irg_visited(called_graph))
+ set_irg_visited(current_ir_graph, get_irg_visited(called_graph)+1);
+ if (get_irg_block_visited(current_ir_graph)< get_irg_block_visited(called_graph))
+ set_irg_block_visited(current_ir_graph, get_irg_block_visited(called_graph));
+ /* Set pre_call as new Start node in link field of the start node of
+ calling graph and pre_calls block as new block for the start block
+ of calling graph.
+ Further mark these nodes so that they are not visited by the
+ copying. */
+ set_irn_link(get_irg_start(called_graph), pre_call);
+ set_irn_visited(get_irg_start(called_graph), get_irg_visited(current_ir_graph));
+ set_irn_link(get_irg_start_block(called_graph), get_nodes_block(pre_call));
+ set_irn_visited(get_irg_start_block(called_graph), get_irg_visited(current_ir_graph));
+ set_irn_link(get_irg_bad(called_graph), get_irg_bad(current_ir_graph));
+ set_irn_visited(get_irg_bad(called_graph), get_irg_visited(current_ir_graph));
+
+ /* Initialize for compaction of in arrays */
+ inc_irg_block_visited(current_ir_graph);
+
+ /* -- Replicate local entities of the called_graph -- */
+ /* copy the entities. */
+ called_frame = get_irg_frame_type(called_graph);
+ for (i = 0; i < get_class_n_members(called_frame); i++) {
+ ir_entity *new_ent, *old_ent;
+ old_ent = get_class_member(called_frame, i);
+ new_ent = copy_entity_own(old_ent, get_cur_frame_type());
+ set_entity_link(old_ent, new_ent);
+ }
+
+ /* visited is > than that of called graph. With this trick visited will
+ remain unchanged so that an outer walker, e.g., searching the call nodes
+ to inline, calling this inline will not visit the inlined nodes. */
+ set_irg_visited(current_ir_graph, get_irg_visited(current_ir_graph)-1);
+
+ /* -- 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,
+ get_irg_frame_type(called_graph));
+
+ /* Repair called_graph */
+ set_irg_visited(called_graph, get_irg_visited(current_ir_graph));
+ set_irg_block_visited(called_graph, get_irg_block_visited(current_ir_graph));
+ set_Block_block_visited(get_irg_start_block(called_graph), 0);
+
+ /* -- Merge the end of the inlined procedure with the call site -- */
+ /* We will turn the old Call node into a Tuple with the following
+ predecessors:
+ -1: Block of Tuple.
+ 0: Phi of all Memories of Return statements.
+ 1: Jmp from new Block that merges the control flow from all exception
+ predecessors of the old end block.
+ 2: Tuple of all arguments.
+ 3: Phi of Exception memories.
+ In case the old Call directly branches to End on an exception we don't
+ need the block merging all exceptions nor the Phi of the exception
+ memories.
+ */
+
+ /* -- Precompute some values -- */
+ end_bl = get_new_node(get_irg_end_block(called_graph));
+ end = get_new_node(get_irg_end(called_graph));
+ arity = get_irn_arity(end_bl); /* arity = n_exc + n_ret */
+ n_res = get_method_n_ress(get_Call_type(call));
+
+ res_pred = xmalloc(n_res * sizeof(*res_pred));
+ cf_pred = xmalloc(arity * sizeof(*res_pred));
+
+ set_irg_current_block(current_ir_graph, post_bl); /* just to make sure */
+
+ /* -- archive keepalives -- */
+ irn_arity = get_irn_arity(end);
+ for (i = 0; i < irn_arity; i++) {
+ ir_node *ka = get_End_keepalive(end, i);
+ if (! is_Bad(ka))
+ add_End_keepalive(get_irg_end(current_ir_graph), ka);
+ }
+
+ /* The new end node will die. We need not free as the in array is on the obstack:
+ copy_node() only generated 'D' arrays. */
+
+ /* -- Replace Return nodes by Jump nodes. -- */
+ n_ret = 0;
+ for (i = 0; i < arity; i++) {
+ ir_node *ret;
+ ret = get_irn_n(end_bl, i);
+ if (is_Return(ret)) {
+ cf_pred[n_ret] = new_r_Jmp(current_ir_graph, get_nodes_block(ret));
+ n_ret++;
+ }
+ }
+ set_irn_in(post_bl, n_ret, cf_pred);
+
+ /* -- Build a Tuple for all results of the method.
+ Add Phi node if there was more than one Return. -- */
+ turn_into_tuple(post_call, pn_Call_max);
+ /* First the Memory-Phi */
+ n_ret = 0;
+ for (i = 0; i < arity; i++) {
+ ret = get_irn_n(end_bl, i);
+ if (is_Return(ret)) {
+ cf_pred[n_ret] = get_Return_mem(ret);
+ n_ret++;
+ }
+ }
+ phi = new_Phi(n_ret, cf_pred, mode_M);
+ set_Tuple_pred(call, pn_Call_M_regular, phi);
+ /* Conserve Phi-list for further inlinings -- but might be optimized */
+ if (get_nodes_block(phi) == post_bl) {
+ set_irn_link(phi, get_irn_link(post_bl));
+ set_irn_link(post_bl, phi);
+ }
+ /* Now the real results */
+ if (n_res > 0) {
+ for (j = 0; j < n_res; j++) {
+ n_ret = 0;
+ for (i = 0; i < arity; i++) {
+ ret = get_irn_n(end_bl, i);
+ if (get_irn_op(ret) == op_Return) {
+ cf_pred[n_ret] = get_Return_res(ret, j);
+ n_ret++;
+ }
+ }
+ if (n_ret > 0)
+ phi = new_Phi(n_ret, cf_pred, get_irn_mode(cf_pred[0]));
+ else
+ phi = new_Bad();
+ res_pred[j] = phi;
+ /* Conserve Phi-list for further inlinings -- but might be optimized */
+ if (get_nodes_block(phi) == post_bl) {
+ set_irn_link(phi, get_irn_link(post_bl));
+ set_irn_link(post_bl, phi);
+ }
+ }
+ set_Tuple_pred(call, pn_Call_T_result, new_Tuple(n_res, res_pred));
+ } else {
+ set_Tuple_pred(call, pn_Call_T_result, new_Bad());
+ }
+ /* handle the regular call */
+ set_Tuple_pred(call, pn_Call_X_regular, new_Jmp());
+
+ /* For now, we cannot inline calls with value_base */
+ set_Tuple_pred(call, pn_Call_P_value_res_base, new_Bad());
+
+ /* Finally the exception control flow.
+ We have two (three) possible situations:
+ First if the Call branches to an exception handler: We need to add a Phi node to
+ collect the memory containing the exception objects. Further we need
+ to add another block to get a correct representation of this Phi. To
+ this block we add a Jmp that resolves into the X output of the Call
+ when the Call is turned into a tuple.
+ Second the Call branches to End, the exception is not handled. Just
+ add all inlined exception branches to the End node.
+ Third: there is no Exception edge at all. Handle as case two. */
+ if (exc_handling == exc_handler) {
+ n_exc = 0;
+ for (i = 0; i < arity; i++) {
+ ir_node *ret, *irn;
+ ret = get_irn_n(end_bl, i);
+ irn = skip_Proj(ret);
+ if (is_fragile_op(irn) || is_Raise(irn)) {
+ cf_pred[n_exc] = ret;
+ ++n_exc;
+ }
+ }
+ if (n_exc > 0) {
+ new_Block(n_exc, cf_pred); /* watch it: current_block is changed! */
+ set_Tuple_pred(call, pn_Call_X_except, new_Jmp());
+ /* The Phi for the memories with the exception objects */
+ n_exc = 0;
+ for (i = 0; i < arity; i++) {
+ ir_node *ret;
+ ret = skip_Proj(get_irn_n(end_bl, i));
+ if (is_Call(ret)) {
+ cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_block(ret), ret, mode_M, 3);
+ n_exc++;
+ } else if (is_fragile_op(ret)) {
+ /* We rely that all cfops have the memory output at the same position. */
+ cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_block(ret), ret, mode_M, 0);
+ n_exc++;
+ } else if (get_irn_op(ret) == op_Raise) {
+ cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_block(ret), ret, mode_M, 1);
+ n_exc++;
+ }
+ }
+ set_Tuple_pred(call, pn_Call_M_except, new_Phi(n_exc, cf_pred, mode_M));
+ } else {
+ set_Tuple_pred(call, pn_Call_X_except, new_Bad());
+ set_Tuple_pred(call, pn_Call_M_except, new_Bad());
+ }
+ } else {
+ ir_node *main_end_bl;
+ int main_end_bl_arity;
+ ir_node **end_preds;
+
+ /* assert(exc_handling == 1 || no exceptions. ) */
+ n_exc = 0;
+ for (i = 0; i < arity; i++) {
+ ir_node *ret = get_irn_n(end_bl, i);
+ ir_node *irn = skip_Proj(ret);
+
+ if (is_fragile_op(irn) || (get_irn_op(irn) == op_Raise)) {
+ cf_pred[n_exc] = ret;
+ n_exc++;
+ }
+ }
+ main_end_bl = get_irg_end_block(current_ir_graph);
+ main_end_bl_arity = get_irn_arity(main_end_bl);
+ end_preds = xmalloc((n_exc + main_end_bl_arity) * sizeof(*end_preds));
+
+ for (i = 0; i < main_end_bl_arity; ++i)
+ end_preds[i] = get_irn_n(main_end_bl, i);
+ for (i = 0; i < n_exc; ++i)
+ end_preds[main_end_bl_arity + i] = cf_pred[i];
+ set_irn_in(main_end_bl, n_exc + main_end_bl_arity, end_preds);
+ set_Tuple_pred(call, pn_Call_X_except, new_Bad());
+ set_Tuple_pred(call, pn_Call_M_except, new_Bad());
+ free(end_preds);
+ }
+ free(res_pred);
+ free(cf_pred);
+
+ /* -- Turn CSE back on. -- */
+ set_optimize(rem_opt);
+
+ return 1;