+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ {
+ tree op = TREE_OPERAND (expr, 0);
+ if (!is_gimple_val (op))
+ {
+ error ("invalid operand in conversion");
+ return true;
+ }
+
+ /* Allow conversions between integral types and between
+ pointer types. */
+ if ((INTEGRAL_TYPE_P (type) && INTEGRAL_TYPE_P (TREE_TYPE (op)))
+ || (POINTER_TYPE_P (type) && POINTER_TYPE_P (TREE_TYPE (op))))
+ return false;
+
+ /* Allow conversions between integral types and pointers only if
+ there is no sign or zero extension involved. */
+ if (((POINTER_TYPE_P (type) && INTEGRAL_TYPE_P (TREE_TYPE (op)))
+ || (POINTER_TYPE_P (TREE_TYPE (op)) && INTEGRAL_TYPE_P (type)))
+ && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (op)))
+ return false;
+
+ /* Allow conversion from integer to offset type and vice versa. */
+ if ((TREE_CODE (type) == OFFSET_TYPE
+ && TREE_CODE (TREE_TYPE (op)) == INTEGER_TYPE)
+ || (TREE_CODE (type) == INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (op)) == OFFSET_TYPE))
+ return false;
+
+ /* Otherwise assert we are converting between types of the
+ same kind. */
+ if (TREE_CODE (type) != TREE_CODE (TREE_TYPE (op)))
+ {
+ error ("invalid types in nop conversion");
+ debug_generic_expr (type);
+ debug_generic_expr (TREE_TYPE (op));
+ return true;
+ }
+
+ return false;
+ }
+
+ case FLOAT_EXPR:
+ {
+ tree op = TREE_OPERAND (expr, 0);
+ if (!is_gimple_val (op))
+ {
+ error ("invalid operand in int to float conversion");
+ return true;
+ }
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (op))
+ || !SCALAR_FLOAT_TYPE_P (type))
+ {
+ error ("invalid types in conversion to floating point");
+ debug_generic_expr (type);
+ debug_generic_expr (TREE_TYPE (op));
+ return true;
+ }
+ return false;
+ }
+
+ case FIX_TRUNC_EXPR:
+ {
+ tree op = TREE_OPERAND (expr, 0);
+ if (!is_gimple_val (op))
+ {
+ error ("invalid operand in float to int conversion");
+ return true;
+ }
+ if (!INTEGRAL_TYPE_P (type)
+ || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (op)))
+ {
+ error ("invalid types in conversion to integer");
+ debug_generic_expr (type);
+ debug_generic_expr (TREE_TYPE (op));
+ return true;
+ }
+ return false;
+ }
+
+ case COMPLEX_EXPR:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+ if (!is_gimple_val (op0) || !is_gimple_val (op1))
+ {
+ error ("invalid operands in complex expression");
+ return true;
+ }
+ if (!TREE_CODE (type) == COMPLEX_TYPE
+ || !(TREE_CODE (TREE_TYPE (op0)) == INTEGER_TYPE
+ || SCALAR_FLOAT_TYPE_P (TREE_TYPE (op0)))
+ || !(TREE_CODE (TREE_TYPE (op1)) == INTEGER_TYPE
+ || SCALAR_FLOAT_TYPE_P (TREE_TYPE (op1)))
+ || !useless_type_conversion_p (TREE_TYPE (type),
+ TREE_TYPE (op0))
+ || !useless_type_conversion_p (TREE_TYPE (type),
+ TREE_TYPE (op1)))
+ {
+ error ("type mismatch in complex expression");
+ debug_generic_stmt (TREE_TYPE (expr));
+ debug_generic_stmt (TREE_TYPE (op0));
+ debug_generic_stmt (TREE_TYPE (op1));
+ return true;
+ }
+ return false;
+ }
+
+ case CONSTRUCTOR:
+ {
+ /* This is used like COMPLEX_EXPR but for vectors. */
+ if (TREE_CODE (type) != VECTOR_TYPE)
+ {
+ error ("constructor not allowed for non-vector types");
+ debug_generic_stmt (type);
+ return true;
+ }
+ /* FIXME: verify constructor arguments. */
+ return false;
+ }
+
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+ if (!is_gimple_val (op0) || !is_gimple_val (op1))
+ {
+ error ("invalid operands in shift expression");
+ return true;
+ }
+ if (!TREE_CODE (TREE_TYPE (op1)) == INTEGER_TYPE
+ || !useless_type_conversion_p (type, TREE_TYPE (op0)))
+ {
+ error ("type mismatch in shift expression");
+ debug_generic_stmt (TREE_TYPE (expr));
+ debug_generic_stmt (TREE_TYPE (op0));
+ debug_generic_stmt (TREE_TYPE (op1));
+ return true;
+ }
+ return false;
+ }
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+ if (POINTER_TYPE_P (type)
+ || POINTER_TYPE_P (TREE_TYPE (op0))
+ || POINTER_TYPE_P (TREE_TYPE (op1)))
+ {
+ error ("invalid (pointer) operands to plus/minus");
+ return true;
+ }
+ /* Continue with generic binary expression handling. */
+ break;
+ }
+
+ case POINTER_PLUS_EXPR:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+ if (!is_gimple_val (op0) || !is_gimple_val (op1))
+ {
+ error ("invalid operands in pointer plus expression");
+ return true;
+ }
+ if (!POINTER_TYPE_P (TREE_TYPE (op0))
+ || TREE_CODE (TREE_TYPE (op1)) != INTEGER_TYPE
+ || !useless_type_conversion_p (type, TREE_TYPE (op0))
+ || !useless_type_conversion_p (sizetype, TREE_TYPE (op1)))
+ {
+ error ("type mismatch in pointer plus expression");
+ debug_generic_stmt (type);
+ debug_generic_stmt (TREE_TYPE (op0));
+ debug_generic_stmt (TREE_TYPE (op1));
+ return true;
+ }
+ return false;
+ }
+
+ case COND_EXPR:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+ tree op2 = TREE_OPERAND (expr, 2);
+ if ((!is_gimple_val (op1)
+ && TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE)
+ || (!is_gimple_val (op2)
+ && TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE))
+ {
+ error ("invalid operands in conditional expression");
+ return true;
+ }
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (op0))
+ || (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE
+ && !useless_type_conversion_p (type, TREE_TYPE (op1)))
+ || (TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE
+ && !useless_type_conversion_p (type, TREE_TYPE (op2))))
+ {
+ error ("type mismatch in conditional expression");
+ debug_generic_stmt (type);
+ debug_generic_stmt (TREE_TYPE (op0));
+ debug_generic_stmt (TREE_TYPE (op1));
+ debug_generic_stmt (TREE_TYPE (op2));
+ return true;
+ }
+ return verify_gimple_expr (op0);
+ }
+
+ case ADDR_EXPR:
+ {
+ tree op = TREE_OPERAND (expr, 0);
+ if (!is_gimple_addressable (op))
+ {
+ error ("invalid operand in unary expression");
+ return true;
+ }
+ if (TYPE_POINTER_TO (TREE_TYPE (op))
+ && !useless_type_conversion_p (type,
+ TYPE_POINTER_TO (TREE_TYPE (op)))
+ /* FIXME: a longstanding wart, &a == &a[0]. */
+ && (TREE_CODE (TREE_TYPE (op)) != ARRAY_TYPE
+ || (TYPE_POINTER_TO (TREE_TYPE (TREE_TYPE (op)))
+ && !useless_type_conversion_p (type,
+ TYPE_POINTER_TO (TREE_TYPE (TREE_TYPE (op)))))))
+ {
+ error ("type mismatch in address expression");
+ debug_generic_stmt (TREE_TYPE (expr));
+ debug_generic_stmt (TYPE_POINTER_TO (TREE_TYPE (op)));
+ return true;
+ }
+
+ return verify_gimple_reference (op);
+ }
+
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+
+ if (!is_gimple_val (op0) || !is_gimple_val (op1))
+ {
+ error ("invalid operands in truth expression");
+ return true;
+ }
+
+ /* We allow any kind of integral typed argument and result. */
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (op0))
+ || !INTEGRAL_TYPE_P (TREE_TYPE (op1))
+ || !INTEGRAL_TYPE_P (type))
+ {
+ error ("type mismatch in binary truth expression");
+ debug_generic_stmt (type);
+ debug_generic_stmt (TREE_TYPE (op0));
+ debug_generic_stmt (TREE_TYPE (op1));
+ return true;
+ }
+
+ return false;
+ }
+
+ case TRUTH_NOT_EXPR:
+ {
+ tree op = TREE_OPERAND (expr, 0);
+
+ if (!is_gimple_val (op))
+ {
+ 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 (TREE_TYPE (op))
+ || !INTEGRAL_TYPE_P (type))
+ {
+ error ("type mismatch in not expression");
+ debug_generic_expr (TREE_TYPE (expr));
+ debug_generic_expr (TREE_TYPE (op));
+ return true;
+ }
+
+ return false;
+ }
+
+ case CALL_EXPR:
+ /* FIXME. The C frontend passes unpromoted arguments in case it
+ didn't see a function declaration before the call. */
+ return false;
+
+ default:;
+ }
+
+ /* Generic handling via classes. */
+ switch (TREE_CODE_CLASS (TREE_CODE (expr)))
+ {
+ case tcc_unary:
+ return verify_gimple_unary_expr (expr);
+
+ case tcc_binary:
+ return verify_gimple_binary_expr (expr);
+
+ case tcc_reference:
+ return verify_gimple_reference (expr);
+
+ case tcc_comparison:
+ {
+ tree op0 = TREE_OPERAND (expr, 0);
+ tree op1 = TREE_OPERAND (expr, 1);
+ if (!is_gimple_val (op0) || !is_gimple_val (op1))
+ {
+ error ("invalid operands in comparison 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 (TREE_TYPE (op0), TREE_TYPE (op1))
+ && !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0))
+ && (!POINTER_TYPE_P (TREE_TYPE (op0))
+ || !POINTER_TYPE_P (TREE_TYPE (op1))
+ || TYPE_MODE (TREE_TYPE (op0)) != TYPE_MODE (TREE_TYPE (op1))))
+ || !INTEGRAL_TYPE_P (type))
+ {
+ error ("type mismatch in comparison expression");
+ debug_generic_stmt (TREE_TYPE (expr));
+ debug_generic_stmt (TREE_TYPE (op0));
+ debug_generic_stmt (TREE_TYPE (op1));
+ return true;
+ }
+ break;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return false;
+}
+
+/* Verify the GIMPLE assignment statement STMT. Returns true if there
+ is an error, otherwise false. */
+
+static bool
+verify_gimple_modify_stmt (const_tree stmt)
+{
+ tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
+ tree rhs = GIMPLE_STMT_OPERAND (stmt, 1);
+
+ gcc_assert (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT);
+
+ if (!useless_type_conversion_p (TREE_TYPE (lhs),
+ TREE_TYPE (rhs)))
+ {
+ error ("non-trivial conversion at assignment");
+ debug_generic_expr (TREE_TYPE (lhs));
+ debug_generic_expr (TREE_TYPE (rhs));
+ return true;
+ }
+
+ /* Loads/stores from/to a variable are ok. */
+ if ((is_gimple_val (lhs)
+ && is_gimple_variable (rhs))
+ || (is_gimple_val (rhs)
+ && is_gimple_variable (lhs)))
+ return false;
+
+ /* Aggregate copies are ok. */
+ if (!is_gimple_reg_type (TREE_TYPE (lhs))
+ && !is_gimple_reg_type (TREE_TYPE (rhs)))
+ return false;
+
+ /* We might get 'loads' from a parameter which is not a gimple value. */
+ if (TREE_CODE (rhs) == PARM_DECL)
+ return verify_gimple_expr (lhs);
+
+ if (!is_gimple_variable (lhs)
+ && verify_gimple_expr (lhs))
+ return true;
+
+ if (!is_gimple_variable (rhs)
+ && verify_gimple_expr (rhs))
+ return true;
+
+ return false;
+}
+
+/* Verify the GIMPLE statement STMT. Returns true if there is an
+ error, otherwise false. */
+
+static bool
+verify_gimple_stmt (tree stmt)
+{
+ if (!is_gimple_stmt (stmt))
+ {
+ error ("is not a valid GIMPLE statement");
+ return true;
+ }
+
+ if (OMP_DIRECTIVE_P (stmt))
+ {
+ /* OpenMP directives are validated by the FE and never operated
+ on by the optimizers. Furthermore, OMP_FOR may contain
+ non-gimple expressions when the main index variable has had
+ its address taken. This does not affect the loop itself
+ because the header of an OMP_FOR is merely used to determine
+ how to setup the parallel iteration. */
+ return false;
+ }
+
+ switch (TREE_CODE (stmt))
+ {
+ case GIMPLE_MODIFY_STMT:
+ return verify_gimple_modify_stmt (stmt);
+
+ case GOTO_EXPR:
+ case LABEL_EXPR:
+ return false;
+
+ case SWITCH_EXPR:
+ if (!is_gimple_val (TREE_OPERAND (stmt, 0)))
+ {
+ error ("invalid operand to switch statement");
+ debug_generic_expr (TREE_OPERAND (stmt, 0));
+ }
+ return false;
+
+ case RETURN_EXPR:
+ {
+ tree op = TREE_OPERAND (stmt, 0);
+
+ if (TREE_CODE (TREE_TYPE (stmt)) != VOID_TYPE)
+ {
+ error ("type error in return expression");
+ return true;
+ }
+
+ if (op == NULL_TREE
+ || TREE_CODE (op) == RESULT_DECL)
+ return false;
+
+ return verify_gimple_modify_stmt (op);
+ }
+
+ case CALL_EXPR:
+ case COND_EXPR:
+ return verify_gimple_expr (stmt);
+
+ case NOP_EXPR:
+ case CHANGE_DYNAMIC_TYPE_EXPR:
+ case ASM_EXPR:
+ return false;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Verify the GIMPLE statements inside the statement list STMTS. */
+
+void
+verify_gimple_1 (tree stmts)
+{
+ tree_stmt_iterator tsi;
+
+ for (tsi = tsi_start (stmts); !tsi_end_p (tsi); tsi_next (&tsi))
+ {
+ tree stmt = tsi_stmt (tsi);
+
+ switch (TREE_CODE (stmt))
+ {
+ case BIND_EXPR:
+ verify_gimple_1 (BIND_EXPR_BODY (stmt));
+ break;
+
+ case TRY_CATCH_EXPR:
+ case TRY_FINALLY_EXPR:
+ verify_gimple_1 (TREE_OPERAND (stmt, 0));
+ verify_gimple_1 (TREE_OPERAND (stmt, 1));
+ break;
+
+ case CATCH_EXPR:
+ verify_gimple_1 (CATCH_BODY (stmt));
+ break;
+
+ case EH_FILTER_EXPR:
+ verify_gimple_1 (EH_FILTER_FAILURE (stmt));
+ break;
+
+ default:
+ if (verify_gimple_stmt (stmt))
+ debug_generic_expr (stmt);
+ }
+ }
+}
+
+/* Verify the GIMPLE statements inside the current function. */
+
+void
+verify_gimple (void)
+{
+ verify_gimple_1 (BIND_EXPR_BODY (DECL_SAVED_TREE (cfun->decl)));
+}
+
+/* Verify STMT, return true if STMT is not in GIMPLE form.
+ TODO: Implement type checking. */
+
+static bool
+verify_stmt (tree stmt, bool last_in_block)
+{
+ tree addr;
+
+ if (OMP_DIRECTIVE_P (stmt))
+ {
+ /* OpenMP directives are validated by the FE and never operated
+ on by the optimizers. Furthermore, OMP_FOR may contain
+ non-gimple expressions when the main index variable has had
+ its address taken. This does not affect the loop itself
+ because the header of an OMP_FOR is merely used to determine
+ how to setup the parallel iteration. */
+ return false;
+ }
+
+ if (!is_gimple_stmt (stmt))
+ {
+ error ("is not a valid GIMPLE statement");
+ goto fail;
+ }
+
+ addr = walk_tree (&stmt, verify_expr, NULL, NULL);
+ if (addr)
+ {
+ debug_generic_stmt (addr);
+ return true;
+ }
+
+ /* If the statement is marked as part of an EH region, then it is
+ expected that the statement could throw. Verify that when we
+ have optimizations that simplify statements such that we prove
+ that they cannot throw, that we update other data structures
+ to match. */
+ if (lookup_stmt_eh_region (stmt) >= 0)
+ {
+ if (!tree_could_throw_p (stmt))
+ {
+ error ("statement marked for throw, but doesn%'t");
+ goto fail;
+ }
+ if (!last_in_block && tree_can_throw_internal (stmt))
+ {
+ error ("statement marked for throw in middle of block");
+ goto fail;
+ }
+ }
+
+ return false;
+
+ fail:
+ debug_generic_stmt (stmt);
+ return true;
+}
+
+
+/* Return true when the T can be shared. */
+
+static bool
+tree_node_can_be_shared (tree t)
+{
+ if (IS_TYPE_OR_DECL_P (t)
+ || is_gimple_min_invariant (t)
+ || TREE_CODE (t) == SSA_NAME
+ || t == error_mark_node
+ || TREE_CODE (t) == IDENTIFIER_NODE)
+ return true;
+
+ if (TREE_CODE (t) == CASE_LABEL_EXPR)
+ return true;
+
+ while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
+ && is_gimple_min_invariant (TREE_OPERAND (t, 1)))
+ || TREE_CODE (t) == COMPONENT_REF
+ || TREE_CODE (t) == REALPART_EXPR
+ || TREE_CODE (t) == IMAGPART_EXPR)
+ t = TREE_OPERAND (t, 0);
+
+ if (DECL_P (t))
+ return true;
+
+ return false;
+}
+
+
+/* Called via walk_trees. Verify tree sharing. */
+
+static tree
+verify_node_sharing (tree * tp, int *walk_subtrees, void *data)
+{
+ struct pointer_set_t *visited = (struct pointer_set_t *) data;
+
+ if (tree_node_can_be_shared (*tp))
+ {
+ *walk_subtrees = false;
+ return NULL;
+ }
+
+ if (pointer_set_insert (visited, *tp))
+ return *tp;
+
+ return NULL;
+}
+
+
+/* Helper function for verify_gimple_tuples. */
+
+static tree
+verify_gimple_tuples_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ switch (TREE_CODE (*tp))
+ {
+ case MODIFY_EXPR:
+ error ("unexpected non-tuple");
+ debug_tree (*tp);
+ gcc_unreachable ();
+ return NULL_TREE;
+
+ default:
+ return NULL_TREE;
+ }
+}
+
+/* Verify that there are no trees that should have been converted to
+ gimple tuples. Return true if T contains a node that should have
+ been converted to a gimple tuple, but hasn't. */
+
+static bool
+verify_gimple_tuples (tree t)
+{
+ return walk_tree (&t, verify_gimple_tuples_1, NULL, NULL) != NULL;
+}
+
+static bool eh_error_found;
+static int
+verify_eh_throw_stmt_node (void **slot, void *data)
+{
+ struct throw_stmt_node *node = (struct throw_stmt_node *)*slot;
+ struct pointer_set_t *visited = (struct pointer_set_t *) data;
+
+ if (!pointer_set_contains (visited, node->stmt))
+ {
+ error ("Dead STMT in EH table");
+ debug_generic_stmt (node->stmt);
+ eh_error_found = true;
+ }
+ return 0;
+}