]> gcc.gnu.org Git - gcc.git/commitdiff
Emit loop initializer for repeat arrays
authorDavid Faust <david.faust@oracle.com>
Thu, 7 Apr 2022 16:41:37 +0000 (09:41 -0700)
committerDavid Faust <david.faust@oracle.com>
Thu, 7 Apr 2022 16:57:46 +0000 (09:57 -0700)
This commit changes how arrays of repeating elements, e.g. [5; 12] are
compiled. Rather than create a constructor which explicitly initializes
each element to the given value (which causes compiler OOM for large
arrays), we emit instructions to allocate the array then initialize the
elements in a loop.

However, we can only take this approach outside of const contexts -
const arrays must still use the old approach.

gcc/rust/backend/rust-compile-context.h
gcc/rust/backend/rust-compile-expr.cc
gcc/rust/backend/rust-compile-item.cc
gcc/rust/rust-backend.h
gcc/rust/rust-gcc.cc
gcc/testsuite/rust/compile/torture/arrays5.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/torture/arrays6.rs [new file with mode: 0644]

index 4bade5c7d603da9099f01b43138367318b159c50..de9d03f6d9d458d6641e8b7f36a723be92e453a9 100644 (file)
@@ -250,6 +250,10 @@ public:
     fn_stack.push_back (fncontext{fn, ret_addr});
   }
   void pop_fn () { fn_stack.pop_back (); }
+
+  bool in_fn () { return fn_stack.size () != 0; }
+
+  // Note: it is undefined behavior to call peek_fn () if fn_stack is empty.
   fncontext peek_fn () { return fn_stack.back (); }
 
   void push_type (tree t) { type_decls.push_back (t); }
@@ -301,6 +305,14 @@ public:
     return pop;
   }
 
+  void push_const_context (void) { const_context++; }
+  void pop_const_context (void)
+  {
+    if (const_context > 0)
+      const_context--;
+  }
+  bool const_context_p (void) { return (const_context > 0); }
+
   std::string mangle_item (const TyTy::BaseType *ty,
                           const Resolver::CanonicalPath &path) const
   {
@@ -341,6 +353,9 @@ private:
   std::vector<::Bvariable *> var_decls;
   std::vector<tree> const_decls;
   std::vector<tree> func_decls;
+
+  // Nonzero iff we are currently compiling something inside a constant context.
+  unsigned int const_context = 0;
 };
 
 } // namespace Compile
index 74ab6a468a82f2e85951ddec3863c6f92c9a87b4..49a6f2f75bf027e7c81f8d30491e7878e661167d 100644 (file)
@@ -1163,19 +1163,54 @@ CompileExpr::array_copied_expr (Location expr_locus,
   unsigned HOST_WIDE_INT len
     = wi::ext (max - min + 1, precision, sign).to_uhwi ();
 
-  // create the constructor
-  size_t idx = 0;
-  std::vector<unsigned long> indexes;
-  std::vector<tree> constructor;
-  for (unsigned HOST_WIDE_INT i = 0; i < len; i++)
-    {
-      constructor.push_back (translated_expr);
-      indexes.push_back (idx++);
+  // In a const context we must initialize the entire array, which entails
+  // allocating for each element. If the user wants a huge array, we will OOM
+  // and die horribly.
+  if (ctx->const_context_p ())
+    {
+      size_t idx = 0;
+      std::vector<unsigned long> indexes;
+      std::vector<tree> constructor;
+      for (unsigned HOST_WIDE_INT i = 0; i < len; i++)
+       {
+         constructor.push_back (translated_expr);
+         indexes.push_back (idx++);
+       }
+
+      return ctx->get_backend ()->array_constructor_expression (array_type,
+                                                               indexes,
+                                                               constructor,
+                                                               expr_locus);
     }
 
-  return ctx->get_backend ()->array_constructor_expression (array_type, indexes,
-                                                           constructor,
-                                                           expr_locus);
+  else
+    {
+      // Create a new block scope in which to initialize the array
+      tree fndecl = NULL_TREE;
+      if (ctx->in_fn ())
+       fndecl = ctx->peek_fn ().fndecl;
+
+      std::vector<Bvariable *> locals;
+      tree enclosing_scope = ctx->peek_enclosing_scope ();
+      tree init_block
+       = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
+                                     expr_locus, expr_locus);
+      ctx->push_block (init_block);
+
+      tree tmp;
+      tree stmts
+       = ctx->get_backend ()->array_initializer (fndecl, init_block,
+                                                 array_type, capacity_expr,
+                                                 translated_expr, &tmp,
+                                                 expr_locus);
+      ctx->add_statement (stmts);
+
+      tree block = ctx->pop_block ();
+
+      // The result is a compound expression which creates a temporary array,
+      // initializes all the elements in a loop, and then yeilds the array.
+      return ctx->get_backend ()->compound_expression (block, tmp, expr_locus);
+    }
 }
 
 tree
index 21cbb1ce905595d51e615bfe54569eb467a3819c..80b7cebfb5fbd662978095e43c9fa0a8eddb4c7a 100644 (file)
@@ -92,9 +92,11 @@ CompileItem::visit (HIR::ConstantItem &constant)
   rust_assert (ok);
 
   HIR::Expr *const_value_expr = constant.get_expr ();
+  ctx->push_const_context ();
   tree const_expr
     = compile_constant_item (ctx, resolved_type, canonical_path,
                             const_value_expr, constant.get_locus ());
