/* Control flow functions for trees.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
Contributed by Diego Novillo <dnovillo@redhat.com>
/* Nonzero if we found a computed goto while building basic blocks. */
static bool found_computed_goto;
+/* Hash table to store last discriminator assigned for each locus. */
+struct locus_discrim_map
+{
+ location_t locus;
+ int discriminator;
+};
+static htab_t discriminator_per_locus;
+
/* Basic blocks and flowgraphs. */
static void make_blocks (gimple_seq);
static void factor_computed_gotos (void);
static void make_cond_expr_edges (basic_block);
static void make_gimple_switch_edges (basic_block);
static void make_goto_expr_edges (basic_block);
+static unsigned int locus_map_hash (const void *);
+static int locus_map_eq (const void *, const void *);
+static void assign_discriminator (location_t, basic_block);
static edge gimple_redirect_edge_and_branch (edge, basic_block);
static edge gimple_try_redirect_by_replacing_jump (edge, basic_block);
static unsigned int split_critical_edges (void);
static int gimple_verify_flow_info (void);
static void gimple_make_forwarder_block (edge);
static void gimple_cfg2vcg (FILE *);
+static gimple first_non_label_stmt (basic_block);
/* Flowgraph optimization and cleanup. */
static void gimple_merge_blocks (basic_block, basic_block);
group_case_labels ();
/* Create the edges of the flowgraph. */
+ discriminator_per_locus = htab_create (13, locus_map_hash, locus_map_eq,
+ free);
make_edges ();
cleanup_dead_labels ();
+ htab_delete (discriminator_per_locus);
/* Debugging dumps. */
#ifdef ENABLE_CHECKING
verify_stmts ();
#endif
-
- /* Dump a textual representation of the flowgraph. */
- if (dump_file)
- gimple_dump_cfg (dump_file, dump_flags);
}
static unsigned int
execute_build_cfg (void)
{
- build_gimple_cfg (gimple_body (current_function_decl));
+ gimple_seq body = gimple_body (current_function_decl);
+
+ build_gimple_cfg (body);
+ gimple_set_body (current_function_decl, NULL);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Scope blocks:\n");
+ dump_scope_blocks (dump_file, dump_flags);
+ }
return 0;
}
PROP_cfg, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_verify_stmts | TODO_cleanup_cfg /* todo_flags_finish */
+ TODO_verify_stmts | TODO_cleanup_cfg
+ | TODO_dump_func /* todo_flags_finish */
}
};
/* Build a label for the new block which will contain the
factored computed goto. */
- factored_label_decl = create_artificial_label ();
+ factored_label_decl = create_artificial_label (UNKNOWN_LOCATION);
factored_computed_goto_label
= gimple_build_label (factored_label_decl);
gsi_insert_after (&new_gsi, factored_computed_goto_label,
/* If STMT is a basic block terminator, set START_NEW_BLOCK for the
next iteration. */
if (stmt_ends_bb_p (stmt))
- start_new_block = true;
+ {
+ /* If the stmt can make abnormal goto use a new temporary
+ for the assignment to the LHS. This makes sure the old value
+ of the LHS is available on the abnormal edge. Otherwise
+ we will end up with overlapping life-ranges for abnormal
+ SSA names. */
+ if (gimple_has_lhs (stmt)
+ && stmt_can_make_abnormal_goto (stmt)
+ && is_gimple_reg_type (TREE_TYPE (gimple_get_lhs (stmt))))
+ {
+ tree lhs = gimple_get_lhs (stmt);
+ tree tmp = create_tmp_var (TREE_TYPE (lhs), NULL);
+ gimple s = gimple_build_assign (lhs, tmp);
+ gimple_set_location (s, gimple_location (stmt));
+ gimple_set_block (s, gimple_block (stmt));
+ gimple_set_lhs (stmt, tmp);
+ if (TREE_CODE (TREE_TYPE (tmp)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (tmp)) == VECTOR_TYPE)
+ DECL_GIMPLE_REG_P (tmp) = 1;
+ gsi_insert_after (&i, s, GSI_SAME_STMT);
+ }
+ start_new_block = true;
+ }
gsi_next (&i);
first_stmt_of_seq = false;
if (stmt && gimple_code (stmt) == GIMPLE_COND)
{
+ location_t loc = gimple_location (stmt);
tree cond;
bool zerop, onep;
fold_defer_overflow_warnings ();
- cond = fold_binary (gimple_cond_code (stmt), boolean_type_node,
+ cond = fold_binary_loc (loc, gimple_cond_code (stmt), boolean_type_node,
gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
if (cond)
{
fallthru = true;
if (fallthru)
- make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+ {
+ make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+ if (last)
+ assign_discriminator (gimple_location (last), bb->next_bb);
+ }
}
if (root_omp_region)
fold_cond_expr_cond ();
}
+/* Trivial hash function for a location_t. ITEM is a pointer to
+ a hash table entry that maps a location_t to a discriminator. */
+
+static unsigned int
+locus_map_hash (const void *item)
+{
+ return ((const struct locus_discrim_map *) item)->locus;
+}
+
+/* Equality function for the locus-to-discriminator map. VA and VB
+ point to the two hash table entries to compare. */
+
+static int
+locus_map_eq (const void *va, const void *vb)
+{
+ const struct locus_discrim_map *a = (const struct locus_discrim_map *) va;
+ const struct locus_discrim_map *b = (const struct locus_discrim_map *) vb;
+ return a->locus == b->locus;
+}
+
+/* Find the next available discriminator value for LOCUS. The
+ discriminator distinguishes among several basic blocks that
+ share a common locus, allowing for more accurate sample-based
+ profiling. */
+
+static int
+next_discriminator_for_locus (location_t locus)
+{
+ struct locus_discrim_map item;
+ struct locus_discrim_map **slot;
+
+ item.locus = locus;
+ item.discriminator = 0;
+ slot = (struct locus_discrim_map **)
+ htab_find_slot_with_hash (discriminator_per_locus, (void *) &item,
+ (hashval_t) locus, INSERT);
+ gcc_assert (slot);
+ if (*slot == HTAB_EMPTY_ENTRY)
+ {
+ *slot = XNEW (struct locus_discrim_map);
+ gcc_assert (*slot);
+ (*slot)->locus = locus;
+ (*slot)->discriminator = 0;
+ }
+ (*slot)->discriminator++;
+ return (*slot)->discriminator;
+}
+
+/* Return TRUE if LOCUS1 and LOCUS2 refer to the same source line. */
+
+static bool
+same_line_p (location_t locus1, location_t locus2)
+{
+ expanded_location from, to;
+
+ if (locus1 == locus2)
+ return true;
+
+ from = expand_location (locus1);
+ to = expand_location (locus2);
+
+ if (from.line != to.line)
+ return false;
+ if (from.file == to.file)
+ return true;
+ return (from.file != NULL
+ && to.file != NULL
+ && strcmp (from.file, to.file) == 0);
+}
+
+/* Assign a unique discriminator value to block BB if it begins at the same
+ LOCUS as its predecessor block. */
+
+static void
+assign_discriminator (location_t locus, basic_block bb)
+{
+ gimple to_stmt;
+
+ if (locus == 0 || bb->discriminator != 0)
+ return;
+
+ to_stmt = first_non_label_stmt (bb);
+ if (to_stmt && same_line_p (locus, gimple_location (to_stmt)))
+ bb->discriminator = next_discriminator_for_locus (locus);
+}
/* Create the edges for a GIMPLE_COND starting at block BB. */
basic_block then_bb, else_bb;
tree then_label, else_label;
edge e;
+ location_t entry_locus;
gcc_assert (entry);
gcc_assert (gimple_code (entry) == GIMPLE_COND);
+ entry_locus = gimple_location (entry);
+
/* Entry basic blocks for each component. */
then_label = gimple_cond_true_label (entry);
else_label = gimple_cond_false_label (entry);
else_stmt = first_stmt (else_bb);
e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+ assign_discriminator (entry_locus, then_bb);
e->goto_locus = gimple_location (then_stmt);
+ if (e->goto_locus)
+ e->goto_block = gimple_block (then_stmt);
e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
if (e)
- e->goto_locus = gimple_location (else_stmt);
+ {
+ assign_discriminator (entry_locus, else_bb);
+ e->goto_locus = gimple_location (else_stmt);
+ if (e->goto_locus)
+ e->goto_block = gimple_block (else_stmt);
+ }
/* We do not need the labels anymore. */
gimple_cond_set_true_label (entry, NULL_TREE);
make_gimple_switch_edges (basic_block bb)
{
gimple entry = last_stmt (bb);
+ location_t entry_locus;
size_t i, n;
+ entry_locus = gimple_location (entry);
+
n = gimple_switch_num_labels (entry);
for (i = 0; i < n; ++i)
tree lab = CASE_LABEL (gimple_switch_label (entry, i));
basic_block label_bb = label_to_block (lab);
make_edge (bb, label_bb, 0);
+ assign_discriminator (entry_locus, label_bb);
}
}
if (simple_goto_p (goto_t))
{
tree dest = gimple_goto_dest (goto_t);
- edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
+ basic_block label_bb = label_to_block (dest);
+ edge e = make_edge (bb, label_bb, EDGE_FALLTHRU);
e->goto_locus = gimple_location (goto_t);
+ assign_discriminator (e->goto_locus, label_bb);
+ if (e->goto_locus)
+ e->goto_block = gimple_block (goto_t);
gsi_remove (&last, true);
return;
}
/* Callback for for_each_eh_region. Helper for cleanup_dead_labels. */
static void
-update_eh_label (struct eh_region *region)
+update_eh_label (struct eh_region_d *region)
{
tree old_label = get_eh_region_tree_label (region);
if (old_label)
if (!single_succ_p (a))
return false;
- if (single_succ_edge (a)->flags & EDGE_ABNORMAL)
+ if (single_succ_edge (a)->flags & (EDGE_ABNORMAL | EDGE_EH))
return false;
if (single_succ (a) != b)
FOR_EACH_IMM_USE_STMT (stmt, imm_iter, name)
{
- if (gimple_code (stmt) != GIMPLE_PHI)
- push_stmt_changes (&stmt);
-
FOR_EACH_IMM_USE_ON_STMT (use, imm_iter)
{
replace_exp (use, val);
if (cfgcleanup_altered_bbs)
bitmap_set_bit (cfgcleanup_altered_bbs, gimple_bb (stmt)->index);
- /* FIXME. This should go in pop_stmt_changes. */
+ /* FIXME. This should go in update_stmt. */
for (i = 0; i < gimple_num_ops (stmt); i++)
{
tree op = gimple_op (stmt, i);
}
maybe_clean_or_replace_eh_stmt (stmt, stmt);
-
- pop_stmt_changes (&stmt);
+ update_stmt (stmt);
}
}
/* Return the one of two successors of BB that is not reachable by a
- reached by a complex edge, if there is one. Else, return BB. We use
+ complex edge, if there is one. Else, return BB. We use
this in optimizations that use post-dominators for their heuristics,
to catch the cases in C++ where function calls are involved. */
{
gimple stmt = gsi_stmt (gsi);
+ if (gimple_no_warning_p (stmt)) return false;
+
if (gimple_has_location (stmt))
{
location_t loc = gimple_location (stmt);
if (LOCATION_LINE (loc) > 0)
{
- warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc);
+ warning_at (loc, OPT_Wunreachable_code, "will never be executed");
return true;
}
}
gimple stmt = gsi_stmt (*gsi);
/* The folded result must still be a conditional statement. */
- fold_stmt_inplace (stmt);
+ fold_stmt (gsi);
+ gcc_assert (gsi_stmt (*gsi) == stmt);
data->may_branch = true;
|| (TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block))
!= FUNCTION_DECL)))
{
- gsi_insert_seq_before (gsi, body_seq, GSI_SAME_STMT);
- gsi_remove (gsi, false);
- data->repeat = true;
+ tree var = NULL_TREE;
+ /* Even if there are no gimple_bind_vars, there might be other
+ decls in BLOCK_VARS rendering the GIMPLE_BIND not useless. */
+ if (block && !BLOCK_NUM_NONLOCALIZED_VARS (block))
+ for (var = BLOCK_VARS (block); var; var = TREE_CHAIN (var))
+ if (TREE_CODE (var) == IMPORTED_DECL)
+ break;
+ if (var || (block && BLOCK_NUM_NONLOCALIZED_VARS (block)))
+ gsi_next (gsi);
+ else
+ {
+ gsi_insert_seq_before (gsi, body_seq, GSI_SAME_STMT);
+ gsi_remove (gsi, false);
+ data->repeat = true;
+ }
}
else
gsi_next (gsi);
remove_useless_stmts_1 (&gsi, &data);
}
while (data.repeat);
+
+#ifdef ENABLE_TYPES_CHECKING
+ verify_types_in_gimple_seq (gimple_body (current_function_decl));
+#endif
+
return 0;
}
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
- 0, /* tv_id */
+ TV_NONE, /* tv_id */
PROP_gimple_any, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
static void
remove_phi_nodes_and_edges_for_unreachable_block (basic_block bb)
{
- gimple_stmt_iterator gsi;
-
/* Since this block is no longer reachable, we can just delete all
of its PHI nodes. */
- for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); )
- remove_phi_node (&gsi, true);
-
- set_phi_nodes (bb, NULL);
+ remove_phi_nodes (bb);
/* Remove edges to BB's successors. */
while (EDGE_COUNT (bb->succs) > 0)
loop above, so the last statement we process is the first statement
in the block. */
if (loc > BUILTINS_LOCATION && LOCATION_LINE (loc) > 0)
- warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc);
+ warning_at (loc, OPT_Wunreachable_code, "will never be executed");
remove_phi_nodes_and_edges_for_unreachable_block (bb);
bb->il.gimple = NULL;
return !gsi_end_p (i) ? gsi_stmt (i) : NULL;
}
+/* Return the first non-label statement in basic block BB. */
+
+static gimple
+first_non_label_stmt (basic_block bb)
+{
+ gimple_stmt_iterator i = gsi_start_bb (bb);
+ while (!gsi_end_p (i) && gimple_code (gsi_stmt (i)) == GIMPLE_LABEL)
+ gsi_next (&i);
+ return !gsi_end_p (i) ? gsi_stmt (i) : NULL;
+}
+
/* Return the last statement in basic block BB. */
gimple
}
break;
+ case INDIRECT_REF:
+ x = TREE_OPERAND (t, 0);
+ if (!is_gimple_reg (x) && !is_gimple_min_invariant (x))
+ {
+ error ("Indirect reference's operand is not a register or a constant.");
+ return x;
+ }
+ break;
+
case ASSERT_EXPR:
x = fold (ASSERT_EXPR_COND (t));
if (x == boolean_false_node)
break;
case MODIFY_EXPR:
- x = TREE_OPERAND (t, 0);
- if (TREE_CODE (x) == BIT_FIELD_REF
- && is_gimple_reg (TREE_OPERAND (x, 0)))
- {
- error ("GIMPLE register modified with BIT_FIELD_REF");
- return t;
- }
- break;
+ error ("MODIFY_EXPR not expected while having tuples.");
+ return *tp;
case ADDR_EXPR:
{
x = TREE_OPERAND (x, 0))
;
- if (TREE_CODE (x) != VAR_DECL && TREE_CODE (x) != PARM_DECL)
+ if (!(TREE_CODE (x) == VAR_DECL
+ || TREE_CODE (x) == PARM_DECL
+ || TREE_CODE (x) == RESULT_DECL))
return NULL;
if (!TREE_ADDRESSABLE (x))
{
error ("address taken, but ADDRESSABLE bit not set");
return x;
}
+ if (DECL_GIMPLE_REG_P (x))
+ {
+ error ("DECL_GIMPLE_REG_P set on a variable with address taken");
+ return x;
+ }
break;
}
if (is_gimple_id (expr))
return false;
- if (TREE_CODE (expr) != INDIRECT_REF
- && TREE_CODE (expr) != ALIGN_INDIRECT_REF
- && TREE_CODE (expr) != MISALIGNED_INDIRECT_REF)
+ if (!INDIRECT_REF_P (expr)
+ && TREE_CODE (expr) != TARGET_MEM_REF)
{
error ("invalid expression for min lvalue");
return true;
}
+ /* TARGET_MEM_REFs are strange beasts. */
+ if (TREE_CODE (expr) == TARGET_MEM_REF)
+ return false;
+
op = TREE_OPERAND (expr, 0);
if (!is_gimple_val (op))
{
return false;
}
-/* Verify if EXPR is a valid GIMPLE reference expression. Returns true
+/* Verify if EXPR is a valid GIMPLE reference expression. If
+ REQUIRE_LVALUE is true verifies it is an lvalue. Returns true
if there is an error, otherwise false. */
static bool
-verify_types_in_gimple_reference (tree expr)
+verify_types_in_gimple_reference (tree expr, bool require_lvalue)
{
while (handled_component_p (expr))
{
/* For VIEW_CONVERT_EXPRs which are allowed here, too, there
is nothing to verify. Gross mismatches at most invoke
undefined behavior. */
+ if (TREE_CODE (expr) == VIEW_CONVERT_EXPR
+ && !handled_component_p (op))
+ return false;
expr = op;
}
- return verify_types_in_gimple_min_lval (expr);
+ return ((require_lvalue || !is_gimple_min_invariant (expr))
+ && verify_types_in_gimple_min_lval (expr));
}
/* Returns true if there is one pointer type in TYPE_POINTER_TO (SRC_OBJ)
|| FIXED_POINT_TYPE_P (type2)));
}
-/* Verify that OP is a valid GIMPLE operand. Return true if there is
- an error, false otherwise. */
-
-static bool
-verify_types_in_gimple_op (tree op)
-{
- if (!is_gimple_val (op) && !is_gimple_lvalue (op))
- {
- error ("Invalid GIMPLE operand");
- debug_generic_expr (op);
- return true;
- }
-
- return false;
-}
-
-
/* Verify the contents of a GIMPLE_CALL STMT. Returns true when there
is a problem, otherwise false. */
static bool
-verify_types_in_gimple_call (gimple stmt)
+verify_gimple_call (gimple stmt)
{
- bool failed = false;
- unsigned int i;
- tree fn;
+ tree fn = gimple_call_fn (stmt);
+ tree fntype;
- if (gimple_call_lhs (stmt))
- failed |= verify_types_in_gimple_op (gimple_call_lhs (stmt));
+ if (!POINTER_TYPE_P (TREE_TYPE (fn))
+ || (TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != METHOD_TYPE))
+ {
+ error ("non-function in gimple call");
+ return true;
+ }
- fn = gimple_call_fn (stmt);
- if (TREE_CODE (fn) != OBJ_TYPE_REF
- && verify_types_in_gimple_op (fn))
- failed = true;
+ if (gimple_call_lhs (stmt)
+ && !is_gimple_lvalue (gimple_call_lhs (stmt)))
+ {
+ error ("invalid LHS in gimple call");
+ return true;
+ }
- if (gimple_call_chain (stmt))
- failed |= verify_types_in_gimple_op (gimple_call_chain (stmt));
+ fntype = TREE_TYPE (TREE_TYPE (fn));
+ if (gimple_call_lhs (stmt)
+ && !useless_type_conversion_p (TREE_TYPE (gimple_call_lhs (stmt)),
+ TREE_TYPE (fntype))
+ /* ??? At least C++ misses conversions at assignments from
+ void * call results.
+ ??? Java is completely off. Especially with functions
+ returning java.lang.Object.
+ For now simply allow arbitrary pointer type conversions. */
+ && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
+ && POINTER_TYPE_P (TREE_TYPE (fntype))))
+ {
+ error ("invalid conversion in gimple call");
+ debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
+ debug_generic_stmt (TREE_TYPE (fntype));
+ return true;
+ }
- for (i = 0; i < gimple_call_num_args (stmt); i++)
- failed |= verify_types_in_gimple_op (gimple_call_arg (stmt,i));
+ /* ??? The C frontend passes unpromoted arguments in case it
+ didn't see a function declaration before the call. So for now
+ leave the call arguments unverified. Once we gimplify
+ unit-at-a-time we have a chance to fix this. */
- return failed;
+ return false;
}
-
-/* Verify the contents of a GIMPLE_COND STMT. Returns true when there
- is a problem, otherwise false. */
+/* Verifies the gimple comparison with the result type TYPE and
+ the operands OP0 and OP1. */
static bool
-verify_types_in_gimple_cond (gimple stmt)
+verify_gimple_comparison (tree type, tree op0, tree op1)
{
- bool failed = false;
-
- failed |= verify_types_in_gimple_op (gimple_cond_lhs (stmt));
- failed |= verify_types_in_gimple_op (gimple_cond_rhs (stmt));
- failed |= verify_types_in_gimple_op (gimple_cond_true_label (stmt));
- failed |= verify_types_in_gimple_op (gimple_cond_false_label (stmt));
+ tree op0_type = TREE_TYPE (op0);
+ tree op1_type = TREE_TYPE (op1);
- return failed;
-}
+ if (!is_gimple_val (op0) || !is_gimple_val (op1))
+ {
+ error ("invalid operands in gimple comparison");
+ return true;
+ }
+ /* For comparisons we do not have the operations type as the
+ effective type the comparison is carried out in. Instead
+ we require that either the first operand is trivially
+ convertible into the second, or the other way around.
+ The resulting type of a comparison may be any integral type.
+ Because we special-case pointers to void we allow
+ comparisons of pointers with the same mode as well. */
+ if ((!useless_type_conversion_p (op0_type, op1_type)
+ && !useless_type_conversion_p (op1_type, op0_type)
+ && (!POINTER_TYPE_P (op0_type)
+ || !POINTER_TYPE_P (op1_type)
+ || TYPE_MODE (op0_type) != TYPE_MODE (op1_type)))
+ || !INTEGRAL_TYPE_P (type))
+ {
+ error ("type mismatch in comparison expression");
+ debug_generic_expr (type);
+ debug_generic_expr (op0_type);
+ debug_generic_expr (op1_type);
+ return true;
+ }
-/* Verify the contents of a GIMPLE_ASSIGN STMT. Returns true when there
- is a problem, otherwise false.
+ return false;
+}
- Verify that the types of the LHS and the RHS operands are
- compatible. This verification largely depends on what kind of
- operation is done on the RHS of the assignment. It is not always
- the case that all the types of the operands must match (e.g., 'a =
- (unsigned long) b' or 'ptr = ptr + 1'). */
+/* Verify a gimple assignment statement STMT with an unary rhs.
+ Returns true if anything is wrong. */
static bool
-verify_types_in_gimple_assign (gimple stmt)
+verify_gimple_assign_unary (gimple stmt)
{
enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
tree lhs = gimple_assign_lhs (stmt);
- tree rhs1 = gimple_assign_rhs1 (stmt);
- tree rhs2 = (gimple_num_ops (stmt) == 3) ? gimple_assign_rhs2 (stmt) : NULL;
tree lhs_type = TREE_TYPE (lhs);
+ tree rhs1 = gimple_assign_rhs1 (stmt);
tree rhs1_type = TREE_TYPE (rhs1);
- tree rhs2_type = (rhs2) ? TREE_TYPE (rhs2) : NULL;
- /* Special codes we cannot handle via their class. */
+ if (!is_gimple_reg (lhs)
+ && !(optimize == 0
+ && TREE_CODE (lhs_type) == COMPLEX_TYPE))
+ {
+ error ("non-register as LHS of unary operation");
+ return true;
+ }
+
+ if (!is_gimple_val (rhs1))
+ {
+ error ("invalid operand in unary operation");
+ return true;
+ }
+
+ /* First handle conversions. */
switch (rhs_code)
{
CASE_CONVERT:
{
- if (!is_gimple_val (rhs1))
- {
- error ("invalid operand in conversion");
- return true;
- }
-
/* Allow conversions between integral types and pointers only if
- there is no sign or zero extension involved. */
- if (((POINTER_TYPE_P (lhs_type) && INTEGRAL_TYPE_P (rhs1_type))
- || (POINTER_TYPE_P (rhs1_type) && INTEGRAL_TYPE_P (lhs_type)))
- && (TYPE_PRECISION (lhs_type) == TYPE_PRECISION (rhs1_type)
- /* For targets were the precision of sizetype doesn't
- match that of pointers we need the following. */
- || lhs_type == sizetype || rhs1_type == sizetype))
+ there is no sign or zero extension involved.
+ For targets were the precision of sizetype doesn't match that
+ of pointers we need to allow arbitrary conversions from and
+ to sizetype. */
+ if ((POINTER_TYPE_P (lhs_type)
+ && INTEGRAL_TYPE_P (rhs1_type)
+ && (TYPE_PRECISION (lhs_type) >= TYPE_PRECISION (rhs1_type)
+ || rhs1_type == sizetype))
+ || (POINTER_TYPE_P (rhs1_type)
+ && INTEGRAL_TYPE_P (lhs_type)
+ && (TYPE_PRECISION (rhs1_type) >= TYPE_PRECISION (lhs_type)
+ || lhs_type == sizetype)))
return false;
/* Allow conversion from integer to offset type and vice versa. */
case FIXED_CONVERT_EXPR:
{
- if (!is_gimple_val (rhs1))
- {
- error ("invalid operand in conversion");
- return true;
- }
-
if (!valid_fixed_convert_types_p (lhs_type, rhs1_type)
&& !valid_fixed_convert_types_p (rhs1_type, lhs_type))
{
case FLOAT_EXPR:
{
- if (!is_gimple_val (rhs1))
- {
- error ("invalid operand in int to float conversion");
- return true;
- }
-
if (!INTEGRAL_TYPE_P (rhs1_type) || !SCALAR_FLOAT_TYPE_P (lhs_type))
{
error ("invalid types in conversion to floating point");
case FIX_TRUNC_EXPR:
{
- if (!is_gimple_val (rhs1))
- {
- error ("invalid operand in float to int conversion");
- return true;
- }
-
if (!INTEGRAL_TYPE_P (lhs_type) || !SCALAR_FLOAT_TYPE_P (rhs1_type))
{
error ("invalid types in conversion to integer");
return false;
}
+ case VEC_UNPACK_HI_EXPR:
+ case VEC_UNPACK_LO_EXPR:
+ case REDUC_MAX_EXPR:
+ case REDUC_MIN_EXPR:
+ case REDUC_PLUS_EXPR:
+ case VEC_UNPACK_FLOAT_HI_EXPR:
+ case VEC_UNPACK_FLOAT_LO_EXPR:
+ /* FIXME. */
+ return false;
+
+ case TRUTH_NOT_EXPR:
+ case NEGATE_EXPR:
+ case ABS_EXPR:
+ case BIT_NOT_EXPR:
+ case PAREN_EXPR:
+ case NON_LVALUE_EXPR:
+ case CONJ_EXPR:
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* For the remaining codes assert there is no conversion involved. */
+ if (!useless_type_conversion_p (lhs_type, rhs1_type))
+ {
+ error ("non-trivial conversion in unary operation");
+ debug_generic_expr (lhs_type);
+ debug_generic_expr (rhs1_type);
+ return true;
+ }
+
+ return false;
+}
+
+/* Verify a gimple assignment statement STMT with a binary rhs.
+ Returns true if anything is wrong. */
+
+static bool
+verify_gimple_assign_binary (gimple stmt)
+{
+ enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
+ tree lhs = gimple_assign_lhs (stmt);
+ tree lhs_type = TREE_TYPE (lhs);
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ tree rhs1_type = TREE_TYPE (rhs1);
+ tree rhs2 = gimple_assign_rhs2 (stmt);
+ tree rhs2_type = TREE_TYPE (rhs2);
+
+ if (!is_gimple_reg (lhs)
+ && !(optimize == 0
+ && TREE_CODE (lhs_type) == COMPLEX_TYPE))
+ {
+ error ("non-register as LHS of binary operation");
+ return true;
+ }
+
+ if (!is_gimple_val (rhs1)
+ || !is_gimple_val (rhs2))
+ {
+ error ("invalid operands in binary operation");
+ return true;
+ }
+
+ /* First handle operations that involve different types. */
+ switch (rhs_code)
+ {
case COMPLEX_EXPR:
{
- if (!is_gimple_val (rhs1) || !is_gimple_val (rhs2))
- {
- error ("invalid operands in complex expression");
- return true;
- }
-
- if (!TREE_CODE (lhs_type) == COMPLEX_TYPE
- || !(TREE_CODE (rhs1_type) == INTEGER_TYPE
+ if (TREE_CODE (lhs_type) != COMPLEX_TYPE
+ || !(INTEGRAL_TYPE_P (rhs1_type)
|| SCALAR_FLOAT_TYPE_P (rhs1_type))
- || !(TREE_CODE (rhs2_type) == INTEGER_TYPE
+ || !(INTEGRAL_TYPE_P (rhs2_type)
|| SCALAR_FLOAT_TYPE_P (rhs2_type)))
{
error ("type mismatch in complex expression");
return false;
}
- case CONSTRUCTOR:
- {
- /* In this context we know that we are on the RHS of an
- assignment, so CONSTRUCTOR operands are OK. */
- /* FIXME: verify constructor arguments. */
- return false;
- }
-
case LSHIFT_EXPR:
case RSHIFT_EXPR:
case LROTATE_EXPR:
case RROTATE_EXPR:
{
- if (!is_gimple_val (rhs1) || !is_gimple_val (rhs2))
+ /* Shifts and rotates are ok on integral types, fixed point
+ types and integer vector types. */
+ if ((!INTEGRAL_TYPE_P (rhs1_type)
+ && !FIXED_POINT_TYPE_P (rhs1_type)
+ && !(TREE_CODE (rhs1_type) == VECTOR_TYPE
+ && TREE_CODE (TREE_TYPE (rhs1_type)) == INTEGER_TYPE))
+ || (!INTEGRAL_TYPE_P (rhs2_type)
+ /* Vector shifts of vectors are also ok. */
+ && !(TREE_CODE (rhs1_type) == VECTOR_TYPE
+ && TREE_CODE (TREE_TYPE (rhs1_type)) == INTEGER_TYPE
+ && TREE_CODE (rhs2_type) == VECTOR_TYPE
+ && TREE_CODE (TREE_TYPE (rhs2_type)) == INTEGER_TYPE))
+ || !useless_type_conversion_p (lhs_type, rhs1_type))
{
- error ("invalid operands in shift expression");
+ error ("type mismatch in shift expression");
+ debug_generic_expr (lhs_type);
+ debug_generic_expr (rhs1_type);
+ debug_generic_expr (rhs2_type);
return true;
}
- if (!TREE_CODE (rhs1_type) == INTEGER_TYPE
+ return false;
+ }
+
+ case VEC_LSHIFT_EXPR:
+ case VEC_RSHIFT_EXPR:
+ {
+ if (TREE_CODE (rhs1_type) != VECTOR_TYPE
+ || !(INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))
+ || FIXED_POINT_TYPE_P (TREE_TYPE (rhs1_type))
+ || SCALAR_FLOAT_TYPE_P (TREE_TYPE (rhs1_type)))
+ || (!INTEGRAL_TYPE_P (rhs2_type)
+ && (TREE_CODE (rhs2_type) != VECTOR_TYPE
+ || !INTEGRAL_TYPE_P (TREE_TYPE (rhs2_type))))
|| !useless_type_conversion_p (lhs_type, rhs1_type))
{
- error ("type mismatch in shift expression");
+ error ("type mismatch in vector shift expression");
debug_generic_expr (lhs_type);
debug_generic_expr (rhs1_type);
debug_generic_expr (rhs2_type);
return true;
}
+ /* For shifting a vector of floating point components we
+ only allow shifting by a constant multiple of the element size. */
+ if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (rhs1_type))
+ && (TREE_CODE (rhs2) != INTEGER_CST
+ || !div_if_zero_remainder (EXACT_DIV_EXPR, rhs2,
+ TYPE_SIZE (TREE_TYPE (rhs1_type)))))
+ {
+ error ("non-element sized vector shift of floating point vector");
+ return true;
+ }
return false;
}
case PLUS_EXPR:
+ {
+ /* We use regular PLUS_EXPR for vectors.
+ ??? This just makes the checker happy and may not be what is
+ intended. */
+ if (TREE_CODE (lhs_type) == VECTOR_TYPE
+ && POINTER_TYPE_P (TREE_TYPE (lhs_type)))
+ {
+ if (TREE_CODE (rhs1_type) != VECTOR_TYPE
+ || TREE_CODE (rhs2_type) != VECTOR_TYPE)
+ {
+ error ("invalid non-vector operands to vector valued plus");
+ return true;
+ }
+ lhs_type = TREE_TYPE (lhs_type);
+ rhs1_type = TREE_TYPE (rhs1_type);
+ rhs2_type = TREE_TYPE (rhs2_type);
+ /* PLUS_EXPR is commutative, so we might end up canonicalizing
+ the pointer to 2nd place. */
+ if (POINTER_TYPE_P (rhs2_type))
+ {
+ tree tem = rhs1_type;
+ rhs1_type = rhs2_type;
+ rhs2_type = tem;
+ }
+ goto do_pointer_plus_expr_check;
+ }
+ }
+ /* Fallthru. */
case MINUS_EXPR:
{
if (POINTER_TYPE_P (lhs_type)
case POINTER_PLUS_EXPR:
{
- if (!is_gimple_val (rhs1) || !is_gimple_val (rhs2))
- {
- error ("invalid operands in pointer plus expression");
- return true;
- }
+do_pointer_plus_expr_check:
if (!POINTER_TYPE_P (rhs1_type)
|| !useless_type_conversion_p (lhs_type, rhs1_type)
|| !useless_type_conversion_p (sizetype, rhs2_type))
return false;
}
- case ADDR_EXPR:
- {
- tree op = TREE_OPERAND (rhs1, 0);
- if (!is_gimple_addressable (op))
- {
- error ("invalid operand in unary expression");
- return true;
- }
-
- if (!one_pointer_to_useless_type_conversion_p (lhs_type, TREE_TYPE (op))
- /* FIXME: a longstanding wart, &a == &a[0]. */
- && (TREE_CODE (TREE_TYPE (op)) != ARRAY_TYPE
- || !one_pointer_to_useless_type_conversion_p (lhs_type,
- TREE_TYPE (TREE_TYPE (op)))))
- {
- error ("type mismatch in address expression");
- debug_generic_stmt (lhs_type);
- debug_generic_stmt (TYPE_POINTER_TO (TREE_TYPE (op)));
- return true;
- }
-
- return verify_types_in_gimple_reference (TREE_OPERAND (rhs1, 0));
- }
-
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
gcc_unreachable ();
case TRUTH_OR_EXPR:
case TRUTH_XOR_EXPR:
{
- if (!is_gimple_val (rhs1) || !is_gimple_val (rhs2))
- {
- error ("invalid operands in truth expression");
- return true;
- }
-
/* We allow any kind of integral typed argument and result. */
if (!INTEGRAL_TYPE_P (rhs1_type)
|| !INTEGRAL_TYPE_P (rhs2_type)
return false;
}
- case TRUTH_NOT_EXPR:
- {
- if (!is_gimple_val (rhs1))
- {
- error ("invalid operand in unary not");
- return true;
- }
-
- /* For TRUTH_NOT_EXPR we can have any kind of integral
- typed arguments and results. */
- if (!INTEGRAL_TYPE_P (rhs1_type)
- || !INTEGRAL_TYPE_P (lhs_type))
- {
- error ("type mismatch in not expression");
- debug_generic_expr (lhs_type);
- debug_generic_expr (rhs1_type);
- return true;
- }
-
- return false;
- }
-
- /* After gimplification we should not have any of these. */
- case ASM_EXPR:
- case BIND_EXPR:
- case CALL_EXPR:
- case COND_EXPR:
- case TREE_LIST:
- case COMPOUND_EXPR:
- case MODIFY_EXPR:
- case INIT_EXPR:
- case GOTO_EXPR:
- case LABEL_EXPR:
- case RETURN_EXPR:
- case TRY_FINALLY_EXPR:
- case TRY_CATCH_EXPR:
- case EH_FILTER_EXPR:
- case STATEMENT_LIST:
- {
- error ("tree node that should already be gimple.");
- return true;
- }
-
- case OBJ_TYPE_REF:
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ case UNORDERED_EXPR:
+ case ORDERED_EXPR:
+ case UNLT_EXPR:
+ case UNLE_EXPR:
+ case UNGT_EXPR:
+ case UNGE_EXPR:
+ case UNEQ_EXPR:
+ case LTGT_EXPR:
+ /* Comparisons are also binary, but the result type is not
+ connected to the operand types. */
+ return verify_gimple_comparison (lhs_type, rhs1, rhs2);
+
+ case WIDEN_SUM_EXPR:
+ case WIDEN_MULT_EXPR:
+ case VEC_WIDEN_MULT_HI_EXPR:
+ case VEC_WIDEN_MULT_LO_EXPR:
+ case VEC_PACK_TRUNC_EXPR:
+ case VEC_PACK_SAT_EXPR:
+ case VEC_PACK_FIX_TRUNC_EXPR:
+ case VEC_EXTRACT_EVEN_EXPR:
+ case VEC_EXTRACT_ODD_EXPR:
+ case VEC_INTERLEAVE_HIGH_EXPR:
+ case VEC_INTERLEAVE_LOW_EXPR:
/* FIXME. */
return false;
- default:;
+ case MULT_EXPR:
+ case TRUNC_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ case RDIV_EXPR:
+ case EXACT_DIV_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_AND_EXPR:
+ /* Continue with generic binary expression handling. */
+ break;
+
+ default:
+ gcc_unreachable ();
}
- /* Generic handling via classes. */
- switch (TREE_CODE_CLASS (rhs_code))
+ if (!useless_type_conversion_p (lhs_type, rhs1_type)
+ || !useless_type_conversion_p (lhs_type, rhs2_type))
{
- case tcc_unary:
- if (!useless_type_conversion_p (lhs_type, rhs1_type))
- {
- error ("non-trivial conversion at assignment");
- debug_generic_expr (lhs);
- debug_generic_expr (rhs1);
- return true;
- }
- break;
+ error ("type mismatch in binary expression");
+ debug_generic_stmt (lhs_type);
+ debug_generic_stmt (rhs1_type);
+ debug_generic_stmt (rhs2_type);
+ return true;
+ }
+
+ return false;
+}
+
+/* Verify a gimple assignment statement STMT with a single rhs.
+ Returns true if anything is wrong. */
+
+static bool
+verify_gimple_assign_single (gimple stmt)
+{
+ enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
+ tree lhs = gimple_assign_lhs (stmt);
+ tree lhs_type = TREE_TYPE (lhs);
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ tree rhs1_type = TREE_TYPE (rhs1);
+ bool res = false;
+
+ if (!useless_type_conversion_p (lhs_type, rhs1_type))
+ {
+ error ("non-trivial conversion at assignment");
+ debug_generic_expr (lhs_type);
+ debug_generic_expr (rhs1_type);
+ return true;
+ }
- case tcc_reference:
- return verify_types_in_gimple_reference (rhs1);
+ if (handled_component_p (lhs))
+ res |= verify_types_in_gimple_reference (lhs, true);
- case tcc_comparison:
+ /* Special codes we cannot handle via their class. */
+ switch (rhs_code)
+ {
+ case ADDR_EXPR:
{
- if (!is_gimple_val (rhs1) || !is_gimple_val (rhs2))
+ tree op = TREE_OPERAND (rhs1, 0);
+ if (!is_gimple_addressable (op))
{
- error ("invalid operands in comparison expression");
+ error ("invalid operand in unary expression");
return true;
}
- /* For comparisons we do not have the operations type as the
- effective type the comparison is carried out in. Instead
- we require that either the first operand is trivially
- convertible into the second, or the other way around.
- The resulting type of a comparison may be any integral type.
- Because we special-case pointers to void we allow
- comparisons of pointers with the same mode as well. */
- if ((!useless_type_conversion_p (rhs1_type, rhs2_type)
- && !useless_type_conversion_p (rhs2_type, rhs1_type)
- && (!POINTER_TYPE_P (rhs1_type)
- || !POINTER_TYPE_P (rhs2_type)
- || TYPE_MODE (rhs1_type) != TYPE_MODE (rhs2_type)))
- || !INTEGRAL_TYPE_P (lhs_type))
+ if (!one_pointer_to_useless_type_conversion_p (lhs_type,
+ TREE_TYPE (op)))
{
- error ("type mismatch in comparison expression");
- debug_generic_expr (lhs_type);
- debug_generic_expr (rhs1_type);
- debug_generic_expr (rhs2_type);
+ error ("type mismatch in address expression");
+ debug_generic_stmt (lhs_type);
+ debug_generic_stmt (TYPE_POINTER_TO (TREE_TYPE (op)));
return true;
}
- break;
+
+ return verify_types_in_gimple_reference (op, true);
}
+ /* tcc_reference */
+ case COMPONENT_REF:
+ case BIT_FIELD_REF:
+ case INDIRECT_REF:
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
+ case ARRAY_REF:
+ case ARRAY_RANGE_REF:
+ case VIEW_CONVERT_EXPR:
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ case TARGET_MEM_REF:
+ if (!is_gimple_reg (lhs)
+ && is_gimple_reg_type (TREE_TYPE (lhs)))
+ {
+ error ("invalid rhs for gimple memory store");
+ debug_generic_stmt (lhs);
+ debug_generic_stmt (rhs1);
+ return true;
+ }
+ return res || verify_types_in_gimple_reference (rhs1, false);
+
+ /* tcc_constant */
+ case SSA_NAME:
+ case INTEGER_CST:
+ case REAL_CST:
+ case FIXED_CST:
+ case COMPLEX_CST:
+ case VECTOR_CST:
+ case STRING_CST:
+ return res;
+
+ /* tcc_declaration */
+ case CONST_DECL:
+ return res;
+ case VAR_DECL:
+ case PARM_DECL:
+ if (!is_gimple_reg (lhs)
+ && !is_gimple_reg (rhs1)
+ && is_gimple_reg_type (TREE_TYPE (lhs)))
+ {
+ error ("invalid rhs for gimple memory store");
+ debug_generic_stmt (lhs);
+ debug_generic_stmt (rhs1);
+ return true;
+ }
+ return res;
+
+ case COND_EXPR:
+ case CONSTRUCTOR:
+ case OBJ_TYPE_REF:
+ case ASSERT_EXPR:
+ case WITH_SIZE_EXPR:
+ case EXC_PTR_EXPR:
+ case FILTER_EXPR:
+ case POLYNOMIAL_CHREC:
+ case DOT_PROD_EXPR:
+ case VEC_COND_EXPR:
+ case REALIGN_LOAD_EXPR:
+ /* FIXME. */
+ return res;
+
default:;
}
- return false;
+ return res;
}
+/* Verify the contents of a GIMPLE_ASSIGN STMT. Returns true when there
+ is a problem, otherwise false. */
+
+static bool
+verify_gimple_assign (gimple stmt)
+{
+ switch (gimple_assign_rhs_class (stmt))
+ {
+ case GIMPLE_SINGLE_RHS:
+ return verify_gimple_assign_single (stmt);
+
+ case GIMPLE_UNARY_RHS:
+ return verify_gimple_assign_unary (stmt);
+
+ case GIMPLE_BINARY_RHS:
+ return verify_gimple_assign_binary (stmt);
+
+ default:
+ gcc_unreachable ();
+ }
+}
/* Verify the contents of a GIMPLE_RETURN STMT. Returns true when there
is a problem, otherwise false. */
static bool
-verify_types_in_gimple_return (gimple stmt)
+verify_gimple_return (gimple stmt)
{
tree op = gimple_return_retval (stmt);
+ tree restype = TREE_TYPE (TREE_TYPE (cfun->decl));
+ /* We cannot test for present return values as we do not fix up missing
+ return values from the original source. */
if (op == NULL)
return false;
-
- return verify_types_in_gimple_op (op);
+
+ if (!is_gimple_val (op)
+ && TREE_CODE (op) != RESULT_DECL)
+ {
+ error ("invalid operand in return statement");
+ debug_generic_stmt (op);
+ return true;
+ }
+
+ if (!useless_type_conversion_p (restype, TREE_TYPE (op))
+ /* ??? With C++ we can have the situation that the result
+ decl is a reference type while the return type is an aggregate. */
+ && !(TREE_CODE (op) == RESULT_DECL
+ && TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE
+ && useless_type_conversion_p (restype, TREE_TYPE (TREE_TYPE (op)))))
+ {
+ error ("invalid conversion in return statement");
+ debug_generic_stmt (restype);
+ debug_generic_stmt (TREE_TYPE (op));
+ return true;
+ }
+
+ return false;
}
+/* Verify the contents of a GIMPLE_GOTO STMT. Returns true when there
+ is a problem, otherwise false. */
+
+static bool
+verify_gimple_goto (gimple stmt)
+{
+ tree dest = gimple_goto_dest (stmt);
+
+ /* ??? We have two canonical forms of direct goto destinations, a
+ bare LABEL_DECL and an ADDR_EXPR of a LABEL_DECL. */
+ if (TREE_CODE (dest) != LABEL_DECL
+ && (!is_gimple_val (dest)
+ || !POINTER_TYPE_P (TREE_TYPE (dest))))
+ {
+ error ("goto destination is neither a label nor a pointer");
+ return true;
+ }
+
+ return false;
+}
+
/* Verify the contents of a GIMPLE_SWITCH STMT. Returns true when there
is a problem, otherwise false. */
static bool
-verify_types_in_gimple_switch (gimple stmt)
+verify_gimple_switch (gimple stmt)
{
if (!is_gimple_val (gimple_switch_index (stmt)))
{
error ("invalid operand to switch statement");
- debug_generic_expr (gimple_switch_index (stmt));
+ debug_generic_stmt (gimple_switch_index (stmt));
return true;
}
and false otherwise. */
static bool
-verify_types_in_gimple_phi (gimple stmt)
+verify_gimple_phi (gimple stmt)
{
- size_t i;
+ tree type = TREE_TYPE (gimple_phi_result (stmt));
+ unsigned i;
- if (verify_types_in_gimple_op (gimple_phi_result (stmt)))
- return true;
+ if (!is_gimple_variable (gimple_phi_result (stmt)))
+ {
+ error ("Invalid PHI result");
+ return true;
+ }
for (i = 0; i < gimple_phi_num_args (stmt); i++)
- if (verify_types_in_gimple_op (gimple_phi_arg_def (stmt, i)))
- return true;
+ {
+ tree arg = gimple_phi_arg_def (stmt, i);
+ if ((is_gimple_reg (gimple_phi_result (stmt))
+ && !is_gimple_val (arg))
+ || (!is_gimple_reg (gimple_phi_result (stmt))
+ && !is_gimple_addressable (arg)))
+ {
+ error ("Invalid PHI argument");
+ debug_generic_stmt (arg);
+ return true;
+ }
+ if (!useless_type_conversion_p (type, TREE_TYPE (arg)))
+ {
+ error ("Incompatible types in PHI argument %u", i);
+ debug_generic_stmt (type);
+ debug_generic_stmt (TREE_TYPE (arg));
+ return true;
+ }
+ }
return false;
}
switch (gimple_code (stmt))
{
case GIMPLE_ASSIGN:
- return verify_types_in_gimple_assign (stmt);
+ return verify_gimple_assign (stmt);
case GIMPLE_LABEL:
return TREE_CODE (gimple_label_label (stmt)) != LABEL_DECL;
case GIMPLE_CALL:
- return verify_types_in_gimple_call (stmt);
+ return verify_gimple_call (stmt);
case GIMPLE_COND:
- return verify_types_in_gimple_cond (stmt);
+ return verify_gimple_comparison (boolean_type_node,
+ gimple_cond_lhs (stmt),
+ gimple_cond_rhs (stmt));
case GIMPLE_GOTO:
- return verify_types_in_gimple_op (gimple_goto_dest (stmt));
-
- case GIMPLE_NOP:
- case GIMPLE_PREDICT:
- return false;
+ return verify_gimple_goto (stmt);
case GIMPLE_SWITCH:
- return verify_types_in_gimple_switch (stmt);
+ return verify_gimple_switch (stmt);
case GIMPLE_RETURN:
- return verify_types_in_gimple_return (stmt);
+ return verify_gimple_return (stmt);
case GIMPLE_ASM:
return false;
- case GIMPLE_CHANGE_DYNAMIC_TYPE:
- return verify_types_in_gimple_op (gimple_cdt_location (stmt));
-
case GIMPLE_PHI:
- return verify_types_in_gimple_phi (stmt);
+ return verify_gimple_phi (stmt);
+
+ /* Tuples that do not have tree operands. */
+ case GIMPLE_NOP:
+ case GIMPLE_RESX:
+ case GIMPLE_PREDICT:
+ return false;
default:
gcc_unreachable ();
switch (gimple_code (stmt))
{
- case GIMPLE_BIND:
- err |= verify_types_in_gimple_seq_2 (gimple_bind_body (stmt));
- break;
-
- case GIMPLE_TRY:
- err |= verify_types_in_gimple_seq_2 (gimple_try_eval (stmt));
- err |= verify_types_in_gimple_seq_2 (gimple_try_cleanup (stmt));
- break;
-
- case GIMPLE_EH_FILTER:
- err |= verify_types_in_gimple_seq_2
- (gimple_eh_filter_failure (stmt));
- break;
-
- case GIMPLE_CATCH:
- err |= verify_types_in_gimple_seq_2 (gimple_catch_handler (stmt));
- break;
-
- case GIMPLE_OMP_CRITICAL:
- case GIMPLE_OMP_CONTINUE:
- case GIMPLE_OMP_MASTER:
- case GIMPLE_OMP_ORDERED:
- case GIMPLE_OMP_SECTION:
- case GIMPLE_OMP_FOR:
- case GIMPLE_OMP_PARALLEL:
- case GIMPLE_OMP_TASK:
- case GIMPLE_OMP_SECTIONS:
- case GIMPLE_OMP_SINGLE:
- case GIMPLE_OMP_ATOMIC_STORE:
- case GIMPLE_OMP_ATOMIC_LOAD:
- break;
-
- /* Tuples that do not have trees. */
- case GIMPLE_NOP:
- case GIMPLE_RESX:
- case GIMPLE_OMP_RETURN:
- case GIMPLE_PREDICT:
- break;
+ case GIMPLE_BIND:
+ err |= verify_types_in_gimple_seq_2 (gimple_bind_body (stmt));
+ break;
+
+ case GIMPLE_TRY:
+ err |= verify_types_in_gimple_seq_2 (gimple_try_eval (stmt));
+ err |= verify_types_in_gimple_seq_2 (gimple_try_cleanup (stmt));
+ break;
+
+ case GIMPLE_EH_FILTER:
+ err |= verify_types_in_gimple_seq_2 (gimple_eh_filter_failure (stmt));
+ break;
+
+ case GIMPLE_CATCH:
+ err |= verify_types_in_gimple_seq_2 (gimple_catch_handler (stmt));
+ break;
default:
{
didn't see a function declaration before the call. */
if (is_gimple_call (stmt))
{
- tree decl = gimple_call_fn (stmt);
+ tree decl;
- if (TREE_CODE (decl) == FUNCTION_DECL
+ if (!is_gimple_call_addr (gimple_call_fn (stmt)))
+ {
+ error ("invalid function in call statement");
+ return true;
+ }
+
+ decl = gimple_call_fndecl (stmt);
+ if (decl
+ && TREE_CODE (decl) == FUNCTION_DECL
&& DECL_LOOPING_CONST_OR_PURE_P (decl)
&& (!DECL_PURE_P (decl))
&& (!TREE_READONLY (decl)))
if (addr)
{
debug_generic_expr (addr);
- inform ("in statement");
+ inform (gimple_location (gsi_stmt (*gsi)), "in statement");
debug_gimple_stmt (stmt);
return true;
}
to match. */
if (lookup_stmt_eh_region (stmt) >= 0)
{
- if (!stmt_could_throw_p (stmt))
+ /* During IPA passes, ipa-pure-const sets nothrow flags on calls
+ and they are updated on statements only after fixup_cfg
+ is executed at beggining of expansion stage. */
+ if (!stmt_could_throw_p (stmt) && cgraph_state != CGRAPH_STATE_IPA_SSA)
{
error ("statement marked for throw, but doesn%'t");
goto fail;
debug_gimple_stmt (node->stmt);
eh_error_found = true;
}
- return 0;
+ return 1;
}
err |= true;
}
}
+
+#ifdef ENABLE_TYPES_CHECKING
+ if (verify_gimple_phi (phi))
+ {
+ debug_gimple_stmt (phi);
+ err |= true;
+ }
+#endif
}
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
if (gimple_bb (stmt) != bb)
{
error ("gimple_bb (stmt) is set to a wrong basic block");
+ debug_gimple_stmt (stmt);
err |= true;
}
}
err |= verify_stmt (&gsi);
+
+#ifdef ENABLE_TYPES_CHECKING
+ if (verify_types_in_gimple_stmt (gsi_stmt (gsi)))
+ {
+ debug_gimple_stmt (stmt);
+ err |= true;
+ }
+#endif
addr = walk_gimple_op (gsi_stmt (gsi), verify_node_sharing, &wi);
if (addr)
{
}
}
- label = create_artificial_label ();
+ label = create_artificial_label (UNKNOWN_LOCATION);
stmt = gimple_build_label (label);
gsi_insert_before (&s, stmt, GSI_NEW_STMT);
return label;
if (e->dest == dest)
return NULL;
+ if (e->flags & EDGE_EH)
+ return redirect_eh_edge (e, dest);
+
gsi = gsi_last_bb (bb);
stmt = gsi_end_p (gsi) ? NULL : gsi_stmt (gsi);
- switch (stmt ? gimple_code (stmt) : ERROR_MARK)
+ switch (stmt ? gimple_code (stmt) : GIMPLE_ERROR_MARK)
{
case GIMPLE_COND:
/* For COND_EXPR, we only need to redirect the edge. */
static bool
gimple_can_remove_branch_p (const_edge e)
{
- if (e->flags & EDGE_ABNORMAL)
+ if (e->flags & (EDGE_ABNORMAL | EDGE_EH))
return false;
return true;
operands. */
copy = gimple_copy (stmt);
gsi_insert_after (&gsi_tgt, copy, GSI_NEW_STMT);
- copy_virtual_operands (copy, stmt);
region = lookup_stmt_eh_region (stmt);
if (region >= 0)
add_stmt_to_eh_region (copy, region);
free_region_copy = true;
}
- gcc_assert (!need_ssa_update_p ());
+ gcc_assert (!need_ssa_update_p (cfun));
/* Record blocks outside the region that are dominated by something
inside. */
free_region_copy = true;
}
- gcc_assert (!need_ssa_update_p ());
+ gcc_assert (!need_ssa_update_p (cfun));
/* Record blocks outside the region that are dominated by something
inside. */
mark_virtual_ops_for_renaming (gsi_stmt (gsi));
}
-/* Marks virtual operands of all statements in basic blocks BBS for
- renaming. */
-
-static void
-mark_virtual_ops_in_region (VEC (basic_block,heap) *bbs)
-{
- basic_block bb;
- unsigned i;
-
- for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
- mark_virtual_ops_in_bb (bb);
-}
-
/* Move basic block BB from function CFUN to function DEST_FN. The
block is moved out of the original linked list and placed after
block AFTER in the new list. Also, the block is removed from the
old_len = VEC_length (basic_block, cfg->x_label_to_block_map);
if (old_len <= (unsigned) uid)
{
- new_len = 3 * uid / 2;
+ new_len = 3 * uid / 2 + 1;
VEC_safe_grow_cleared (basic_block, gc,
cfg->x_label_to_block_map, new_len);
}
update_stmt (stmt);
pop_cfun ();
}
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->goto_locus)
+ {
+ tree block = e->goto_block;
+ if (d->orig_block == NULL_TREE
+ || block == d->orig_block)
+ e->goto_block = d->new_block;
+#ifdef ENABLE_CHECKING
+ else if (block != d->new_block)
+ {
+ while (block && block != d->orig_block)
+ block = BLOCK_SUPERCONTEXT (block);
+ gcc_assert (block);
+ }
+#endif
+ }
}
/* Examine the statements in BB (which is in SRC_CFUN); find and return
m = XNEW (struct tree_map);
m->hash = DECL_UID (decl);
m->base.from = decl;
- m->to = create_artificial_label ();
+ m->to = create_artificial_label (UNKNOWN_LOCATION);
LABEL_DECL_UID (m->to) = LABEL_DECL_UID (decl);
if (LABEL_DECL_UID (m->to) >= cfun->cfg->last_label_uid)
cfun->cfg->last_label_uid = LABEL_DECL_UID (m->to) + 1;
for (tp = &BLOCK_VARS (block); *tp; tp = &TREE_CHAIN (*tp))
{
t = *tp;
+ if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != CONST_DECL)
+ continue;
replace_by_duplicate_decl (&t, vars_map, to_context);
if (t != *tp)
{
pop_cfun ();
- /* The ssa form for virtual operands in the source function will have to
- be repaired. We do not care for the real operands -- the sese region
- must be closed with respect to those. */
- mark_virtual_ops_in_region (bbs);
-
/* Move blocks from BBS into DEST_CFUN. */
gcc_assert (VEC_length (basic_block, bbs) >= 2);
after = dest_cfun->cfg->x_entry_block_ptr;
if (dsf && (flags & TDF_DETAILS))
dump_eh_tree (file, dsf);
- if (flags & TDF_RAW && !gimple_body (fn))
+ if (flags & TDF_RAW && !gimple_has_body_p (fn))
{
dump_node (fn, TDF_SLIM | flags, file);
return;
{
basic_block bb;
- bb = BASIC_BLOCK (NUM_FIXED_BLOCKS);
+ bb = ENTRY_BLOCK_PTR;
if (bb && bb->loop_father)
print_loop_and_siblings (file, bb->loop_father, 0, verbosity);
}
&& fndecl
&& DECL_BUILT_IN (fndecl)
&& (call_flags & ECF_NOTHROW)
- && !(call_flags & ECF_NORETURN)
- && !(call_flags & ECF_RETURNS_TWICE))
- return false;
+ && !(call_flags & ECF_RETURNS_TWICE)
+ /* fork() doesn't really return twice, but the effect of
+ wrapping it in __gcov_fork() which calls __gcov_flush()
+ and clears the counters before forking has the same
+ effect as returning twice. Force a fake edge. */
+ && !(DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FORK))
+ return false;
if (is_gimple_call (t)
&& !(call_flags & ECF_NORETURN))
return true;
- if (gimple_code (t) == ASM_EXPR
+ if (gimple_code (t) == GIMPLE_ASM
&& (gimple_asm_volatile_p (t) || gimple_asm_input_p (t)))
return true;
return changed;
}
-/* Stores all basic blocks dominated by BB to DOM_BBS. */
-
-static void
-get_all_dominated_blocks (basic_block bb, VEC (basic_block, heap) **dom_bbs)
-{
- basic_block son;
-
- VEC_safe_push (basic_block, heap, *dom_bbs, bb);
- for (son = first_dom_son (CDI_DOMINATORS, bb);
- son;
- son = next_dom_son (CDI_DOMINATORS, son))
- get_all_dominated_blocks (son, dom_bbs);
-}
-
/* Removes edge E and all the blocks dominated by it, and updates dominance
information. The IL in E->src needs to be updated separately.
If dominance info is not available, only the edge E is removed.*/
get_immediate_dominator (CDI_DOMINATORS, e->dest)->index);
else
{
- get_all_dominated_blocks (e->dest, &bbs_to_remove);
+ bbs_to_remove = get_all_dominated_blocks (CDI_DOMINATORS, e->dest);
for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
{
FOR_EACH_EDGE (f, ei, bb->succs)
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i, bi)
{
- changed |= gimple_purge_dead_eh_edges (BASIC_BLOCK (i));
+ basic_block bb = BASIC_BLOCK (i);
+
+ /* Earlier gimple_purge_dead_eh_edges could have removed
+ this basic block already. */
+ gcc_assert (bb || changed);
+ if (bb != NULL)
+ changed |= gimple_purge_dead_eh_edges (bb);
}
return changed;
FOR_ALL_BB (bb)
{
FOR_EACH_EDGE (e, ei, bb->succs)
- if (EDGE_CRITICAL_P (e) && !(e->flags & EDGE_ABNORMAL))
- {
+ {
+ if (EDGE_CRITICAL_P (e) && !(e->flags & EDGE_ABNORMAL))
split_edge (e);
- }
+ /* PRE inserts statements to edges and expects that
+ since split_critical_edges was done beforehand, committing edge
+ insertions will not split more edges. In addition to critical
+ edges we must split edges that have multiple successors and
+ end by control flow statements, such as RESX.
+ Go ahead and split them too. This matches the logic in
+ gimple_find_edge_insert_loc. */
+ else if ((!single_pred_p (e->dest)
+ || phi_nodes (e->dest)
+ || e->dest == EXIT_BLOCK_PTR)
+ && e->src != ENTRY_BLOCK_PTR
+ && !(e->flags & EDGE_ABNORMAL))
+ {
+ gimple_stmt_iterator gsi;
+
+ gsi = gsi_last_bb (e->src);
+ if (!gsi_end_p (gsi)
+ && stmt_ends_bb_p (gsi_stmt (gsi))
+ && gimple_code (gsi_stmt (gsi)) != GIMPLE_RETURN)
+ split_edge (e);
+ }
+ }
}
end_recording_case_labels ();
return 0;
PROP_no_crit_edges, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ TODO_dump_func | TODO_verify_flow /* todo_flags_finish */
}
};
tree type, tree a, tree b, tree c)
{
tree ret;
+ location_t loc = gimple_location (gsi_stmt (*gsi));
- ret = fold_build3 (code, type, a, b, c);
+ ret = fold_build3_loc (loc, code, type, a, b, c);
STRIP_NOPS (ret);
return force_gimple_operand_gsi (gsi, ret, true, NULL, true,
{
tree ret;
- ret = fold_build2 (code, type, a, b);
+ ret = fold_build2_loc (gimple_location (gsi_stmt (*gsi)), code, type, a, b);
STRIP_NOPS (ret);
return force_gimple_operand_gsi (gsi, ret, true, NULL, true,
{
tree ret;
- ret = fold_build1 (code, type, a);
+ ret = fold_build1_loc (gimple_location (gsi_stmt (*gsi)), code, type, a);
STRIP_NOPS (ret);
return force_gimple_operand_gsi (gsi, ret, true, NULL, true,
}
if (location == UNKNOWN_LOCATION)
location = cfun->function_end_locus;
- warning (0, "%H%<noreturn%> function does return", &location);
+ warning_at (location, 0, "%<noreturn%> function does return");
}
/* If we see "return;" in some basic block, then we do reach the end
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
- 0, /* tv_id */
+ TV_NONE, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
&& !TREE_THIS_VOLATILE (cfun->decl)
&& EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0
&& !lang_hooks.missing_noreturn_ok_p (cfun->decl))
- warning (OPT_Wmissing_noreturn, "%Jfunction might be possible candidate "
- "for attribute %<noreturn%>",
- cfun->decl);
+ warning_at (DECL_SOURCE_LOCATION (cfun->decl), OPT_Wmissing_noreturn,
+ "function might be possible candidate "
+ "for attribute %<noreturn%>");
return 0;
}
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
- 0, /* tv_id */
+ TV_NONE, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0 /* todo_flags_finish */
}
};
+
+
+/* Walk a gimplified function and warn for functions whose return value is
+ ignored and attribute((warn_unused_result)) is set. This is done before
+ inlining, so we don't have to worry about that. */
+
+static void
+do_warn_unused_result (gimple_seq seq)
+{
+ tree fdecl, ftype;
+ gimple_stmt_iterator i;
+
+ for (i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i))
+ {
+ gimple g = gsi_stmt (i);
+
+ switch (gimple_code (g))
+ {
+ case GIMPLE_BIND:
+ do_warn_unused_result (gimple_bind_body (g));
+ break;
+ case GIMPLE_TRY:
+ do_warn_unused_result (gimple_try_eval (g));
+ do_warn_unused_result (gimple_try_cleanup (g));
+ break;
+ case GIMPLE_CATCH:
+ do_warn_unused_result (gimple_catch_handler (g));
+ break;
+ case GIMPLE_EH_FILTER:
+ do_warn_unused_result (gimple_eh_filter_failure (g));
+ break;
+
+ case GIMPLE_CALL:
+ if (gimple_call_lhs (g))
+ break;
+
+ /* This is a naked call, as opposed to a GIMPLE_CALL with an
+ LHS. All calls whose value is ignored should be
+ represented like this. Look for the attribute. */
+ fdecl = gimple_call_fndecl (g);
+ ftype = TREE_TYPE (TREE_TYPE (gimple_call_fn (g)));
+
+ if (lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (ftype)))
+ {
+ location_t loc = gimple_location (g);
+
+ if (fdecl)
+ warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of %qD, "
+ "declared with attribute warn_unused_result",
+ fdecl);
+ else
+ warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of function "
+ "declared with attribute warn_unused_result");
+ }
+ break;
+
+ default:
+ /* Not a container, not a call, or a call whose value is used. */
+ break;
+ }
+ }
+}
+
+static unsigned int
+run_warn_unused_result (void)
+{
+ do_warn_unused_result (gimple_body (current_function_decl));
+ return 0;
+}
+
+static bool
+gate_warn_unused_result (void)
+{
+ return flag_warn_unused_result;
+}
+
+struct gimple_opt_pass pass_warn_unused_result =
+{
+ {
+ GIMPLE_PASS,
+ "warn_unused_result", /* name */
+ gate_warn_unused_result, /* gate */
+ run_warn_unused_result, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_NONE, /* tv_id */
+ PROP_gimple_any, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ }
+};
+