]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/tree-sra.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / tree-sra.c
index 49f9001f7fb9bd63a4c8be40aaf0f29206bd86e9..0363c5efb7861499c032e63bedd6dccfddeceb81 100644 (file)
@@ -1,7 +1,7 @@
 /* Scalar Replacement of Aggregates (SRA) converts some structure
    references into scalar references, exposing them to the scalar
    optimizers.
-   Copyright (C) 2008-2020 Free Software Foundation, Inc.
+   Copyright (C) 2008-2021 Free Software Foundation, Inc.
    Contributed by Martin Jambor <mjambor@suse.cz>
 
 This file is part of GCC.
@@ -285,6 +285,9 @@ static object_allocator<assign_link> assign_link_pool ("SRA links");
 /* Base (tree) -> Vector (vec<access_p> *) map.  */
 static hash_map<tree, auto_vec<access_p> > *base_access_vec;
 
+/* Hash to limit creation of artificial accesses */
+static hash_map<tree, unsigned> *propagation_budget;
+
 /* Candidate hash table helpers.  */
 
 struct uid_decl_hasher : nofree_ptr_hash <tree_node>
@@ -381,6 +384,13 @@ static struct
 
   /* Numbber of components created when splitting aggregate parameters.  */
   int param_reductions_created;
+
+  /* Number of deferred_init calls that are modified.  */
+  int deferred_init;
+
+  /* Number of deferred_init calls that are created by
+     generate_subtree_deferred_init.  */
+  int subtree_deferred_init;
 } sra_stats;
 
 static void
