wide_int min, max;
int_range_max r;
- get_global_range_query ()->range_of_expr (r, bound);
+ get_range_query (cfun)->range_of_expr (r, bound,
+ currently_expanding_gimple_stmt);
if (r.varying_p () || r.undefined_p ())
return NULL_RTX;
min = r.lower_bound ();
{
int_range_max r;
tree tmin, tmax;
- get_global_range_query ()->range_of_expr (r, len);
+ gimple *cg = currently_expanding_gimple_stmt;
+ get_range_query (cfun)->range_of_expr (r, len, cg);
range_type = get_legacy_range (r, tmin, tmax);
if (range_type != VR_UNDEFINED)
{
#include "output.h"
#include "builtins.h"
#include "opts.h"
+#include "gimple-range.h"
/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
}
}
+/* Temporary storage for BB_HEAD and BB_END of bbs until they are converted
+ to BB_RTL. */
+static vec<std::pair <rtx_insn *, rtx_insn *>> head_end_for_bb;
+
/* Maps the blocks that do not contain tree labels to rtx labels. */
static hash_map<basic_block, rtx_code_label *> *lab_rtx_for_bb;
/* Returns the label_rtx expression for a label starting basic block BB. */
static rtx_code_label *
-label_rtx_for_bb (basic_block bb ATTRIBUTE_UNUSED)
+label_rtx_for_bb (basic_block bb)
{
if (bb->flags & BB_RTL)
return block_label (bb);
+ if ((unsigned) bb->index < head_end_for_bb.length ()
+ && head_end_for_bb[bb->index].first)
+ {
+ if (!LABEL_P (head_end_for_bb[bb->index].first))
+ {
+ head_end_for_bb[bb->index].first
+ = emit_label_before (gen_label_rtx (),
+ head_end_for_bb[bb->index].first);
+ }
+ return as_a <rtx_code_label *> (head_end_for_bb[bb->index].first);
+ }
+
rtx_code_label **elt = lab_rtx_for_bb->get (bb);
if (elt)
return *elt;
}
+/* Wrapper around remove_edge during expansion. */
+
+void
+expand_remove_edge (edge e)
+{
+ if (current_ir_type () != IR_GIMPLE
+ && (e->dest->flags & BB_RTL) == 0
+ && !gimple_seq_empty_p (phi_nodes (e->dest)))
+ remove_phi_args (e);
+ remove_edge (e);
+}
+
+
/* A subroutine of expand_gimple_cond. Given E, a fallthrough edge
of a basic block where we just expanded the conditional at the end,
possibly clean up the CFG and instruction sequence. LAST is the
if (BARRIER_P (get_last_insn ()))
{
rtx_insn *insn;
- remove_edge (e);
+ expand_remove_edge (e);
/* Now, we have a single successor block, if we have insns to
insert on the remaining edge we potentially will insert
it at the end of this block (if the dest block isn't feasible)
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
set_curr_insn_location (gimple_location (stmt));
- /* These flags have no purpose in RTL land. */
- true_edge->flags &= ~EDGE_TRUE_VALUE;
- false_edge->flags &= ~EDGE_FALSE_VALUE;
-
/* We can either have a pure conditional jump with one fallthru edge or
two-way jump that needs to be decomposed into two basic blocks. */
if (false_edge->dest == bb->next_bb)
set_curr_insn_location (false_edge->goto_locus);
emit_jump (label_rtx_for_bb (false_edge->dest));
- BB_END (bb) = last;
- if (BARRIER_P (BB_END (bb)))
- BB_END (bb) = PREV_INSN (BB_END (bb));
- update_bb_for_insn (bb);
+ head_end_for_bb[bb->index].second = last;
+ if (BARRIER_P (head_end_for_bb[bb->index].second))
+ head_end_for_bb[bb->index].second
+ = PREV_INSN (head_end_for_bb[bb->index].second);
+ update_bb_for_insn_chain (head_end_for_bb[bb->index].first,
+ head_end_for_bb[bb->index].second, bb);
new_bb = create_basic_block (NEXT_INSN (last), get_last_insn (), bb);
dest = false_edge->dest;
if (e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
e->dest->count -= e->count ();
probability += e->probability;
- remove_edge (e);
+ expand_remove_edge (e);
}
else
ei_next (&ei);
e = make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), EDGE_ABNORMAL
| EDGE_SIBCALL);
e->probability = probability;
- BB_END (bb) = last;
- update_bb_for_insn (bb);
+ head_end_for_bb[bb->index].second = last;
+ update_bb_for_insn_chain (head_end_for_bb[bb->index].first,
+ head_end_for_bb[bb->index].second, bb);
if (NEXT_INSN (last))
{
expand_gimple_basic_block (basic_block bb, bool disable_tail_calls)
{
gimple_stmt_iterator gsi;
- gimple_seq stmts;
gimple *stmt = NULL;
rtx_note *note = NULL;
rtx_insn *last;
access the BB sequence directly. */
if (optimize)
reorder_operands (bb);
- stmts = bb_seq (bb);
- bb->il.gimple.seq = NULL;
- bb->il.gimple.phi_nodes = NULL;
rtl_profile_for_bb (bb);
- init_rtl_bb_info (bb);
- bb->flags |= BB_RTL;
/* Remove the RETURN_EXPR if we may fall though to the exit
instead. */
- gsi = gsi_last (stmts);
- if (!gsi_end_p (gsi)
- && gimple_code (gsi_stmt (gsi)) == GIMPLE_RETURN)
+ gsi = gsi_last_bb (bb);
+ if (!gsi_end_p (gsi) && gimple_code (gsi_stmt (gsi)) == GIMPLE_RETURN)
{
greturn *ret_stmt = as_a <greturn *> (gsi_stmt (gsi));
}
}
- gsi = gsi_start (stmts);
+ gsi = gsi_start_bb (bb);
if (!gsi_end_p (gsi))
{
stmt = gsi_stmt (gsi);
}
rtx_code_label **elt = lab_rtx_for_bb->get (bb);
+ if ((unsigned) bb->index >= head_end_for_bb.length ())
+ head_end_for_bb.safe_grow_cleared (bb->index + 1);
if (stmt || elt)
{
if (elt)
emit_label (*elt);
- BB_HEAD (bb) = NEXT_INSN (last);
- if (NOTE_P (BB_HEAD (bb)))
- BB_HEAD (bb) = NEXT_INSN (BB_HEAD (bb));
- gcc_assert (LABEL_P (BB_HEAD (bb)));
- note = emit_note_after (NOTE_INSN_BASIC_BLOCK, BB_HEAD (bb));
+ head_end_for_bb[bb->index].first = NEXT_INSN (last);
+ if (NOTE_P (head_end_for_bb[bb->index].first))
+ head_end_for_bb[bb->index].first
+ = NEXT_INSN (head_end_for_bb[bb->index].first);
+ gcc_assert (LABEL_P (head_end_for_bb[bb->index].first));
+ note = emit_note_after (NOTE_INSN_BASIC_BLOCK,
+ head_end_for_bb[bb->index].first);
maybe_dump_rtl_for_gimple_stmt (stmt, last);
}
else
- BB_HEAD (bb) = note = emit_note (NOTE_INSN_BASIC_BLOCK);
+ head_end_for_bb[bb->index].first = note = emit_note (NOTE_INSN_BASIC_BLOCK);
if (note)
NOTE_BASIC_BLOCK (note) = bb;
last = PREV_INSN (PREV_INSN (last));
if (BARRIER_P (last))
last = PREV_INSN (last);
- BB_END (bb) = last;
+ head_end_for_bb[bb->index].second = last;
- update_bb_for_insn (bb);
+ update_bb_for_insn_chain (head_end_for_bb[bb->index].first,
+ head_end_for_bb[bb->index].second, bb);
return bb;
}
>= param_max_debug_marker_count)
cfun->debug_nonbind_markers = false;
+ enable_ranger (fun);
lab_rtx_for_bb = new hash_map<basic_block, rtx_code_label *>;
+ head_end_for_bb.create (last_basic_block_for_fn (fun));
FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR_FOR_FN (fun),
next_bb)
bb = expand_gimple_basic_block (bb, var_ret_seq != NULL_RTX);
+ disable_ranger (fun);
+ FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR_FOR_FN (fun),
+ next_bb)
+ {
+ if ((bb->flags & BB_RTL) == 0)
+ {
+ bb->il.gimple.seq = NULL;
+ bb->il.gimple.phi_nodes = NULL;
+ init_rtl_bb_info (bb);
+ bb->flags |= BB_RTL;
+ BB_HEAD (bb) = head_end_for_bb[bb->index].first;
+ BB_END (bb) = head_end_for_bb[bb->index].second;
+ }
+ /* These flags have no purpose in RTL land. */
+ if (EDGE_COUNT (bb->succs) == 2)
+ {
+ EDGE_SUCC (bb, 0)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+ EDGE_SUCC (bb, 1)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+ }
+ else if (single_succ_p (bb))
+ single_succ_edge (bb)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+ }
+ head_end_for_bb.release ();
if (MAY_HAVE_DEBUG_BIND_INSNS)
expand_debug_locations ();
extern tree gimple_assign_rhs_to_tree (gimple *);
extern HOST_WIDE_INT estimated_stack_frame_size (struct cgraph_node *);
+extern void expand_remove_edge (edge);
extern void set_parm_rtl (tree, rtx);
The insn chain range is inclusive
(i.e. both BEGIN and END will be updated. */
-static void
+void
update_bb_for_insn_chain (rtx_insn *begin, rtx_insn *end, basic_block bb)
{
rtx_insn *insn;
extern void compute_bb_for_insn (void);
extern void free_bb_for_insn (void);
extern rtx_insn *entry_of_function (void);
+extern void update_bb_for_insn_chain (rtx_insn *, rtx_insn *, basic_block);
extern void update_bb_for_insn (basic_block);
extern bool contains_no_active_insn_p (const_basic_block);
extern bool forwarder_block_p (const_basic_block);
|| code == CEIL_MOD_EXPR || code == ROUND_MOD_EXPR);
if (SCALAR_INT_MODE_P (mode)
&& optimize >= 2
- && get_range_pos_neg (treeop0) == 1
- && get_range_pos_neg (treeop1) == 1)
+ && get_range_pos_neg (treeop0, currently_expanding_gimple_stmt) == 1
+ && get_range_pos_neg (treeop1, currently_expanding_gimple_stmt) == 1)
{
/* If both arguments are known to be positive when interpreted
as signed, we can expand it as both signed and unsigned
/* ??? internal call expansion doesn't follow the usual API
of returning the destination RTX and being passed a desired
target. */
+ if (modifier == EXPAND_WRITE)
+ return DECL_RTL (SSA_NAME_VAR (exp));
rtx dest = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
tree tmplhs = make_tree (TREE_TYPE (exp), dest);
- gimple_call_set_lhs (g, tmplhs);
+ tree var_or_id = SSA_NAME_VAR (exp);
+ if (!var_or_id)
+ var_or_id = SSA_NAME_IDENTIFIER (exp);
+ SET_SSA_NAME_VAR_OR_IDENTIFIER (exp, tmplhs);
expand_internal_call (as_a <gcall *> (g));
- gimple_call_set_lhs (g, exp);
+ SET_SSA_NAME_VAR_OR_IDENTIFIER (exp, var_or_id);
return dest;
}
|| integer_zerop (*arg1)
/* If c is known to be non-negative, modulo will be expanded as unsigned
modulo. */
- || get_range_pos_neg (treeop0) == 1)
+ || get_range_pos_neg (treeop0, currently_expanding_gimple_stmt) == 1)
return code;
/* x % c == d where d < 0 && d <= -c should be always false. */
/* If both operands are known to have the sign bit clear, handle
even the signed modulo case as unsigned. treeop1 is always
positive >= 2, checked above. */
- if (!TYPE_UNSIGNED (type) && get_range_pos_neg (treeop0) != 1)
+ if (!TYPE_UNSIGNED (type)
+ && get_range_pos_neg (treeop0, currently_expanding_gimple_stmt) != 1)
sgn = SIGNED;
if (!TYPE_UNSIGNED (type))
#include "tree-cfg.h"
#include "gimple-range.h"
#include "value-range-storage.h"
+#include "rtl.h"
// If there is a range control statement at the end of block BB, return it.
// Otherwise return NULL.
gimple *
gimple_outgoing_range_stmt_p (basic_block bb)
{
+ if (bb->flags & BB_RTL)
+ return NULL;
gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb);
if (!gsi_end_p (gsi))
{
slot = m_range_allocator->clone (case_range);
}
+ if (default_edge == NULL)
+ {
+ /* During expansion the default edge could have been removed
+ if the default is unreachable. */
+ gcc_assert (currently_expanding_to_rtl);
+ return;
+ }
+
vrange_storage *&slot = m_edge_table->get_or_insert (default_edge, &existed);
// This should be the first call into this switch.
gcc_checking_assert (!existed);
// If this is not the definition block, get the range on the last stmt in
// the block... if there is one.
if (def_bb != bb)
- s = last_nondebug_stmt (bb);
+ {
+ if (bb->flags & BB_RTL)
+ s = NULL;
+ else
+ s = last_nondebug_stmt (bb);
+ }
// If there is no statement provided, get the range_on_entry for this block.
if (s)
range_of_expr (r, name, s);
{
if (TYPE_UNSIGNED (TREE_TYPE (arg)))
sign = UNSIGNED;
- else if (sign == UNSIGNED && get_range_pos_neg (arg) != 1)
+ else if (sign == UNSIGNED
+ && (get_range_pos_neg (arg,
+ currently_expanding_gimple_stmt)
+ != 1))
return prec + (orig_sign != sign);
prec = TYPE_PRECISION (TREE_TYPE (arg));
}
if (TREE_CODE (arg) != SSA_NAME)
return prec + (orig_sign != sign);
int_range_max r;
- while (!get_global_range_query ()->range_of_expr (r, arg)
+ gimple *cg = currently_expanding_gimple_stmt;
+ while (!get_range_query (cfun)->range_of_expr (r, arg, cg)
|| r.varying_p ()
|| r.undefined_p ())
{
{
if (TYPE_UNSIGNED (TREE_TYPE (arg)))
sign = UNSIGNED;
- else if (sign == UNSIGNED && get_range_pos_neg (arg) != 1)
+ else if (sign == UNSIGNED
+ && get_range_pos_neg (arg, g) != 1)
return prec + (orig_sign != sign);
prec = TYPE_PRECISION (TREE_TYPE (arg));
}
unsigned. */
res = expand_binop (mode, sub_optab, op0, op1, NULL_RTX, false,
OPTAB_LIB_WIDEN);
- int pos_neg = get_range_pos_neg (arg0);
+ int pos_neg = get_range_pos_neg (arg0, currently_expanding_gimple_stmt);
if (pos_neg == 2)
/* If ARG0 is known to be always negative, this is always overflow. */
emit_jump (do_error);
unsigned. */
res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab,
op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN);
- int pos_neg = get_range_pos_neg (arg1);
+ int pos_neg = get_range_pos_neg (arg1, currently_expanding_gimple_stmt);
if (code == PLUS_EXPR)
{
- int pos_neg0 = get_range_pos_neg (arg0);
+ int pos_neg0 = get_range_pos_neg (arg0,
+ currently_expanding_gimple_stmt);
if (pos_neg0 != 3 && pos_neg == 3)
{
std::swap (op0, op1);
the second operand, as subtraction is not commutative) is always
non-negative or always negative, we can do just one comparison
and conditional jump. */
- int pos_neg = get_range_pos_neg (arg1);
+ int pos_neg = get_range_pos_neg (arg1, currently_expanding_gimple_stmt);
if (code == PLUS_EXPR)
{
- int pos_neg0 = get_range_pos_neg (arg0);
+ int pos_neg0 = get_range_pos_neg (arg0,
+ currently_expanding_gimple_stmt);
if (pos_neg0 != 3 && pos_neg == 3)
{
std::swap (op0, op1);
uns1_p = true;
}
- int pos_neg0 = get_range_pos_neg (arg0);
- int pos_neg1 = get_range_pos_neg (arg1);
+ int pos_neg0 = get_range_pos_neg (arg0, currently_expanding_gimple_stmt);
+ int pos_neg1 = get_range_pos_neg (arg1, currently_expanding_gimple_stmt);
/* Unsigned types with smaller than mode precision, even if they have most
significant bit set, are still zero-extended. */
if (uns0_p && TYPE_PRECISION (TREE_TYPE (arg0)) < GET_MODE_PRECISION (mode))
int prec1 = TYPE_PRECISION (TREE_TYPE (arg1));
int precres = TYPE_PRECISION (type);
location_t loc = gimple_location (stmt);
- if (!uns0_p && get_range_pos_neg (arg0) == 1)
+ if (!uns0_p && get_range_pos_neg (arg0, stmt) == 1)
uns0_p = true;
- if (!uns1_p && get_range_pos_neg (arg1) == 1)
+ if (!uns1_p && get_range_pos_neg (arg1, stmt) == 1)
uns1_p = true;
int pr = get_min_precision (arg0, uns0_p ? UNSIGNED : SIGNED);
prec0 = MIN (prec0, pr);
#include "tree-cfg.h"
#include "dumpfile.h"
#include "builtins.h"
+#include "cfgexpand.h"
\f
/* Functions and data structures for expanding case statements. */
&& gimple_seq_unreachable_p (bb_seq (default_edge->dest)))
{
default_label = NULL;
- remove_edge (default_edge);
+ expand_remove_edge (default_edge);
default_edge = NULL;
}
--- /dev/null
+/* PR middle-end/120434 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=generic -masm=att" } */
+/* { dg-final { scan-assembler-times "\tsar\[lq]\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tshr\[lq]\t" 2 } } */
+
+[[gnu::noipa]] int
+foo (int x)
+{
+ return x / 200;
+}
+
+[[gnu::noipa]] int
+bar (int x)
+{
+ if (x < 0)
+ __builtin_unreachable ();
+ return x / 200;
+}
+
+[[gnu::noipa]] int
+baz (int x)
+{
+ if (x >= 0)
+ return x / 200;
+ else
+ return 24;
+}
}
/* Given the out-of-ssa info object SA (with prepared partitions)
- eliminate all phi nodes in all basic blocks. Afterwards no
- basic block will have phi nodes anymore and there are possibly
- some RTL instructions inserted on edges. */
+ eliminate all phi nodes in all basic blocks. Afterwards there
+ are possibly some RTL instructions inserted on edges. */
void
expand_phi_nodes (struct ssaexpand *sa)
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->preds)
eliminate_phi (e, &g);
- set_phi_nodes (bb, NULL);
/* We can't redirect EH edges in RTL land, so we need to do this
here. Redirection happens only when splitting is necessary,
which it is only for critical edges, normally. For EH edges
/* Return 1 if ARG interpreted as signed in its precision is known to be
always non-negative or 2 if ARG is known to be always negative, or 3 if
- ARG may be non-negative or negative. */
+ ARG may be non-negative or negative. STMT if specified is the statement
+ on which it is being tested. */
int
-get_range_pos_neg (tree arg)
+get_range_pos_neg (tree arg, gimple *stmt)
{
if (arg == error_mark_node)
return 3;
if (TREE_CODE (arg) != SSA_NAME)
return 3;
int_range_max r;
- while (!get_global_range_query ()->range_of_expr (r, arg)
+ while (!get_range_query (cfun)->range_of_expr (r, arg, stmt)
|| r.undefined_p () || r.varying_p ())
{
gimple *g = SSA_NAME_DEF_STMT (arg);
bool trust_type_canonical = true);
extern bool type_with_interoperable_signedness (const_tree);
extern bitmap get_nonnull_args (const_tree);
-extern int get_range_pos_neg (tree);
+extern int get_range_pos_neg (tree, gimple * = NULL);
/* Return true for a valid pair of new and delete operators. */
extern bool valid_new_delete_pair_p (tree, tree, bool * = NULL);