static funct_state analyze_function (struct cgraph_node *fn, bool ipa) { tree decl = fn->decl; funct_state l; basic_block this_block; l = XCNEW (struct funct_state_d); l->pure_const_state = IPA_CONST; l->state_previously_known = IPA_NEITHER; l->looping_previously_known = true; l->looping = false; l->can_throw = false; state_from_flags (&l->state_previously_known, &l->looping_previously_known, flags_from_decl_or_type (fn->decl), fn->cannot_return_p ()); if (fn->thunk.thunk_p || fn->alias) { /* Thunk gets propagated through, so nothing interesting happens. */ gcc_assert (ipa); return l; } if (dump_file) { fprintf (dump_file, "\n\n local analysis of %s\n ", fn->name ()); } push_cfun (DECL_STRUCT_FUNCTION (decl)); FOR_EACH_BB_FN (this_block, cfun) { gimple_stmt_iterator gsi; struct walk_stmt_info wi; memset (&wi, 0, sizeof (wi)); for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi)) { check_stmt (&gsi, l, ipa); if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw) goto end; } }
static funct_state analyze_function (struct cgraph_node *fn, bool ipa) { tree decl = fn->symbol.decl; funct_state l; basic_block this_block; l = XCNEW (struct funct_state_d); l->pure_const_state = IPA_CONST; l->state_previously_known = IPA_NEITHER; l->looping_previously_known = true; l->looping = false; l->can_throw = false; state_from_flags (&l->state_previously_known, &l->looping_previously_known, flags_from_decl_or_type (fn->symbol.decl), cgraph_node_cannot_return (fn)); if (fn->thunk.thunk_p || fn->symbol.alias) { /* Thunk gets propagated through, so nothing interesting happens. */ gcc_assert (ipa); return l; } if (dump_file) { fprintf (dump_file, "\n\n local analysis of %s\n ", cgraph_node_name (fn)); } push_cfun (DECL_STRUCT_FUNCTION (decl)); FOR_EACH_BB (this_block) { gimple_stmt_iterator gsi; struct walk_stmt_info wi; memset (&wi, 0, sizeof(wi)); for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi)) { check_stmt (&gsi, l, ipa); if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw) goto end; } } end: if (l->pure_const_state != IPA_NEITHER) { /* Const functions cannot have back edges (an indication of possible infinite loop side effect. */ if (mark_dfs_back_edges ()) { /* Preheaders are needed for SCEV to work. Simple latches and recorded exits improve chances that loop will proved to be finite in testcases such as in loop-15.c and loop-24.c */ loop_optimizer_init (LOOPS_HAVE_PREHEADERS | LOOPS_HAVE_SIMPLE_LATCHES | LOOPS_HAVE_RECORDED_EXITS); if (dump_file && (dump_flags & TDF_DETAILS)) flow_loops_dump (dump_file, NULL, 0); if (mark_irreducible_loops ()) { if (dump_file) fprintf (dump_file, " has irreducible loops\n"); l->looping = true; } else { loop_iterator li; struct loop *loop; scev_initialize (); FOR_EACH_LOOP (li, loop, 0) if (!finite_loop_p (loop)) { if (dump_file) fprintf (dump_file, " can not prove finiteness of " "loop %i\n", loop->num); l->looping =true; FOR_EACH_LOOP_BREAK (li); } scev_finalize (); } loop_optimizer_finalize (); } } if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " checking previously known:"); better_state (&l->pure_const_state, &l->looping, l->state_previously_known, l->looping_previously_known); if (TREE_NOTHROW (decl)) l->can_throw = false; pop_cfun (); if (dump_file) { if (l->looping) fprintf (dump_file, "Function is locally looping.\n"); if (l->can_throw) fprintf (dump_file, "Function is locally throwing.\n"); if (l->pure_const_state == IPA_CONST) fprintf (dump_file, "Function is locally const.\n"); if (l->pure_const_state == IPA_PURE) fprintf (dump_file, "Function is locally pure.\n"); } return l; }
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 == current_function_decl) { 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. */ }