@@ -912,6 +922,12 @@ create_access (tree expr, gimple *stmt, bool write)
   if (!DECL_P (base) || !bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
     return NULL;
 
+  if (write && TREE_READONLY (base))
+    {
+      disqualify_candidate (base, "Encountered a store to a read-only decl.");
+      return NULL;
+    }
+
   HOST_WIDE_INT offset, size, max_size;
   if (!poffset.is_constant (&offset)
       || !psize.is_constant (&size)
@@ -928,11 +944,21 @@ create_access (tree expr, gimple *stmt, bool write)
     }
   if (size == 0)
     return NULL;
+  if (offset < 0)
+    {
+      disqualify_candidate (base, "Encountered a negative offset access.");
+      return NULL;
+    }
   if (size < 0)
     {
       disqualify_candidate (base, "Encountered an unconstrained access.");
       return NULL;
     }
+  if (offset + size > tree_to_shwi (DECL_SIZE (base)))
+    {
+      disqualify_candidate (base, "Encountered an access beyond the base.");
+      return NULL;
+    }
 
   access = create_access_1 (base, offset, size);
   access->expr = expr;
@@ -1369,7 +1395,14 @@ scan_function (void)
 
              t = gimple_call_lhs (stmt);
              if (t && !disqualify_if_bad_bb_terminating_stmt (stmt, t, NULL))
-               ret |= build_access_from_expr (t, stmt, true);
+               {
+                 /* If the STMT is a call to DEFERRED_INIT, avoid setting
+                    cannot_scalarize_away_bitmap.  */
+                 if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
+                   ret |= !!build_access_from_expr_1 (t, stmt, true);
+                 else
+                   ret |= build_access_from_expr (t, stmt, true);
+               }
              break;
 
            case GIMPLE_ASM:
@@ -1664,6 +1697,7 @@ build_ref_for_model (location_t loc, tree base, HOST_WIDE_INT offset,
                     struct access *model, gimple_stmt_iterator *gsi,
                     bool insert_after)
 {
+  gcc_assert (offset >= 0);
   if (TREE_CODE (model->expr) == COMPONENT_REF
       && DECL_BIT_FIELD (TREE_OPERAND (model->expr, 1)))
     {
@@ -1871,12 +1905,12 @@ maybe_add_sra_candidate (tree var)
       reject (var, "has incomplete type");
       return false;
     }
-  if (!tree_fits_uhwi_p (TYPE_SIZE (type)))
+  if (!tree_fits_shwi_p (TYPE_SIZE (type)))
     {
       reject (var, "type size not fixed");
       return false;
     }
-  if (tree_to_uhwi (TYPE_SIZE (type)) == 0)
+  if (tree_to_shwi (TYPE_SIZE (type)) == 0)
     {
       reject (var, "type size is zero");
       return false;
@@ -2142,7 +2176,7 @@ sort_and_splice_var_accesses (tree var)
 /* Create a variable for the given ACCESS which determines the type, name and a
    few other properties.  Return the variable declaration and store it also to
    ACCESS->replacement.  REG_TREE is used when creating a declaration to base a
-   default-definition SSA name on on in order to facilitate an uninitialized
+   default-definition SSA name on in order to facilitate an uninitialized
    warning.  It is used instead of the actual ACCESS type if that is not of a
    gimple register type.  */
 
@@ -2165,15 +2199,9 @@ create_access_replacement (struct access *access, tree reg_type = NULL_TREE)
        variant.  This avoids issues with weirdo ABIs like AAPCS.  */
     repl = create_tmp_var (build_qualified_type (TYPE_MAIN_VARIANT (type),
                                                 TYPE_QUALS (type)), "SR");
-  if (TREE_CODE (type) == COMPLEX_TYPE
-      || TREE_CODE (type) == VECTOR_TYPE)
-    {
-      if (!access->grp_partial_lhs)
-       DECL_GIMPLE_REG_P (repl) = 1;
-    }
-  else if (access->grp_partial_lhs
-          && is_gimple_reg_type (type))
-    TREE_ADDRESSABLE (repl) = 1;
+  if (access->grp_partial_lhs
+      && is_gimple_reg_type (type))
+    DECL_NOT_GIMPLE_REG_P (repl) = 1;
 
   DECL_SOURCE_LOCATION (repl) = DECL_SOURCE_LOCATION (access->base);
   DECL_ARTIFICIAL (repl) = 1;
@@ -2232,12 +2260,12 @@ create_access_replacement (struct access *access, tree reg_type = NULL_TREE)
          DECL_HAS_DEBUG_EXPR_P (repl) = 1;
        }
       if (access->grp_no_warning)
-       TREE_NO_WARNING (repl) = 1;
+       suppress_warning (repl /* Be more selective! */);
       else
-       TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
+       copy_warning (repl, access->base);
     }
   else
-    TREE_NO_WARNING (repl) = 1;
+    suppress_warning (repl /* Be more selective! */);
 
   if (dump_file)
     {
@@ -2254,7 +2282,7 @@ create_access_replacement (struct access *access, tree reg_type = NULL_TREE)
          print_generic_expr (dump_file, access->base);
          fprintf (dump_file, " offset: %u, size: %u: ",
                   (unsigned) access->offset, (unsigned) access->size);
-         print_generic_expr (dump_file, repl);
+         print_generic_expr (dump_file, repl, TDF_UID);
          fprintf (dump_file, "\n");
        }
     }
@@ -2354,8 +2382,11 @@ verify_sra_access_forest (struct access *root)
       gcc_assert (base == first_base);
       gcc_assert (offset == access->offset);
       gcc_assert (access->grp_unscalarizable_region
+                 || access->grp_total_scalarization
                  || size == max_size);
-      gcc_assert (max_size == access->size);
+      gcc_assert (access->grp_unscalarizable_region
+                 || !is_gimple_reg_type (access->type)
+                 || size == access->size);
       gcc_assert (reverse == access->reverse);
 
       if (access->first_child)
@@ -2686,6 +2717,45 @@ subtree_mark_written_and_rhs_enqueue (struct access *access)
     subtree_mark_written_and_rhs_enqueue (child);
 }
 
