static void check_call (funct_state local, gimple call, bool ipa) { int flags = gimple_call_flags (call); tree callee_t = gimple_call_fndecl (call); bool possibly_throws = stmt_could_throw_p (call); bool possibly_throws_externally = (possibly_throws && stmt_can_throw_external (call)); if (possibly_throws) { unsigned int i; for (i = 0; i < gimple_num_ops (call); i++) if (gimple_op (call, i) && tree_could_throw_p (gimple_op (call, i))) { if (possibly_throws && cfun->can_throw_non_call_exceptions) { if (dump_file) fprintf (dump_file, " operand can throw; looping\n"); local->looping = true; } if (possibly_throws_externally) { if (dump_file) fprintf (dump_file, " operand can throw externally\n"); local->can_throw = true; } } } /* The const and pure flags are set by a variety of places in the compiler (including here). If someone has already set the flags for the callee, (such as for some of the builtins) we will use them, otherwise we will compute our own information. Const and pure functions have less clobber effects than other functions so we process these first. Otherwise if it is a call outside the compilation unit or an indirect call we punt. This leaves local calls which will be processed by following the call graph. */ if (callee_t) { enum pure_const_state_e call_state; bool call_looping; if (special_builtin_state (&call_state, &call_looping, callee_t)) { worse_state (&local->pure_const_state, &local->looping, call_state, call_looping); return; } /* When bad things happen to bad functions, they cannot be const or pure. */ if (setjmp_call_p (callee_t)) { if (dump_file) fprintf (dump_file, " setjmp is not const/pure\n"); local->looping = true; local->pure_const_state = IPA_NEITHER; } if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL) switch (DECL_FUNCTION_CODE (callee_t)) { case BUILT_IN_LONGJMP: case BUILT_IN_NONLOCAL_GOTO: if (dump_file) fprintf (dump_file, " longjmp and nonlocal goto is not const/pure\n"); local->pure_const_state = IPA_NEITHER; local->looping = true; break; default: break; } } /* When not in IPA mode, we can still handle self recursion. */ if (!ipa && callee_t && recursive_call_p (current_function_decl, callee_t)) { if (dump_file) fprintf (dump_file, " Recursive call can loop.\n"); local->looping = true; } /* Either callee is unknown or we are doing local analysis. Look to see if there are any bits available for the callee (such as by declaration or because it is builtin) and process solely on the basis of those bits. */ else if (!ipa) { enum pure_const_state_e call_state; bool call_looping; if (possibly_throws && cfun->can_throw_non_call_exceptions) { if (dump_file) fprintf (dump_file, " can throw; looping\n"); local->looping = true; } if (possibly_throws_externally) { if (dump_file) { fprintf (dump_file, " can throw externally to lp %i\n", lookup_stmt_eh_lp (call)); if (callee_t) fprintf (dump_file, " callee:%s\n", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (callee_t))); } local->can_throw = true; } if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " checking flags for call:"); state_from_flags (&call_state, &call_looping, flags, ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)) || (!flag_exceptions && (flags & ECF_NORETURN))); worse_state (&local->pure_const_state, &local->looping, call_state, call_looping); } /* Direct functions calls are handled by IPA propagation. */ }
static void find_tail_calls (basic_block bb, struct tailcall **ret) { tree ass_var = NULL_TREE, ret_var, func, param; gimple stmt, call = NULL; gimple_stmt_iterator gsi, agsi; bool tail_recursion; struct tailcall *nw; edge e; tree m, a; basic_block abb; size_t idx; tree var; if (!single_succ_p (bb)) return; for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi)) { stmt = gsi_stmt (gsi); /* Ignore labels, returns, clobbers and debug stmts. */ if (gimple_code (stmt) == GIMPLE_LABEL || gimple_code (stmt) == GIMPLE_RETURN || gimple_clobber_p (stmt) || is_gimple_debug (stmt)) continue; /* Check for a call. */ if (is_gimple_call (stmt)) { call = stmt; ass_var = gimple_call_lhs (stmt); break; } /* If the statement references memory or volatile operands, fail. */ if (gimple_references_memory_p (stmt) || gimple_has_volatile_ops (stmt)) return; } if (gsi_end_p (gsi)) { edge_iterator ei; /* Recurse to the predecessors. */ FOR_EACH_EDGE (e, ei, bb->preds) find_tail_calls (e->src, ret); return; } /* If the LHS of our call is not just a simple register, we can't transform this into a tail or sibling call. This situation happens, in (e.g.) "*p = foo()" where foo returns a struct. In this case we won't have a temporary here, but we need to carry out the side effect anyway, so tailcall is impossible. ??? In some situations (when the struct is returned in memory via invisible argument) we could deal with this, e.g. by passing 'p' itself as that argument to foo, but it's too early to do this here, and expand_call() will not handle it anyway. If it ever can, then we need to revisit this here, to allow that situation. */ if (ass_var && !is_gimple_reg (ass_var)) return; /* We found the call, check whether it is suitable. */ tail_recursion = false; func = gimple_call_fndecl (call); if (func && !DECL_BUILT_IN (func) && recursive_call_p (current_function_decl, func)) { tree arg; for (param = DECL_ARGUMENTS (func), idx = 0; param && idx < gimple_call_num_args (call); param = DECL_CHAIN (param), idx ++) { arg = gimple_call_arg (call, idx); if (param != arg) { /* Make sure there are no problems with copying. The parameter have a copyable type and the two arguments must have reasonably equivalent types. The latter requirement could be relaxed if we emitted a suitable type conversion statement. */ if (!is_gimple_reg_type (TREE_TYPE (param)) || !useless_type_conversion_p (TREE_TYPE (param), TREE_TYPE (arg))) break; /* The parameter should be a real operand, so that phi node created for it at the start of the function has the meaning of copying the value. This test implies is_gimple_reg_type from the previous condition, however this one could be relaxed by being more careful with copying the new value of the parameter (emitting appropriate GIMPLE_ASSIGN and updating the virtual operands). */ if (!is_gimple_reg (param)) break; } } if (idx == gimple_call_num_args (call) && !param) tail_recursion = true; } /* Make sure the tail invocation of this function does not refer to local variables. */ FOR_EACH_LOCAL_DECL (cfun, idx, var) { if (TREE_CODE (var) != PARM_DECL && auto_var_in_fn_p (var, cfun->decl) && (ref_maybe_used_by_stmt_p (call, var) || call_may_clobber_ref_p (call, var))) return; } /* Now check the statements after the call. None of them has virtual operands, so they may only depend on the call through its return value. The return value should also be dependent on each of them, since we are running after dce. */ m = NULL_TREE; a = NULL_TREE; abb = bb; agsi = gsi; while (1) { tree tmp_a = NULL_TREE; tree tmp_m = NULL_TREE; gsi_next (&agsi); while (gsi_end_p (agsi)) { ass_var = propagate_through_phis (ass_var, single_succ_edge (abb)); abb = single_succ (abb); agsi = gsi_start_bb (abb); } stmt = gsi_stmt (agsi); if (gimple_code (stmt) == GIMPLE_LABEL) continue; if (gimple_code (stmt) == GIMPLE_RETURN) break; if (gimple_clobber_p (stmt)) continue; if (is_gimple_debug (stmt)) continue; if (gimple_code (stmt) != GIMPLE_ASSIGN) return; /* This is a gimple assign. */ if (! process_assignment (stmt, gsi, &tmp_m, &tmp_a, &ass_var)) return; if (tmp_a) { tree type = TREE_TYPE (tmp_a); if (a) a = fold_build2 (PLUS_EXPR, type, fold_convert (type, a), tmp_a); else a = tmp_a; } if (tmp_m) { tree type = TREE_TYPE (tmp_m); if (m) m = fold_build2 (MULT_EXPR, type, fold_convert (type, m), tmp_m); else m = tmp_m; if (a) a = fold_build2 (MULT_EXPR, type, fold_convert (type, a), tmp_m); } } /* See if this is a tail call we can handle. */ ret_var = gimple_return_retval (stmt); /* We may proceed if there either is no return value, or the return value is identical to the call's return. */ if (ret_var && (ret_var != ass_var)) return; /* If this is not a tail recursive call, we cannot handle addends or multiplicands. */ if (!tail_recursion && (m || a)) return; /* For pointers only allow additions. */ if (m && POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl)))) return; nw = XNEW (struct tailcall); nw->call_gsi = gsi; nw->tail_recursion = tail_recursion; nw->mult = m; nw->add = a; nw->next = *ret; *ret = nw; }