OSDN Git Service

./:
[pf3gnuchains/gcc-fork.git] / gcc / tree-cfg.c
index 99978ef..4c7c0db 100644 (file)
@@ -1,5 +1,5 @@
 /* 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>
 
@@ -82,6 +82,14 @@ static struct cfg_stats_d cfg_stats;
 /* 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);
@@ -91,6 +99,9 @@ static void make_edges (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);
@@ -100,6 +111,7 @@ static inline bool stmt_starts_bb_p (gimple, gimple);
 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);
@@ -193,8 +205,11 @@ build_gimple_cfg (gimple_seq seq)
   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.  */
 
@@ -212,16 +227,20 @@ build_gimple_cfg (gimple_seq seq)
 #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;
 }
 
@@ -240,7 +259,8 @@ struct gimple_opt_pass pass_build_cfg =
   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 */
  }
 };
 
@@ -309,7 +329,7 @@ factor_computed_gotos (void)
 
              /* 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,
@@ -370,7 +390,29 @@ make_blocks (gimple_seq seq)
       /* 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;
@@ -623,7 +665,11 @@ make_edges (void)
        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)
@@ -633,6 +679,91 @@ make_edges (void)
   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.  */
 
@@ -644,10 +775,13 @@ make_cond_expr_edges (basic_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);
@@ -657,10 +791,18 @@ make_cond_expr_edges (basic_block bb)
   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);
@@ -766,8 +908,11 @@ static void
 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)
@@ -775,6 +920,7 @@ make_gimple_switch_edges (basic_block bb)
       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);
     }
 }
 
@@ -847,8 +993,12 @@ make_goto_expr_edges (basic_block 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;
     }
@@ -882,7 +1032,7 @@ static struct label_record
 
 /* 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)
@@ -1177,7 +1327,7 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b)
   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)
@@ -1254,9 +1404,6 @@ replace_uses_by (tree name, tree val)
 
   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);
@@ -1283,7 +1430,7 @@ replace_uses_by (tree name, tree 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);
@@ -1295,8 +1442,7 @@ replace_uses_by (tree name, tree val)
            }
 
          maybe_clean_or_replace_eh_stmt (stmt, stmt);
-
-         pop_stmt_changes (&stmt);
+         update_stmt (stmt);
        }
     }
 
@@ -1425,7 +1571,7 @@ gimple_merge_blocks (basic_block a, basic_block b)
 
 
 /* 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.  */
 
@@ -1539,7 +1685,8 @@ remove_useless_stmts_cond (gimple_stmt_iterator *gsi, struct rus_data *data)
   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;
 
@@ -1783,9 +1930,21 @@ remove_useless_stmts_bind (gimple_stmt_iterator *gsi, struct rus_data *data ATTR
          || (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);
@@ -2024,6 +2183,11 @@ remove_useless_stmts (void)
       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;
 }
 
@@ -2038,7 +2202,7 @@ struct gimple_opt_pass pass_remove_useless_stmts =
   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 */
@@ -2052,14 +2216,9 @@ struct gimple_opt_pass pass_remove_useless_stmts =
 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)
@@ -2646,6 +2805,17 @@ first_stmt (basic_block bb)
   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
@@ -2790,6 +2960,15 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
        }
       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)
@@ -2800,14 +2979,8 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
       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:
       {
@@ -2844,13 +3017,20 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
             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;
       }
@@ -3035,14 +3215,17 @@ verify_types_in_gimple_min_lval (tree expr)
   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))
     {
@@ -3062,11 +3245,12 @@ verify_types_in_gimple_min_lval (tree expr)
   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))
     {
@@ -3131,11 +3315,15 @@ verify_types_in_gimple_reference (tree 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)
@@ -3168,107 +3356,139 @@ valid_fixed_convert_types_p (tree type1, tree type2)
              || 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.  */
@@ -3293,12 +3513,6 @@ verify_types_in_gimple_assign (gimple stmt)
 
     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))
          {
@@ -3313,12 +3527,6 @@ verify_types_in_gimple_assign (gimple stmt)
 
     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");
@@ -3332,12 +3540,6 @@ verify_types_in_gimple_assign (gimple stmt)
 
     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");
@@ -3349,18 +3551,79 @@ verify_types_in_gimple_assign (gimple stmt)
         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");
@@ -3373,39 +3636,96 @@ verify_types_in_gimple_assign (gimple stmt)
        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)
@@ -3422,11 +3742,7 @@ verify_types_in_gimple_assign (gimple stmt)
 
     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))
@@ -3441,30 +3757,6 @@ verify_types_in_gimple_assign (gimple stmt)
        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 ();
@@ -3473,12 +3765,6 @@ verify_types_in_gimple_assign (gimple stmt)
     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)
@@ -3494,135 +3780,282 @@ verify_types_in_gimple_assign (gimple stmt)
        return false;
       }
 
