compiler: recognize and optimize map range clear
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 1 May 2019 21:37:00 +0000 (21:37 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 1 May 2019 21:37:00 +0000 (21:37 +0000)
    Recognize

            for k := range m { delete(m, k) }

    for map m, and rewrite it to runtime.mapclear, as the gc compiler
    does.

    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/169397

From-SVN: r270780

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/runtime.def
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h
libgo/go/runtime/map.go

index f5aa6a9d384369f9b3a4febbde78e8ac14dba82c..0203eee5d27cf14294140e19bab3578be4620894 100644 (file)
@@ -1,4 +1,4 @@
-b42744825e3f2d1d2981eedbb67d6ac6419b8122
+7e590184ae1ebc02e1b2577de00cf4fe842217dc
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 83a71528a8c4d5ead3bfb1f37369bab0b4bc3abd..a87b4d270d4e0ef6e7bc94cc133e94e96ffe81e2 100644 (file)
@@ -137,6 +137,9 @@ DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P3(TYPE, MAP, POINTER),
 // Range over a map, moving to the next map entry.
 DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(POINTER), R0())
 
+// Clear a map.
+DEF_GO_RUNTIME(MAPCLEAR, "runtime.mapclear", P2(TYPE, MAP), R0())
+
 
 // Make a channel.
 DEF_GO_RUNTIME(MAKECHAN, "runtime.makechan", P2(TYPE, INT), R1(CHAN))
index 4d10f60e6c201f878ef00b5ca1f21f30ccb3d0ed..6dd179a708896986b5aa30036f3f90edd3a99ee9 100644 (file)
@@ -5485,6 +5485,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
   Location loc = this->location();
   Block* temp_block = new Block(enclosing, loc);
 
+  Expression* orig_range_expr = this->range_;
   Named_object* range_object = NULL;
   Temporary_statement* range_temp = NULL;
   if (eval)
@@ -5500,6 +5501,22 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
        }
     }
 
+  // Try to match "range clear" patterns and rewrite to simple runtime
+  // calls.
+  if (range_type->map_type() != NULL)
+    {
+      Statement* clear = this->lower_map_range_clear(range_type,
+                                                     enclosing,
+                                                     orig_range_expr,
+                                                     range_object,
+                                                     range_temp, loc);
+      if (clear != NULL)
+        {
+          temp_block->add_statement(clear);
+          return Statement::make_block_statement(temp_block, loc);
+        }
+    }
+
   Temporary_statement* index_temp = Statement::make_temporary(index_type,
                                                              NULL, loc);
   temp_block->add_statement(index_temp);
@@ -6141,6 +6158,85 @@ For_range_statement::lower_range_channel(Gogo*,
   *piter_init = iter_init;
 }
 
+// Match
+//
+//   for k := range m { delete(m, k) }
+//
+// Lower it to runtime.mapclear(TYPE, m) on match, return the statement
+// containing the call.  Return NULL otherwise.
+
+Statement*
+For_range_statement::lower_map_range_clear(Type* map_type,
+                                           Block* enclosing,
+                                           Expression* orig_range_expr,
+                                           Named_object* range_object,
+                                           Temporary_statement* range_temp,
+                                           Location loc)
+{
+  if (this->value_var_ != NULL)
+    return NULL;
+  if (this->index_var_ == NULL)
+    return NULL;
+
+  // Require the loop index be a new variable.  We cannot rewrite
+  // if it is used outside of the loop.
+  Var_expression* index_ve = this->index_var_->var_expression();
+  if (index_ve == NULL)
+    return NULL;
+  Named_object* index_no = index_ve->named_object();
+  if (enclosing->bindings()->lookup_local(index_no->name()) != index_no)
+    return NULL;
+
+  // Match the body.  When lowering the builtin delete function, we have
+  // inserted temporaries, so we actually match for
+  //
+  //   tmp1 = m
+  //   tmp2 = k
+  //   runtime.mapdelete(TYPE, tmp1, &tmp2)
+
+  const std::vector<Statement*>* statements = this->statements_->statements();
+  if (statements->size() != 3)
+    return NULL;
+
+  Temporary_statement* ts1 = statements->at(0)->temporary_statement();
+  Temporary_statement* ts2 = statements->at(1)->temporary_statement();
+  Expression_statement* es3 = statements->at(2)->expression_statement();
+  if (ts1 == NULL || ts2 == NULL || es3 == NULL
+      || !Expression::is_same_variable(orig_range_expr, ts1->init())
+      || !Expression::is_same_variable(this->index_var_, ts2->init()))
+    return NULL;
+  Call_expression* call = es3->expr()->call_expression();
+  if (call == NULL)
+    return NULL;
+  Func_expression* fe = call->fn()->func_expression();
+  if (fe == NULL || !fe->is_runtime_function()
+      || fe->runtime_code() != Runtime::MAPDELETE)
+    return NULL;
+  Expression* a1 = call->args()->at(1);
+  a1 = (a1->unsafe_conversion_expression() != NULL
+        ? a1->unsafe_conversion_expression()->expr()
+        : a1);
+  Temporary_reference_expression* tre = a1->temporary_reference_expression();
+  if (tre == NULL || tre->statement() != ts1)
+    return NULL;
+  Expression* a2 = call->args()->at(2);
+  a2 = (a2->conversion_expression() != NULL
+        ? a2->conversion_expression()->expr()
+        : a2);
+  Unary_expression* ue = a2->unary_expression();
+  if (ue == NULL || ue->op() != OPERATOR_AND)
+    return NULL;
+  tre = ue->operand()->temporary_reference_expression();
+  if (tre == NULL || tre->statement() != ts2)
+    return NULL;
+
+  // Everything matches. Rewrite to mapclear(TYPE, MAP).
+  Expression* e1 = Expression::make_type_descriptor(map_type, loc);
+  Expression* e2 = this->make_range_ref(range_object, range_temp, loc);
+  call = Runtime::make_call(Runtime::MAPCLEAR, loc, 2, e1, e2);
+  return Statement::make_statement(call, true);
+}
+
 // Return the break LABEL_EXPR.
 
 Unnamed_label*
index 621d301fb8229b96cdb81074f122f09503f4fffb..67c8e4343b799c485920525cec523816ad45d820 100644 (file)
@@ -1618,6 +1618,10 @@ class For_range_statement : public Statement
                      Temporary_statement*, Block**, Expression**, Block**,
                      Block**);
 
+  Statement*
+  lower_map_range_clear(Type*, Block*, Expression*, Named_object*,
+                        Temporary_statement*, Location);
+
   // The variable which is set to the index value.
   Expression* index_var_;
   // The variable which is set to the element value.  This may be
index 5dd5283e1eca346c5a677676cec85f2f8aa56df7..b210f5a5320952f8e75293c6b82150f49b396dbd 100644 (file)
@@ -72,6 +72,7 @@ import (
 //go:linkname mapaccess2_fat runtime.mapaccess2_fat
 //go:linkname mapassign runtime.mapassign
 //go:linkname mapdelete runtime.mapdelete
+//go:linkname mapclear runtime.mapclear
 //go:linkname mapiterinit runtime.mapiterinit
 //go:linkname mapiternext runtime.mapiternext