From 58dbd45339823deb30fe4f1e97f6664f118b2f62 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 1 May 2019 21:37:00 +0000 Subject: [PATCH] compiler: recognize and optimize map range clear 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 | 2 +- gcc/go/gofrontend/runtime.def | 3 ++ gcc/go/gofrontend/statements.cc | 96 +++++++++++++++++++++++++++++++++ gcc/go/gofrontend/statements.h | 4 ++ libgo/go/runtime/map.go | 1 + 5 files changed, 105 insertions(+), 1 deletion(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index f5aa6a9d384..0203eee5d27 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 83a71528a8c..a87b4d270d4 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -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)) diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 4d10f60e6c2..6dd179a7088 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -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* 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* diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index 621d301fb82..67c8e4343b7 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -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 diff --git a/libgo/go/runtime/map.go b/libgo/go/runtime/map.go index 5dd5283e1ec..b210f5a5320 100644 --- a/libgo/go/runtime/map.go +++ b/libgo/go/runtime/map.go @@ -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 -- 2.30.2