+  ctx->pop_const_context ();
 
   ctx->push_const (const_expr);
   ctx->insert_const_decl (constant.get_mappings ().get_hirid (), const_expr);
index 6bfebe3d5c2fe413b9598a82f01dbe5943c83eac..17b7bae6e990077e009c78053592e681edecfe34 100644 (file)
@@ -275,6 +275,10 @@ public:
                                const std::vector<tree> &vals, Location)
     = 0;
 
+  virtual tree array_initializer (tree, tree, tree, tree, tree, tree *,
+                                 Location)
+    = 0;
+
   // Return an expression for ARRAY[INDEX] as an l-value.  ARRAY is a valid
   // fixed-length array, not a slice.
   virtual tree array_index_expression (tree array, tree index, Location) = 0;
index 6ea9ef3e44e03088abd33a58c9fcb6183b9c605d..334e9e504894233fab5b8bf6bcc55c4f338bd701 100644 (file)
@@ -246,6 +246,8 @@ public:
   tree array_constructor_expression (tree, const std::vector<unsigned long> &,
                                     const std::vector<tree> &, Location);
 
+  tree array_initializer (tree, tree, tree, tree, tree, tree *, Location);
+
   tree array_index_expression (tree array, tree index, Location);
 
   tree call_expression (tree caller, tree fn, const std::vector<tree> &args,
@@ -1694,6 +1696,77 @@ Gcc_backend::array_constructor_expression (
   return ret;
 }
 
+// Build insns to create an array, initialize all elements of the array to
+// value, and return it
+tree
+Gcc_backend::array_initializer (tree fndecl, tree block, tree array_type,
+                               tree length, tree value, tree *tmp,
+                               Location locus)
+{
+  std::vector<tree> stmts;
+
+  // Temporary array we initialize with the desired value.
+  tree t = NULL_TREE;
+  Bvariable *tmp_array = this->temporary_variable (fndecl, block, array_type,
+                                                  NULL_TREE, true, locus, &t);
+  tree arr = tmp_array->get_tree (locus);
+  stmts.push_back (t);
+
+  // Temporary for the array length used for initialization loop guard.
+  Bvariable *tmp_len = this->temporary_variable (fndecl, block, size_type_node,
+                                                length, true, locus, &t);
+  tree len = tmp_len->get_tree (locus);
+  stmts.push_back (t);
+
+  // Temporary variable for pointer used to initialize elements.
+  tree ptr_type = this->pointer_type (TREE_TYPE (array_type));
+  tree ptr_init
+    = build1_loc (locus.gcc_location (), ADDR_EXPR, ptr_type,
+                 this->array_index_expression (arr, integer_zero_node, locus));
+  Bvariable *tmp_ptr = this->temporary_variable (fndecl, block, ptr_type,
+                                                ptr_init, false, locus, &t);
+  tree ptr = tmp_ptr->get_tree (locus);
+  stmts.push_back (t);
+
+  // push statement list for the loop
+  std::vector<tree> loop_stmts;
+
+  // Loop exit condition:
+  //   if (length == 0) break;
+  t = this->comparison_expression (ComparisonOperator::EQUAL, len,
+                                  this->zero_expression (TREE_TYPE (len)),
+                                  locus);
+
+  t = this->exit_expression (t, locus);
+  loop_stmts.push_back (t);
+
+  // Assign value to the current pointer position
+  //   *ptr = value;
+  t = this->assignment_statement (build_fold_indirect_ref (ptr), value, locus);
+  loop_stmts.push_back (t);
+
+  // Move pointer to next element
+  //   ptr++;
+  tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptr_type));
+  t = build2 (POSTINCREMENT_EXPR, ptr_type, ptr, convert (ptr_type, size));
+  loop_stmts.push_back (t);
+
+  // Decrement loop counter.
+  //   length--;
+  t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (len), len,
+             convert (TREE_TYPE (len), integer_one_node));
+  loop_stmts.push_back (t);
+
+  // pop statments and finish loop
+  tree loop_body = this->statement_list (loop_stmts);
+  stmts.push_back (this->loop_expression (loop_body, locus));
+
+  // Return the temporary in the provided pointer and the statement list which
+  // initializes it.
+  *tmp = tmp_array->get_tree (locus);
+  return this->statement_list (stmts);
+}
+
 // Return an expression representing ARRAY[INDEX]
 
 tree
diff --git a/gcc/testsuite/rust/compile/torture/arrays5.rs b/gcc/testsuite/rust/compile/torture/arrays5.rs
new file mode 100644 (file)
index 0000000..58950a1
--- /dev/null
@@ -0,0 +1,6 @@
+
+// Checks that we don't try to allocate a 4TB array during compilation
+fn main () {
+    let x = [0; 4 * 1024 * 1024 * 1024 * 1024];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays6.rs b/gcc/testsuite/rust/compile/torture/arrays6.rs
new file mode 100644 (file)
index 0000000..c7212d3
--- /dev/null
@@ -0,0 +1,10 @@
+
+// Checks that we don't try to allocate a 4TB array during compilation
+fn foo() -> [u8; 4 * 1024 * 1024 * 1024 * 1024] {
+    [0; 4 * 1024 * 1024 * 1024 * 1024]
+}
+
+fn main () {
+    let x = foo ();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
This page took 0.071454 seconds and 5 git commands to generate.