+/* If there is still budget to create a propagation access for DECL, return
+   true and decrement the budget.  Otherwise return false.  */
+
+static bool
+budget_for_propagation_access (tree decl)
+{
+  unsigned b, *p = propagation_budget->get (decl);
+  if (p)
+    b = *p;
+  else
+    b = param_sra_max_propagations;
+
+  if (b == 0)
+    return false;
+  b--;
+
+  if (b == 0 && dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "The propagation budget of ");
+      print_generic_expr (dump_file, decl);
+      fprintf (dump_file, " (UID: %u) has been exhausted.\n", DECL_UID (decl));
+    }
+  propagation_budget->put (decl, b);
+  return true;
+}
+
+/* Return true if ACC or any of its subaccesses has grp_child set.  */
+
+static bool
+access_or_its_child_written (struct access *acc)
+{
+  if (acc->grp_write)
+    return true;
+  for (struct access *sub = acc->first_child; sub; sub = sub->next_sibling)
+    if (access_or_its_child_written (sub))
+      return true;
+  return false;
+}
+
 /* Propagate subaccesses and grp_write flags of RACC across an assignment link
    to LACC.  Enqueue sub-accesses as necessary so that the write flag is
    propagated transitively.  Return true if anything changed.  Additionally, if
@@ -2732,6 +2802,12 @@ propagate_subaccesses_from_rhs (struct access *lacc, struct access *racc)
        }
       if (!lacc->first_child && !racc->first_child)
        {
+         /* We are about to change the access type from aggregate to scalar,
+            so we need to put the reverse flag onto the access, if any.  */
+         const bool reverse
+           = TYPE_REVERSE_STORAGE_ORDER (lacc->type)
+             && !POINTER_TYPE_P (racc->type)
+             && !VECTOR_TYPE_P (racc->type);
          tree t = lacc->base;
 
          lacc->type = racc->type;
@@ -2746,9 +2822,12 @@ propagate_subaccesses_from_rhs (struct access *lacc, struct access *racc)
              lacc->expr = build_ref_for_model (EXPR_LOCATION (lacc->base),
                                                lacc->base, lacc->offset,
                                                racc, NULL, false);
+             if (TREE_CODE (lacc->expr) == MEM_REF)
+               REF_REVERSE_STORAGE_ORDER (lacc->expr) = reverse;
              lacc->grp_no_warning = true;
              lacc->grp_same_access_path = false;
            }
+         lacc->reverse = reverse;
        }
       return ret;
     }
@@ -2790,9 +2869,10 @@ propagate_subaccesses_from_rhs (struct access *lacc, struct access *racc)
          continue;
        }
 
-      if (rchild->grp_unscalarizable_region)
+      if (rchild->grp_unscalarizable_region
+         || !budget_for_propagation_access (lacc->base))
        {
-         if (rchild->grp_write && !lacc->grp_write)
+         if (!lacc->grp_write && access_or_its_child_written (rchild))
            {
              ret = true;
              subtree_mark_written_and_rhs_enqueue (lacc);
@@ -2850,7 +2930,8 @@ propagate_subaccesses_from_lhs (struct access *lacc, struct access *racc)
 
       if (lchild->grp_unscalarizable_region
          || child_would_conflict_in_acc (racc, norm_offset, lchild->size,
-                                         &matching_acc))
+                                         &matching_acc)
+         || !budget_for_propagation_access (racc->base))
        {
          if (matching_acc
              && propagate_subaccesses_from_lhs (lchild, matching_acc))
@@ -2881,6 +2962,7 @@ propagate_subaccesses_from_lhs (struct access *lacc, struct access *racc)
 static void
 propagate_all_subaccesses (void)
 {
+  propagation_budget = new hash_map<tree, unsigned>;
   while (rhs_work_queue_head)
     {
       struct access *racc = pop_access_from_rhs_work_queue ();
@@ -2944,6 +3026,7 @@ propagate_all_subaccesses (void)
            add_access_to_lhs_work_queue (racc);
        }
     }
+  delete propagation_budget;
 }
 
 /* Return true if the forest beginning with ROOT does not contain
@@ -3496,7 +3579,7 @@ generate_subtree_copies (struct access *access, tree agg,
            }
          else
            {
-             TREE_NO_WARNING (repl) = 1;
+             suppress_warning (repl /* Be more selective! */);
              if (access->grp_partial_lhs)
                repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
                                                 !insert_after,
@@ -3664,6 +3747,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
   location_t loc;
   struct access *access;
   tree type, bfr, orig_expr;
