diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rwxr-xr-x | configure | 4 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | gcc/config.in | 6 | ||||
-rwxr-xr-x | gcc/configure | 22 | ||||
-rw-r--r-- | gcc/configure.ac | 21 | ||||
-rw-r--r-- | gcc/gimplify.c | 100 | ||||
-rw-r--r-- | gcc/tree-cfg.c | 704 | ||||
-rw-r--r-- | gcc/tree-flow.h | 2 |
9 files changed, 758 insertions, 110 deletions
diff --git a/ChangeLog b/ChangeLog index 17832209062..94ab187effc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-07-26 Richard Guenther <rguenther@suse.de> + + * configure.ac: Add types checking to stage1 checking flags. + * configure: Regenerate. + 2007-07-17 Nick Clifton <nickc@redhat.com> * COPYING3: New file. Contains version 3 of the GNU General diff --git a/configure b/configure index 1a43f9e69a6..df7479320e4 100755 --- a/configure +++ b/configure @@ -11967,9 +11967,9 @@ if test "${enable_stage1_checking+set}" = set; then stage1_checking=--enable-checking=${enable_stage1_checking} else if test "x$enable_checking" = xno; then - stage1_checking=--enable-checking + stage1_checking=--enable-checking=yes,types else - stage1_checking=--enable-checking${enable_checking+=}$enable_checking + stage1_checking=--enable-checking=types${enable_checking+,}$enable_checking fi fi; diff --git a/configure.ac b/configure.ac index da88113f068..2500cc6f351 100644 --- a/configure.ac +++ b/configure.ac @@ -2593,9 +2593,9 @@ AC_ARG_ENABLE(stage1-checking, of the compiler], [stage1_checking=--enable-checking=${enable_stage1_checking}], [if test "x$enable_checking" = xno; then - stage1_checking=--enable-checking + stage1_checking=--enable-checking=yes,types else - stage1_checking=--enable-checking${enable_checking+=}$enable_checking + stage1_checking=--enable-checking=types${enable_checking+,}$enable_checking fi]) AC_SUBST(stage1_checking) diff --git a/gcc/config.in b/gcc/config.in index 30b1c426659..aba1bb54c5b 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -121,6 +121,12 @@ #endif +/* Define if you want all gimple types to be verified after gimplifiation. */ +#ifndef USED_FOR_TARGET +#undef ENABLE_TYPES_CHECKING +#endif + + /* Define if you want to run subprograms and generated programs through valgrind (a memory checker). This is extremely expensive. */ #ifndef USED_FOR_TARGET diff --git a/gcc/configure b/gcc/configure index 1b72cdb9442..376a3e394c4 100755 --- a/gcc/configure +++ b/gcc/configure @@ -868,7 +868,7 @@ Optional Features: enable only specific categories of checks. Categories are: yes,no,all,none,release. Flags are: assert,df,fold,gc,gcac,misc, - rtlflag,rtl,runtime,tree,valgrind. + rtlflag,rtl,runtime,tree,valgrind,types. --enable-mapped-location location_t is fileline integer cookie --enable-coverage=LEVEL enable compiler's code coverage collection. @@ -6429,22 +6429,26 @@ do ac_fold_checking= ; ac_gc_checking=1 ; ac_gc_always_collect= ; ac_rtl_checking= ; ac_rtlflag_checking=1 ; ac_runtime_checking=1 ; - ac_tree_checking=1 ; ac_valgrind_checking= ;; + ac_tree_checking=1 ; ac_valgrind_checking= ; + ac_types_checking= ;; no|none) ac_assert_checking= ; ac_checking= ; ac_df_checking= ; ac_fold_checking= ; ac_gc_checking= ; ac_gc_always_collect= ; ac_rtl_checking= ; ac_rtlflag_checking= ; ac_runtime_checking= ; - ac_tree_checking= ; ac_valgrind_checking= ;; + ac_tree_checking= ; ac_valgrind_checking= ; + ac_types_checking= ;; all) ac_assert_checking=1 ; ac_checking=1 ; ac_df_checking=1 ; ac_fold_checking=1 ; ac_gc_checking=1 ; ac_gc_always_collect=1 ; ac_rtl_checking=1 ; ac_rtlflag_checking=1 ; ac_runtime_checking=1 ; - ac_tree_checking=1 ; ac_valgrind_checking= ;; + ac_tree_checking=1 ; ac_valgrind_checking= ; + ac_types_checking=1 ;; release) ac_assert_checking=1 ; ac_checking= ; ac_df_checking= ; ac_fold_checking= ; ac_gc_checking= ; ac_gc_always_collect= ; ac_rtl_checking= ; ac_rtlflag_checking= ; ac_runtime_checking=1 ; - ac_tree_checking= ; ac_valgrind_checking= ;; + ac_tree_checking= ; ac_valgrind_checking= ; + ac_types_checking= ;; # these enable particular checks assert) ac_assert_checking=1 ;; df) ac_df_checking=1 ;; @@ -6456,6 +6460,7 @@ do rtlflag) ac_rtlflag_checking=1 ;; runtime) ac_runtime_checking=1 ;; tree) ac_tree_checking=1 ;; + types) ac_types_checking=1 ;; valgrind) ac_valgrind_checking=1 ;; *) { { echo "$as_me:$LINENO: error: unknown check category $check" >&5 echo "$as_me: error: unknown check category $check" >&2;} @@ -6504,6 +6509,13 @@ _ACEOF TREEBROWSER=tree-browser.o fi +if test x$ac_types_checking != x ; then + +cat >>confdefs.h <<\_ACEOF +#define ENABLE_TYPES_CHECKING 1 +_ACEOF + +fi if test x$ac_rtl_checking != x ; then diff --git a/gcc/configure.ac b/gcc/configure.ac index c7b9bc99025..b50bba56fb1 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -346,7 +346,7 @@ AC_ARG_ENABLE(checking, enable only specific categories of checks. Categories are: yes,no,all,none,release. Flags are: assert,df,fold,gc,gcac,misc, - rtlflag,rtl,runtime,tree,valgrind.], + rtlflag,rtl,runtime,tree,valgrind,types.], [ac_checking_flags="${enableval}"],[ # Determine the default checks. if test x$is_release = x ; then @@ -363,22 +363,26 @@ do ac_fold_checking= ; ac_gc_checking=1 ; ac_gc_always_collect= ; ac_rtl_checking= ; ac_rtlflag_checking=1 ; ac_runtime_checking=1 ; - ac_tree_checking=1 ; ac_valgrind_checking= ;; + ac_tree_checking=1 ; ac_valgrind_checking= ; + ac_types_checking= ;; no|none) ac_assert_checking= ; ac_checking= ; ac_df_checking= ; ac_fold_checking= ; ac_gc_checking= ; ac_gc_always_collect= ; ac_rtl_checking= ; ac_rtlflag_checking= ; ac_runtime_checking= ; - ac_tree_checking= ; ac_valgrind_checking= ;; + ac_tree_checking= ; ac_valgrind_checking= ; + ac_types_checking= ;; all) ac_assert_checking=1 ; ac_checking=1 ; ac_df_checking=1 ; ac_fold_checking=1 ; ac_gc_checking=1 ; ac_gc_always_collect=1 ; ac_rtl_checking=1 ; ac_rtlflag_checking=1 ; ac_runtime_checking=1 ; - ac_tree_checking=1 ; ac_valgrind_checking= ;; + ac_tree_checking=1 ; ac_valgrind_checking= ; + ac_types_checking=1 ;; release) ac_assert_checking=1 ; ac_checking= ; ac_df_checking= ; ac_fold_checking= ; ac_gc_checking= ; ac_gc_always_collect= ; ac_rtl_checking= ; ac_rtlflag_checking= ; ac_runtime_checking=1 ; - ac_tree_checking= ; ac_valgrind_checking= ;; + ac_tree_checking= ; ac_valgrind_checking= ; + ac_types_checking= ;; # these enable particular checks assert) ac_assert_checking=1 ;; df) ac_df_checking=1 ;; @@ -390,6 +394,7 @@ do rtlflag) ac_rtlflag_checking=1 ;; runtime) ac_runtime_checking=1 ;; tree) ac_tree_checking=1 ;; + types) ac_types_checking=1 ;; valgrind) ac_valgrind_checking=1 ;; *) AC_MSG_ERROR(unknown check category $check) ;; esac @@ -426,6 +431,12 @@ if test x$ac_tree_checking != x ; then ]) TREEBROWSER=tree-browser.o fi +if test x$ac_types_checking != x ; then + AC_DEFINE(ENABLE_TYPES_CHECKING, 1, +[Define if you want all gimple types to be verified after gimplifiation. + This is cheap. + ]) +fi AC_SUBST(TREEBROWSER) if test x$ac_rtl_checking != x ; then AC_DEFINE(ENABLE_RTL_CHECKING, 1, diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 4e0e16aad12..fdf34d18ea8 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -112,9 +112,6 @@ typedef struct gimple_temp_hash_elt /* Forward declarations. */ static enum gimplify_status gimplify_compound_expr (tree *, tree *, bool); -#ifdef ENABLE_CHECKING -static bool cpt_same_type (tree a, tree b); -#endif /* Mark X addressable. Unlike the langhook we expect X to be in gimple form and we don't do any syntax checking. */ @@ -3985,19 +3982,7 @@ gimplify_addr_expr (tree *expr_p, tree *pre_p, tree *post_p) tree t_op00 = TREE_TYPE (op00); if (!useless_type_conversion_p (t_expr, t_op00)) - { -#ifdef ENABLE_CHECKING - tree t_op0 = TREE_TYPE (op0); - gcc_assert (POINTER_TYPE_P (t_expr) - && (cpt_same_type (TREE_TYPE (t_expr), t_op0) - || (TREE_CODE (t_op0) == ARRAY_TYPE - && cpt_same_type (TREE_TYPE (t_expr), - TREE_TYPE (t_op0)))) - && POINTER_TYPE_P (t_op00) - && cpt_same_type (t_op0, TREE_TYPE (t_op00))); -#endif - op00 = fold_convert (TREE_TYPE (expr), op00); - } + op00 = fold_convert (TREE_TYPE (expr), op00); *expr_p = op00; ret = GS_OK; } @@ -6393,84 +6378,6 @@ gimplify_one_sizepos (tree *expr_p, tree *stmt_p) } } -#ifdef ENABLE_CHECKING -/* Compare types A and B for a "close enough" match. */ - -static bool -cpt_same_type (tree a, tree b) -{ - if (useless_type_conversion_p (a, b)) - return true; - - /* ??? The C++ FE decomposes METHOD_TYPES to FUNCTION_TYPES and doesn't - link them together. This routine is intended to catch type errors - that will affect the optimizers, and the optimizers don't add new - dereferences of function pointers, so ignore it. */ - if ((TREE_CODE (a) == FUNCTION_TYPE || TREE_CODE (a) == METHOD_TYPE) - && (TREE_CODE (b) == FUNCTION_TYPE || TREE_CODE (b) == METHOD_TYPE)) - return true; - - /* ??? The C FE pushes type qualifiers after the fact into the type of - the element from the type of the array. See build_unary_op's handling - of ADDR_EXPR. This seems wrong -- if we were going to do this, we - should have done it when creating the variable in the first place. - Alternately, why aren't the two array types made variants? */ - if (TREE_CODE (a) == ARRAY_TYPE && TREE_CODE (b) == ARRAY_TYPE) - return cpt_same_type (TREE_TYPE (a), TREE_TYPE (b)); - - /* And because of those, we have to recurse down through pointers. */ - if (POINTER_TYPE_P (a) && POINTER_TYPE_P (b)) - return cpt_same_type (TREE_TYPE (a), TREE_TYPE (b)); - - return false; -} - -/* Check for some cases of the front end missing cast expressions. - The type of a dereference should correspond to the pointer type; - similarly the type of an address should match its object. */ - -static tree -check_pointer_types_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) -{ - tree t = *tp; - tree ptype, otype, dtype; - - switch (TREE_CODE (t)) - { - case INDIRECT_REF: - case ARRAY_REF: - otype = TREE_TYPE (t); - ptype = TREE_TYPE (TREE_OPERAND (t, 0)); - dtype = TREE_TYPE (ptype); - gcc_assert (cpt_same_type (otype, dtype)); - break; - - case ADDR_EXPR: - ptype = TREE_TYPE (t); - otype = TREE_TYPE (TREE_OPERAND (t, 0)); - dtype = TREE_TYPE (ptype); - if (!cpt_same_type (dtype, otype)) - { - /* &array is allowed to produce a pointer to the element, rather than - a pointer to the array type. We must allow this in order to - properly represent assigning the address of an array in C into - pointer to the element type. */ - gcc_assert (TREE_CODE (otype) == ARRAY_TYPE - && POINTER_TYPE_P (ptype) - && cpt_same_type (dtype, TREE_TYPE (otype))); - break; - } - break; - - default: - return NULL_TREE; - } - - - return NULL_TREE; -} -#endif /* Gimplify the body of statements pointed to by BODY_P. FNDECL is the function decl containing BODY. */ @@ -6539,8 +6446,9 @@ gimplify_body (tree *body_p, tree fndecl, bool do_parms) pop_gimplify_context (body); gcc_assert (gimplify_ctxp == NULL); -#ifdef ENABLE_CHECKING - walk_tree (body_p, check_pointer_types_r, NULL, NULL); +#ifdef ENABLE_TYPES_CHECKING + if (!errorcount && !sorrycount) + verify_gimple_1 (BIND_EXPR_BODY (*body_p)); #endif timevar_pop (TV_TREE_GIMPLIFY); diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 17808e77e7c..26a5ac9c06f 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -3345,6 +3345,710 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) #undef CHECK_OP } +/* Verifies if EXPR is a valid GIMPLE unary expression. Returns true + if there is an error, otherwise false. */ + +static bool +verify_gimple_unary_expr (tree expr) +{ + tree op = TREE_OPERAND (expr, 0); + tree type = TREE_TYPE (expr); + + if (!is_gimple_val (op)) + { + error ("invalid operand in unary expression"); + return true; + } + + /* For general unary expressions we have the operations type + as the effective type the operation is carried out on. So all + we need to require is that the operand is trivially convertible + to that type. */ + if (!useless_type_conversion_p (type, TREE_TYPE (op))) + { + error ("type mismatch in unary expression"); + debug_generic_expr (type); + debug_generic_expr (TREE_TYPE (op)); + return true; + } + + return false; +} + +/* Verifies if EXPR is a valid GIMPLE binary expression. Returns true + if there is an error, otherwise false. */ + +static bool +verify_gimple_binary_expr (tree expr) +{ + tree op0 = TREE_OPERAND (expr, 0); + tree op1 = TREE_OPERAND (expr, 1); + tree type = TREE_TYPE (expr); + + if (!is_gimple_val (op0) || !is_gimple_val (op1)) + { + error ("invalid operands in binary expression"); + return true; + } + + /* For general binary expressions we have the operations type + as the effective type the operation is carried out on. So all + we need to require is that both operands are trivially convertible + to that type. */ + if (!useless_type_conversion_p (type, TREE_TYPE (op0)) + || !useless_type_conversion_p (type, TREE_TYPE (op1))) + { + error ("type mismatch in binary expression"); + debug_generic_stmt (type); + debug_generic_stmt (TREE_TYPE (op0)); + debug_generic_stmt (TREE_TYPE (op1)); + return true; + } + + return false; +} + +/* Verify if EXPR is either a GIMPLE ID or a GIMPLE indirect reference. + Returns true if there is an error, otherwise false. */ + +static bool +verify_gimple_min_lval (tree expr) +{ + tree op; + + 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) + { + error ("invalid expression for min lvalue"); + return true; + } + + op = TREE_OPERAND (expr, 0); + if (!is_gimple_val (op)) + { + error ("invalid operand in indirect reference"); + debug_generic_stmt (op); + return true; + } + if (!useless_type_conversion_p (TREE_TYPE (expr), + TREE_TYPE (TREE_TYPE (op)))) + { + error ("type mismatch in indirect reference"); + debug_generic_stmt (TREE_TYPE (expr)); + debug_generic_stmt (TREE_TYPE (TREE_TYPE (op))); + return true; + } + + return false; +} + +/* Verify if EXPR is a valid GIMPLE reference expression. Returns true + if there is an error, otherwise false. */ + +static bool +verify_gimple_reference (tree expr) +{ + while (handled_component_p (expr)) + { + tree op = TREE_OPERAND (expr, 0); + + if (TREE_CODE (expr) == ARRAY_REF + || TREE_CODE (expr) == ARRAY_RANGE_REF) + { + if (!is_gimple_val (TREE_OPERAND (expr, 1)) + || (TREE_OPERAND (expr, 2) + && !is_gimple_val (TREE_OPERAND (expr, 2))) + || (TREE_OPERAND (expr, 3) + && !is_gimple_val (TREE_OPERAND (expr, 3)))) + { + error ("invalid operands to array reference"); + debug_generic_stmt (expr); + return true; + } + } + + /* Verify if the reference array element types are compatible. */ + if (TREE_CODE (expr) == ARRAY_REF + && !useless_type_conversion_p (TREE_TYPE (expr), + TREE_TYPE (TREE_TYPE (op)))) + { + error ("type mismatch in array reference"); + debug_generic_stmt (TREE_TYPE (expr)); + debug_generic_stmt (TREE_TYPE (TREE_TYPE (op))); + return true; + } + if (TREE_CODE (expr) == ARRAY_RANGE_REF + && !useless_type_conversion_p (TREE_TYPE (TREE_TYPE (expr)), + TREE_TYPE (TREE_TYPE (op)))) + { + error ("type mismatch in array range reference"); + debug_generic_stmt (TREE_TYPE (TREE_TYPE (expr))); + debug_generic_stmt (TREE_TYPE (TREE_TYPE (op))); + return true; + } + + if ((TREE_CODE (expr) == REALPART_EXPR + || TREE_CODE (expr) == IMAGPART_EXPR) + && !useless_type_conversion_p (TREE_TYPE (expr), + TREE_TYPE (TREE_TYPE (op)))) + { + error ("type mismatch in real/imagpart reference"); + debug_generic_stmt (TREE_TYPE (expr)); + debug_generic_stmt (TREE_TYPE (TREE_TYPE (op))); + return true; + } + + if (TREE_CODE (expr) == COMPONENT_REF + && !useless_type_conversion_p (TREE_TYPE (expr), + TREE_TYPE (TREE_OPERAND (expr, 1)))) + { + error ("type mismatch in component reference"); + debug_generic_stmt (TREE_TYPE (expr)); + debug_generic_stmt (TREE_TYPE (TREE_OPERAND (expr, 1))); + return true; + } + + /* For VIEW_CONVERT_EXPRs which are allowed here, too, there + is nothing to verify. Gross mismatches at most invoke + undefined behavior. */ + + expr = op; + } + + return verify_gimple_min_lval (expr); +} + +/* Verify the GIMPLE expression EXPR. Returns true if there is an + error, otherwise false. */ + +static bool +verify_gimple_expr (tree expr) +{ + tree type = TREE_TYPE (expr); + + if (is_gimple_val (expr)) + return false; + + /* Special codes we cannot handle via their class. */ + switch (TREE_CODE (expr)) + { + 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. */ + if (INTEGRAL_TYPE_P (type) == INTEGRAL_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); + tree ptr_type; + if (!is_gimple_addressable (op)) + { + error ("invalid operand in unary expression"); + return true; + } + ptr_type = build_pointer_type (TREE_TYPE (op)); + if (!useless_type_conversion_p (type, ptr_type) + /* FIXME: a longstanding wart, &a == &a[0]. */ + && (TREE_CODE (TREE_TYPE (op)) != ARRAY_TYPE + || !useless_type_conversion_p (type, + build_pointer_type (TREE_TYPE (TREE_TYPE (op)))))) + { + error ("type mismatch in address expression"); + debug_generic_stmt (TREE_TYPE (expr)); + debug_generic_stmt (ptr_type); + 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 (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. */ diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index c314ed5bd73..e67bd0a50b5 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -752,6 +752,8 @@ extern void bsi_commit_edge_inserts (void); extern void notice_special_calls (tree); extern void clear_special_calls (void); extern void verify_stmts (void); +extern void verify_gimple (void); +extern void verify_gimple_1 (tree); extern tree tree_block_label (basic_block); extern void extract_true_false_edges_from_block (basic_block, edge *, edge *); extern bool tree_duplicate_sese_region (edge, edge, basic_block *, unsigned, |