public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Emit loop initializer for repeat arrays
@ 2022-06-08 12:26 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-06-08 12:26 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:5559bdc86635b2ff6cbaa60e933a7989ff212e1f

commit 5559bdc86635b2ff6cbaa60e933a7989ff212e1f
Author: David Faust <david.faust@oracle.com>
Date:   Thu Apr 7 09:41:37 2022 -0700

    Emit loop initializer for repeat arrays
    
    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.

Diff:
---
 gcc/rust/backend/rust-compile-context.h       | 15 ++++++
 gcc/rust/backend/rust-compile-expr.cc         | 57 +++++++++++++++++----
 gcc/rust/backend/rust-compile-item.cc         |  2 +
 gcc/rust/rust-backend.h                       |  4 ++
 gcc/rust/rust-gcc.cc                          | 73 +++++++++++++++++++++++++++
 gcc/testsuite/rust/compile/torture/arrays5.rs |  6 +++
 gcc/testsuite/rust/compile/torture/arrays6.rs | 10 ++++
 7 files changed, 156 insertions(+), 11 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 4bade5c7d60..de9d03f6d9d 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -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
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 74ab6a468a8..49a6f2f75bf 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -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
diff --git a/gcc/rust/backend/rust-compile-item.cc b/gcc/rust/backend/rust-compile-item.cc
index 21cbb1ce905..80b7cebfb5f 100644
--- a/gcc/rust/backend/rust-compile-item.cc
+++ b/gcc/rust/backend/rust-compile-item.cc
@@ -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);
diff --git a/gcc/rust/rust-backend.h b/gcc/rust/rust-backend.h
index 6bfebe3d5c2..17b7bae6e99 100644
--- a/gcc/rust/rust-backend.h
+++ b/gcc/rust/rust-backend.h
@@ -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;
diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc
index 6ea9ef3e44e..334e9e50489 100644
--- a/gcc/rust/rust-gcc.cc
+++ b/gcc/rust/rust-gcc.cc
@@ -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
index 00000000000..58950a17a15
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays5.rs
@@ -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
index 00000000000..c7212d3f183
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays6.rs
@@ -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 }
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-06-08 12:26 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-08 12:26 [gcc/devel/rust/master] Emit loop initializer for repeat arrays Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).