+  bool partial_cplx_access = false;
 
   if (TREE_CODE (*expr) == BIT_FIELD_REF)
     {
@@ -3674,7 +3758,10 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
     bfr = NULL_TREE;
 
   if (TREE_CODE (*expr) == REALPART_EXPR || TREE_CODE (*expr) == IMAGPART_EXPR)
-    expr = &TREE_OPERAND (*expr, 0);
+    {
+      expr = &TREE_OPERAND (*expr, 0);
+      partial_cplx_access = true;
+    }
   access = get_access_for_expr (*expr);
   if (!access)
     return false;
@@ -3702,13 +3789,32 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
          be accessed as a different type too, potentially creating a need for
          type conversion (see PR42196) and when scalarized unions are involved
          in assembler statements (see PR42398).  */
-      if (!useless_type_conversion_p (type, access->type))
+      if (!bfr && !useless_type_conversion_p (type, access->type))
        {
          tree ref;
 
          ref = build_ref_for_model (loc, orig_expr, 0, access, gsi, false);
 
-         if (write)
+         if (partial_cplx_access)
+           {
+           /* VIEW_CONVERT_EXPRs in partial complex access are always fine in
+              the case of a write because in such case the replacement cannot
+              be a gimple register.  In the case of a load, we have to
+              differentiate in between a register an non-register
+              replacement.  */
+             tree t = build1 (VIEW_CONVERT_EXPR, type, repl);
+             gcc_checking_assert (!write || access->grp_partial_lhs);
+             if (!access->grp_partial_lhs)
+               {
+                 tree tmp = make_ssa_name (type);
+                 gassign *stmt = gimple_build_assign (tmp, t);
+                 /* This is always a read. */
+                 gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+                 t = tmp;
+               }
+             *expr = t;
+           }
+         else if (write)
            {
              gassign *stmt;
 
@@ -3743,7 +3849,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
       gsi_insert_after (gsi, ds, GSI_NEW_STMT);
     }
 
-  if (access->first_child)
+  if (access->first_child && !TREE_READONLY (access->base))
     {
       HOST_WIDE_INT start_offset, chunk_size;
       if (bfr
@@ -3807,6 +3913,13 @@ static void
 handle_unscalarized_data_in_subtree (struct subreplacement_assignment_data *sad)
 {
   tree src;
+  /* If the RHS is a load from a constant, we do not need to (and must not)
+     flush replacements to it and can use it directly as if we did.  */
+  if (TREE_READONLY (sad->top_racc->base))
+    {
+      sad->refreshed = SRA_UDH_RIGHT;
+      return;
+    }
   if (sad->top_racc->grp_unscalarized_data)
     {
       src = sad->assignment_rhs;
@@ -4000,6 +4113,88 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
   return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
 }
 
+
+/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
+   of accesses within a subtree ACCESS; all its children, siblings and their
+   children are to be processed.
+   GSI is a statement iterator used to place the new statements.  */
+static void
+generate_subtree_deferred_init (struct access *access,
+                               tree init_type,
+                               tree is_vla,
+                               gimple_stmt_iterator *gsi,
+                               location_t loc)
+{
+  do
+    {
+      if (access->grp_to_be_replaced)
+       {
+         tree repl = get_access_replacement (access);
+         gimple *call
+           = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
+                                         TYPE_SIZE_UNIT (TREE_TYPE (repl)),
+                                         init_type, is_vla);
+         gimple_call_set_lhs (call, repl);
+         gsi_insert_before (gsi, call, GSI_SAME_STMT);
+         update_stmt (call);
+         gimple_set_location (call, loc);
+         sra_stats.subtree_deferred_init++;
+       }
+      if (access->first_child)
+       generate_subtree_deferred_init (access->first_child, init_type,
+                                       is_vla, gsi, loc);
+
+      access = access ->next_sibling;
+    }
+  while (access);
+}
+
+/* For a call to .DEFERRED_INIT:
+   var = .DEFERRED_INIT (size_of_var, init_type, is_vla);
+   examine the LHS variable VAR and replace it with a scalar replacement if
+   there is one, also replace the RHS call to a call to .DEFERRED_INIT of
+   the corresponding scalar relacement variable.  Examine the subtree and
+   do the scalar replacements in the subtree too.  STMT is the call, GSI is
+   the statment iterator to place newly created statement.  */
+
+static enum assignment_mod_result
+sra_modify_deferred_init (gimple *stmt, gimple_stmt_iterator *gsi)
+{
+  tree lhs = gimple_call_lhs (stmt);
+  tree init_type = gimple_call_arg (stmt, 1);
+  tree is_vla = gimple_call_arg (stmt, 2);
+
+  struct access *lhs_access = get_access_for_expr (lhs);
+  if (!lhs_access)
+    return SRA_AM_NONE;
+
+  location_t loc = gimple_location (stmt);
+
+  if (lhs_access->grp_to_be_replaced)
+    {
+      tree lhs_repl = get_access_replacement (lhs_access);
+      gimple_call_set_lhs (stmt, lhs_repl);
+      tree arg0_repl = TYPE_SIZE_UNIT (TREE_TYPE (lhs_repl));
+      gimple_call_set_arg (stmt, 0, arg0_repl);
+      sra_stats.deferred_init++;
+      gcc_assert (!lhs_access->first_child);
+      return SRA_AM_MODIFIED;
+    }
+
+  if (lhs_access->first_child)
+    generate_subtree_deferred_init (lhs_access->first_child,
+                                   init_type, is_vla, gsi, loc);
+  if (lhs_access->grp_covered)
+    {
+      unlink_stmt_vdef (stmt);
+      gsi_remove (gsi, true);
+      release_defs (stmt);
+      return SRA_AM_REMOVED;
+    }
+
+  return SRA_AM_MODIFIED;
+}
+
 /* Examine both sides of the assignment statement pointed to by STMT, replace
    them with a scalare replacement if there is one and generate copying of
    replacements if scalarized aggregates have been used in the assignment.  GSI
@@ -4160,8 +4355,8 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
       || contains_vce_or_bfcref_p (lhs)
       || stmt_ends_bb_p (stmt))
     {
-      /* No need to copy into a constant-pool, it comes pre-initialized.  */
-      if (access_has_children_p (racc) && !constant_decl_p (racc->base))
+      /* No need to copy into a constant, it comes pre-initialized.  */
+      if (access_has_children_p (racc) && !TREE_READONLY (racc->base))
        generate_subtree_copies (racc->first_child, rhs, racc->offset, 0, 0,
                                 gsi, false, false, loc);
       if (access_has_children_p (lacc))
@@ -4250,7 +4445,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
            }
          /* Restore the aggregate RHS from its components so the
             prevailing aggregate copy does the right thing.  */
-         if (access_has_children_p (racc))
+         if (access_has_children_p (racc) && !TREE_READONLY (racc->base))
            generate_subtree_copies (racc->first_child, rhs, racc->offset, 0, 0,
                                     gsi, false, false, loc);
          /* Re-load the components of the aggregate copy destination.
@@ -4364,17 +4559,27 @@ sra_modify_function_body (void)
              break;
 
            case GIMPLE_CALL:
-             /* Operands must be processed before the lhs.  */
-             for (i = 0; i < gimple_call_num_args (stmt); i++)
+             /* Handle calls to .DEFERRED_INIT specially.  */
+             if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
                {
-                 t = gimple_call_arg_ptr (stmt, i);
-                 modified |= sra_modify_expr (t, &gsi, false);
+                 assign_result = sra_modify_deferred_init (stmt, &gsi);
+                 modified |= assign_result == SRA_AM_MODIFIED;
+                 deleted = assign_result == SRA_AM_REMOVED;
                }
-
-             if (gimple_call_lhs (stmt))
+             else
                {
-                 t = gimple_call_lhs_ptr (stmt);
-                 modified |= sra_modify_expr (t, &gsi, true);
+                 /* Operands must be processed before the lhs.  */
+                 for (i = 0; i < gimple_call_num_args (stmt); i++)
+                   {
+                     t = gimple_call_arg_ptr (stmt, i);
+                     modified |= sra_modify_expr (t, &gsi, false);
+                   }
+
+                 if (gimple_call_lhs (stmt))
+                   {
+                     t = gimple_call_lhs_ptr (stmt);
+                     modified |= sra_modify_expr (t, &gsi, true);
+                   }
                }
              break;