-    case TRUTH_NOT_EXPR:
-      {
-       if (!is_gimple_val (rhs1))
-         {
-           error ("invalid operand in unary not");
-           return true;
-         }
+    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;
 
-       /* 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;
-         }
+    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;
 
-       return false;
-      }
+    default:
+      gcc_unreachable ();
+    }
 
-    /* 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;
-      }
+  if (!useless_type_conversion_p (lhs_type, rhs1_type)
+      || !useless_type_conversion_p (lhs_type, rhs2_type))
+    {
+      error ("type mismatch in binary expression");
+      debug_generic_stmt (lhs_type);
+      debug_generic_stmt (rhs1_type);
+      debug_generic_stmt (rhs2_type);
+      return true;
+    }
 
-    case OBJ_TYPE_REF:
-      /* FIXME.  */
-      return false;
+  return false;
+}
 
-    default:;
-    }
+/* 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;
 
-  /* Generic handling via classes.  */
-  switch (TREE_CODE_CLASS (rhs_code))
+  if (!useless_type_conversion_p (lhs_type, rhs1_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 ("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;
     }
 
@@ -3634,16 +4067,37 @@ verify_types_in_gimple_switch (gimple stmt)
    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;
 }
@@ -3669,38 +4123,39 @@ verify_types_in_gimple_stmt (gimple stmt)
   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 ();
@@ -3721,44 +4176,22 @@ verify_types_in_gimple_seq_2 (gimple_seq stmts)
 
       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:
          {
@@ -3835,7 +4268,7 @@ verify_stmt (gimple_stmt_iterator *gsi)
   if (addr)
     {
       debug_generic_expr (addr);
-      inform ("in statement");
+      inform (gimple_location (gsi_stmt (*gsi)), "in statement");
       debug_gimple_stmt (stmt);
       return true;
     }
@@ -3847,7 +4280,10 @@ verify_stmt (gimple_stmt_iterator *gsi)
      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;
@@ -3930,7 +4366,7 @@ verify_eh_throw_stmt_node (void **slot, void *data)
       debug_gimple_stmt (node->stmt);
       eh_error_found = true;
     }
-  return 0;
+  return 1;
 }
 
 
@@ -4001,6 +4437,14 @@ verify_stmts (void)
                  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); )
@@ -4020,6 +4464,7 @@ verify_stmts (void)
          if (gimple_bb (stmt) != bb)
            {
              error ("gimple_bb (stmt) is set to a wrong basic block");
+             debug_gimple_stmt (stmt);
              err |= true;
            }
 
@@ -4037,6 +4482,14 @@ verify_stmts (void)
            }
 
          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)
            {
@@ -4421,7 +4874,7 @@ gimple_block_label (basic_block bb)
        }
     }
 
-  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;
@@ -4488,10 +4941,13 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest)
   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.  */
@@ -4580,7 +5036,7 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest)
 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;
@@ -4718,7 +5174,6 @@ gimple_duplicate_bb (basic_block bb)
         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);
@@ -4890,7 +5345,7 @@ gimple_duplicate_sese_region (edge entry, edge exit,
       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.  */
@@ -5049,7 +5504,7 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU
       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.  */
@@ -5376,19 +5831,6 @@ mark_virtual_ops_in_bb (basic_block bb)
     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
@@ -5495,7 +5937,7 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
          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);
            }
@@ -5527,6 +5969,23 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
       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
@@ -5574,7 +6033,7 @@ new_label_mapper (tree decl, void *data)
   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;
@@ -5599,6 +6058,8 @@ replace_block_vars_by_duplicates (tree block, struct pointer_map_t *vars_map,
   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)
        {
@@ -5738,11 +6199,6 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
 
   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;
@@ -5874,7 +6330,7 @@ dump_function_to_file (tree fn, FILE *file, int flags)
   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;
@@ -6116,7 +6572,7 @@ print_loops (FILE *file, int verbosity)
 {
   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);
 }
@@ -6196,15 +6652,20 @@ need_fake_edge_p (gimple t)
       && 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;
 
@@ -6364,20 +6825,6 @@ gimple_purge_dead_abnormal_call_edges (basic_block bb)
   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.*/
@@ -6437,7 +6884,7 @@ remove_edge_and_dominated_blocks (edge e)
                    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)
@@ -6536,7 +6983,13 @@ gimple_purge_all_dead_eh_edges (const_bitmap blocks)
 
   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;
@@ -6683,10 +7136,31 @@ split_critical_edges (void)
   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;
@@ -6707,7 +7181,7 @@ struct gimple_opt_pass pass_split_crit_edges =
   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 */
  }
 };
 
@@ -6850,7 +7324,7 @@ struct gimple_opt_pass pass_warn_function_return =
   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 */
@@ -6884,7 +7358,7 @@ struct gimple_opt_pass pass_warn_function_noreturn =
   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 */