static void shrink_wrap_one_built_in_call (gcall *bi_call) { unsigned nconds = 0; auto_vec<gimple *, 12> conds; gen_shrink_wrap_conditions (bi_call, conds, &nconds); gcc_assert (nconds != 0); shrink_wrap_one_built_in_call_with_conds (bi_call, conds, nconds); }
static bool shrink_wrap_one_built_in_call (gimple bi_call) { gimple_stmt_iterator bi_call_bsi; basic_block bi_call_bb, join_tgt_bb, guard_bb, guard_bb0; edge join_tgt_in_edge_from_call, join_tgt_in_edge_fall_thru; edge bi_call_in_edge0, guard_bb_in_edge; unsigned tn_cond_stmts, nconds; unsigned ci; gimple cond_expr = NULL; gimple cond_expr_start; tree bi_call_label_decl; gimple bi_call_label; auto_vec<gimple, 12> conds; gen_shrink_wrap_conditions (bi_call, conds, &nconds); /* This can happen if the condition generator decides it is not beneficial to do the transformation. Just return false and do not do any transformation for the call. */ if (nconds == 0) return false; bi_call_bb = gimple_bb (bi_call); /* Now find the join target bb -- split bi_call_bb if needed. */ if (stmt_ends_bb_p (bi_call)) { /* If the call must be the last in the bb, don't split the block, it could e.g. have EH edges. */ join_tgt_in_edge_from_call = find_fallthru_edge (bi_call_bb->succs); if (join_tgt_in_edge_from_call == NULL) return false; } else join_tgt_in_edge_from_call = split_block (bi_call_bb, bi_call); bi_call_bsi = gsi_for_stmt (bi_call); join_tgt_bb = join_tgt_in_edge_from_call->dest; /* Now it is time to insert the first conditional expression into bi_call_bb and split this bb so that bi_call is shrink-wrapped. */ tn_cond_stmts = conds.length (); cond_expr = NULL; cond_expr_start = conds[0]; for (ci = 0; ci < tn_cond_stmts; ci++) { gimple c = conds[ci]; gcc_assert (c || ci != 0); if (!c) break; gsi_insert_before (&bi_call_bsi, c, GSI_SAME_STMT); cond_expr = c; } nconds--; ci++; gcc_assert (cond_expr && gimple_code (cond_expr) == GIMPLE_COND); /* Now the label. */ bi_call_label_decl = create_artificial_label (gimple_location (bi_call)); bi_call_label = gimple_build_label (bi_call_label_decl); gsi_insert_before (&bi_call_bsi, bi_call_label, GSI_SAME_STMT); bi_call_in_edge0 = split_block (bi_call_bb, cond_expr); bi_call_in_edge0->flags &= ~EDGE_FALLTHRU; bi_call_in_edge0->flags |= EDGE_TRUE_VALUE; guard_bb0 = bi_call_bb; bi_call_bb = bi_call_in_edge0->dest; join_tgt_in_edge_fall_thru = make_edge (guard_bb0, join_tgt_bb, EDGE_FALSE_VALUE); bi_call_in_edge0->probability = REG_BR_PROB_BASE * ERR_PROB; bi_call_in_edge0->count = apply_probability (guard_bb0->count, bi_call_in_edge0->probability); join_tgt_in_edge_fall_thru->probability = inverse_probability (bi_call_in_edge0->probability); join_tgt_in_edge_fall_thru->count = guard_bb0->count - bi_call_in_edge0->count; /* Code generation for the rest of the conditions */ guard_bb = guard_bb0; while (nconds > 0) { unsigned ci0; edge bi_call_in_edge; gimple_stmt_iterator guard_bsi = gsi_for_stmt (cond_expr_start); ci0 = ci; cond_expr_start = conds[ci0]; for (; ci < tn_cond_stmts; ci++) { gimple c = conds[ci]; gcc_assert (c || ci != ci0); if (!c) break; gsi_insert_before (&guard_bsi, c, GSI_SAME_STMT); cond_expr = c; } nconds--; ci++; gcc_assert (cond_expr && gimple_code (cond_expr) == GIMPLE_COND); guard_bb_in_edge = split_block (guard_bb, cond_expr); guard_bb_in_edge->flags &= ~EDGE_FALLTHRU; guard_bb_in_edge->flags |= EDGE_FALSE_VALUE; bi_call_in_edge = make_edge (guard_bb, bi_call_bb, EDGE_TRUE_VALUE); bi_call_in_edge->probability = REG_BR_PROB_BASE * ERR_PROB; bi_call_in_edge->count = apply_probability (guard_bb->count, bi_call_in_edge->probability); guard_bb_in_edge->probability = inverse_probability (bi_call_in_edge->probability); guard_bb_in_edge->count = guard_bb->count - bi_call_in_edge->count; } if (dump_file && (dump_flags & TDF_DETAILS)) { location_t loc; loc = gimple_location (bi_call); fprintf (dump_file, "%s:%d: note: function call is shrink-wrapped" " into error conditions.\n", LOCATION_FILE (loc), LOCATION_LINE (loc)); } return true; }
static void use_internal_fn (gcall *call) { /* We'll be inserting another call with the same arguments after the lhs has been set, so prevent any possible coalescing failure from having both values live at once. See PR 71020. */ replace_abnormal_ssa_names (call); unsigned nconds = 0; auto_vec<gimple *, 12> conds; if (can_test_argument_range (call)) { gen_shrink_wrap_conditions (call, conds, &nconds); gcc_assert (nconds != 0); } else gcc_assert (edom_only_function (call)); internal_fn ifn = replacement_internal_fn (call); gcc_assert (ifn != IFN_LAST); /* Construct the new call, with the same arguments as the original one. */ auto_vec <tree, 16> args; unsigned int nargs = gimple_call_num_args (call); for (unsigned int i = 0; i < nargs; ++i) args.safe_push (gimple_call_arg (call, i)); gcall *new_call = gimple_build_call_internal_vec (ifn, args); gimple_set_location (new_call, gimple_location (call)); gimple_call_set_nothrow (new_call, gimple_call_nothrow_p (call)); /* Transfer the LHS to the new call. */ tree lhs = gimple_call_lhs (call); gimple_call_set_lhs (new_call, lhs); gimple_call_set_lhs (call, NULL_TREE); SSA_NAME_DEF_STMT (lhs) = new_call; /* Insert the new call. */ gimple_stmt_iterator gsi = gsi_for_stmt (call); gsi_insert_before (&gsi, new_call, GSI_SAME_STMT); if (nconds == 0) { /* Skip the call if LHS == LHS. If we reach here, EDOM is the only valid errno value and it is used iff the result is NaN. */ conds.quick_push (gimple_build_cond (EQ_EXPR, lhs, lhs, NULL_TREE, NULL_TREE)); nconds++; /* Try replacing the original call with a direct assignment to errno, via an internal function. */ if (set_edom_supported_p () && !stmt_ends_bb_p (call)) { gimple_stmt_iterator gsi = gsi_for_stmt (call); gcall *new_call = gimple_build_call_internal (IFN_SET_EDOM, 0); gimple_set_vuse (new_call, gimple_vuse (call)); gimple_set_vdef (new_call, gimple_vdef (call)); SSA_NAME_DEF_STMT (gimple_vdef (new_call)) = new_call; gimple_set_location (new_call, gimple_location (call)); gsi_replace (&gsi, new_call, false); call = new_call; } } shrink_wrap_one_built_in_call_with_conds (call, conds, nconds); }