bool is_gimple_condexpr (tree t) { return (is_gimple_val (t) || (COMPARISON_CLASS_P (t) && !tree_could_throw_p (t) && is_gimple_val (TREE_OPERAND (t, 0)) && is_gimple_val (TREE_OPERAND (t, 1)))); }
bool is_gimple_reg_rhs (tree t) { /* If the RHS of the MODIFY_EXPR may throw or make a nonlocal goto and the LHS is a user variable, then we need to introduce a formal temporary. This way the optimizers can determine that the user variable is only modified if evaluation of the RHS does not throw. Don't force a temp of a non-renamable type; the copy could be arbitrarily expensive. Instead we will generate a V_MAY_DEF for the assignment. */ if (is_gimple_reg_type (TREE_TYPE (t)) && ((TREE_CODE (t) == CALL_EXPR && TREE_SIDE_EFFECTS (t)) || tree_could_throw_p (t))) return false; return is_gimple_formal_tmp_rhs (t); }
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. */ }
enum move_pos movement_possibility (tree stmt) { tree lhs, rhs; if (flag_unswitch_loops && TREE_CODE (stmt) == COND_EXPR) { /* If we perform unswitching, force the operands of the invariant condition to be moved out of the loop. */ return MOVE_POSSIBLE; } if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT) return MOVE_IMPOSSIBLE; if (stmt_ends_bb_p (stmt)) return MOVE_IMPOSSIBLE; if (stmt_ann (stmt)->has_volatile_ops) return MOVE_IMPOSSIBLE; lhs = GIMPLE_STMT_OPERAND (stmt, 0); if (TREE_CODE (lhs) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) return MOVE_IMPOSSIBLE; rhs = GIMPLE_STMT_OPERAND (stmt, 1); if (TREE_SIDE_EFFECTS (rhs) || tree_could_throw_p (rhs)) return MOVE_IMPOSSIBLE; if (TREE_CODE (lhs) != SSA_NAME || tree_could_trap_p (rhs)) return MOVE_PRESERVE_EXECUTION; if (get_call_expr_in (stmt)) { /* While pure or const call is guaranteed to have no side effects, we cannot move it arbitrarily. Consider code like char *s = something (); while (1) { if (s) t = strlen (s); else t = 0; } Here the strlen call cannot be moved out of the loop, even though s is invariant. In addition to possibly creating a call with invalid arguments, moving out a function call that is not executed may cause performance regressions in case the call is costly and not executed at all. */ return MOVE_PRESERVE_EXECUTION; } return MOVE_POSSIBLE; }