Commit 08c8a26e by Ian Lance Taylor

compiler: recognize and optimize array range clear

    
    Recognize
    
            for i := range a { a[i] = zero }
    
    for array or slice a, and rewrite it to call memclr, as the gc
    compiler does.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/169398

From-SVN: r270862
parent e339291f
208521930c9b5adcfb495799ee01b6aec86c2ccf 4b3015de639cf22ed11ff96097555700909827c8
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -1672,6 +1672,10 @@ class Boolean_expression : public Expression ...@@ -1672,6 +1672,10 @@ class Boolean_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{ return this->val_ == false; }
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -2055,6 +2059,10 @@ class Integer_expression : public Expression ...@@ -2055,6 +2059,10 @@ class Integer_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{ return mpz_sgn(this->val_) == 0; }
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -2475,6 +2483,13 @@ class Float_expression : public Expression ...@@ -2475,6 +2483,13 @@ class Float_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{
return mpfr_zero_p(this->val_) != 0
&& mpfr_signbit(this->val_) == 0;
}
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -2686,6 +2701,15 @@ class Complex_expression : public Expression ...@@ -2686,6 +2701,15 @@ class Complex_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{
return mpfr_zero_p(mpc_realref(this->val_)) != 0
&& mpfr_signbit(mpc_realref(this->val_)) == 0
&& mpfr_zero_p(mpc_imagref(this->val_)) != 0
&& mpfr_signbit(mpc_imagref(this->val_)) == 0;
}
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -2923,6 +2947,10 @@ class Const_expression : public Expression ...@@ -2923,6 +2947,10 @@ class Const_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{ return this->constant_->const_value()->expr()->is_zero_value(); }
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -3290,6 +3318,10 @@ class Nil_expression : public Expression ...@@ -3290,6 +3318,10 @@ class Nil_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{ return true; }
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -3533,6 +3565,28 @@ Type_conversion_expression::do_is_constant() const ...@@ -3533,6 +3565,28 @@ Type_conversion_expression::do_is_constant() const
return true; return true;
} }
// Return whether a type conversion is a zero value.
bool
Type_conversion_expression::do_is_zero_value() const
{
if (!this->expr_->is_zero_value())
return false;
// Some type conversion from zero value is still not zero value.
// For example, []byte("") or interface{}(0).
// Conservatively, only report true if the RHS is nil.
Type* type = this->type_;
if (type->integer_type() == NULL
&& type->float_type() == NULL
&& type->complex_type() == NULL
&& !type->is_boolean_type()
&& !type->is_string_type())
return this->expr_->is_nil_expression();
return true;
}
// Return whether a type conversion can be used in a constant // Return whether a type conversion can be used in a constant
// initializer. // initializer.
...@@ -6880,6 +6934,19 @@ String_concat_expression::do_is_constant() const ...@@ -6880,6 +6934,19 @@ String_concat_expression::do_is_constant() const
} }
bool bool
String_concat_expression::do_is_zero_value() const
{
for (Expression_list::const_iterator pe = this->exprs_->begin();
pe != this->exprs_->end();
++pe)
{
if (!(*pe)->is_zero_value())
return false;
}
return true;
}
bool
String_concat_expression::do_is_static_initializer() const String_concat_expression::do_is_static_initializer() const
{ {
for (Expression_list::const_iterator pe = this->exprs_->begin(); for (Expression_list::const_iterator pe = this->exprs_->begin();
...@@ -13007,6 +13074,33 @@ Struct_construction_expression::is_constant_struct() const ...@@ -13007,6 +13074,33 @@ Struct_construction_expression::is_constant_struct() const
return true; return true;
} }
// Return whether this is a zero value.
bool
Struct_construction_expression::do_is_zero_value() const
{
if (this->vals() == NULL)
return true;
for (Expression_list::const_iterator pv = this->vals()->begin();
pv != this->vals()->end();
++pv)
if (*pv != NULL && !(*pv)->is_zero_value())
return false;
const Struct_field_list* fields = this->type_->struct_type()->fields();
for (Struct_field_list::const_iterator pf = fields->begin();
pf != fields->end();
++pf)
{
// Interface conversion may cause a zero value being converted
// to a non-zero value, like interface{}(0). Be conservative.
if (pf->type()->interface_type() != NULL)
return false;
}
return true;
}
// Return whether this struct can be used as a constant initializer. // Return whether this struct can be used as a constant initializer.
bool bool
...@@ -13288,6 +13382,28 @@ Array_construction_expression::is_constant_array() const ...@@ -13288,6 +13382,28 @@ Array_construction_expression::is_constant_array() const
return true; return true;
} }
// Return whether this is a zero value.
bool
Array_construction_expression::do_is_zero_value() const
{
if (this->vals() == NULL)
return true;
// Interface conversion may cause a zero value being converted
// to a non-zero value, like interface{}(0). Be conservative.
if (this->type_->array_type()->element_type()->interface_type() != NULL)
return false;
for (Expression_list::const_iterator pv = this->vals()->begin();
pv != this->vals()->end();
++pv)
if (*pv != NULL && !(*pv)->is_zero_value())
return false;
return true;
}
// Return whether this can be used a constant initializer. // Return whether this can be used a constant initializer.
bool bool
......
...@@ -544,6 +544,11 @@ class Expression ...@@ -544,6 +544,11 @@ class Expression
is_constant() const is_constant() const
{ return this->do_is_constant(); } { return this->do_is_constant(); }
// Return whether this is the zero value of its type.
bool
is_zero_value() const
{ return this->do_is_zero_value(); }
// Return whether this expression can be used as a static // Return whether this expression can be used as a static
// initializer. This is true for an expression that has only // initializer. This is true for an expression that has only
// numbers and pointers to global variables or composite literals // numbers and pointers to global variables or composite literals
...@@ -1066,6 +1071,11 @@ class Expression ...@@ -1066,6 +1071,11 @@ class Expression
do_is_constant() const do_is_constant() const
{ return false; } { return false; }
// Return whether this is the zero value of its type.
virtual bool
do_is_zero_value() const
{ return false; }
// Return whether this expression can be used as a constant // Return whether this expression can be used as a constant
// initializer. // initializer.
virtual bool virtual bool
...@@ -1600,6 +1610,10 @@ class String_expression : public Expression ...@@ -1600,6 +1610,10 @@ class String_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_zero_value() const
{ return this->val_ == ""; }
bool
do_is_static_initializer() const do_is_static_initializer() const
{ return true; } { return true; }
...@@ -1693,6 +1707,9 @@ class Type_conversion_expression : public Expression ...@@ -1693,6 +1707,9 @@ class Type_conversion_expression : public Expression
do_is_constant() const; do_is_constant() const;
bool bool
do_is_zero_value() const;
bool
do_is_static_initializer() const; do_is_static_initializer() const;
bool bool
...@@ -1756,6 +1773,10 @@ class Unsafe_type_conversion_expression : public Expression ...@@ -1756,6 +1773,10 @@ class Unsafe_type_conversion_expression : public Expression
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool bool
do_is_zero_value() const
{ return this->expr_->is_zero_value(); }
bool
do_is_static_initializer() const; do_is_static_initializer() const;
Type* Type*
...@@ -2152,6 +2173,9 @@ class String_concat_expression : public Expression ...@@ -2152,6 +2173,9 @@ class String_concat_expression : public Expression
do_is_constant() const; do_is_constant() const;
bool bool
do_is_zero_value() const;
bool
do_is_static_initializer() const; do_is_static_initializer() const;
Type* Type*
...@@ -3570,7 +3594,7 @@ class Struct_construction_expression : public Expression, ...@@ -3570,7 +3594,7 @@ class Struct_construction_expression : public Expression,
type_(type) type_(type)
{ } { }
// Return whether this is a constant initializer. // Return whether this is a constant initializer.
bool bool
is_constant_struct() const; is_constant_struct() const;
...@@ -3579,6 +3603,9 @@ class Struct_construction_expression : public Expression, ...@@ -3579,6 +3603,9 @@ class Struct_construction_expression : public Expression,
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool bool
do_is_zero_value() const;
bool
do_is_static_initializer() const; do_is_static_initializer() const;
Type* Type*
...@@ -3643,6 +3670,9 @@ protected: ...@@ -3643,6 +3670,9 @@ protected:
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool bool
do_is_zero_value() const;
bool
do_is_static_initializer() const; do_is_static_initializer() const;
Type* Type*
......
...@@ -313,6 +313,14 @@ DEF_GO_RUNTIME(GCWRITEBARRIER, "runtime.gcWriteBarrier", ...@@ -313,6 +313,14 @@ DEF_GO_RUNTIME(GCWRITEBARRIER, "runtime.gcWriteBarrier",
DEF_GO_RUNTIME(TYPEDMEMMOVE, "runtime.typedmemmove", DEF_GO_RUNTIME(TYPEDMEMMOVE, "runtime.typedmemmove",
P3(TYPE, POINTER, POINTER), R0()) P3(TYPE, POINTER, POINTER), R0())
// Clear memory that contains no pointer.
DEF_GO_RUNTIME(MEMCLRNOPTR, "runtime.memclrNoHeapPointers",
P2(POINTER, UINTPTR), R0())
// Clear memory that contains pointer.
DEF_GO_RUNTIME(MEMCLRHASPTR, "runtime.memclrHasPointers",
P2(POINTER, UINTPTR), R0())
// Lock the printer (for print/println). // Lock the printer (for print/println).
DEF_GO_RUNTIME(PRINTLOCK, "runtime.printlock", P0(), R0()) DEF_GO_RUNTIME(PRINTLOCK, "runtime.printlock", P0(), R0())
......
...@@ -5516,6 +5516,21 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, ...@@ -5516,6 +5516,21 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
return Statement::make_block_statement(temp_block, loc); return Statement::make_block_statement(temp_block, loc);
} }
} }
else if (range_type->array_type() != NULL)
{
// Slice or array.
Statement* clear = this->lower_array_range_clear(gogo,
range_type,
orig_range_expr,
temp_block,
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, Temporary_statement* index_temp = Statement::make_temporary(index_type,
NULL, loc); NULL, loc);
...@@ -6237,6 +6252,109 @@ For_range_statement::lower_map_range_clear(Type* map_type, ...@@ -6237,6 +6252,109 @@ For_range_statement::lower_map_range_clear(Type* map_type,
return Statement::make_statement(call, true); return Statement::make_statement(call, true);
} }
// Match
//
// for i := range a { a[i] = zero }
//
// Lower it to call memclr on match, and return the statement. Return
// NULL otherwise.
Statement*
For_range_statement::lower_array_range_clear(Gogo* gogo,
Type* array_type,
Expression* orig_range_expr,
Block* temp_block,
Named_object* range_object,
Temporary_statement* range_temp,
Location loc)
{
if (this->value_var_ != NULL)
return NULL;
if (this->index_var_ == NULL)
return NULL;
// Match the body, a single assignment statement a[i] = zero.
const std::vector<Statement*>* statements = this->statements_->statements();
if (statements->size() != 1)
return NULL;
Assignment_statement* as = statements->at(0)->assignment_statement();
if (as == NULL || !as->rhs()->is_zero_value())
return NULL;
if (as->lhs()->type()->interface_type() != NULL
&& as->rhs()->type()->interface_type() == NULL
&& !as->rhs()->type()->is_nil_type())
// Implicit type conversion may change a zero value to non-zero, like
// interface{}(0).
return NULL;
Array_index_expression* aie = as->lhs()->array_index_expression();
if (aie == NULL || aie->end() != NULL
|| !Expression::is_same_variable(orig_range_expr, aie->array())
|| !Expression::is_same_variable(this->index_var_, aie->start()))
return NULL;
// Everything matches. Rewrite to
//
// if len(a) != 0 {
// tmp1 = &a[0]
// tmp2 = len(a)*sizeof(elem(a))
// memclr{NoHeap,Has}Pointers(tmp1, tmp2)
// i = len(a) - 1
// }
Type* elem_type = array_type->array_type()->element_type();
int64_t elme_sz;
bool ok = elem_type->backend_type_size(gogo, &elme_sz);
if (!ok)
return NULL;
Block* b = new Block(temp_block, loc);
Expression* ref;
if (range_object == NULL && range_temp == NULL)
// is_same_variable implies no side effect, so it is ok to copy.
ref = orig_range_expr->copy();
else
ref = this->make_range_ref(range_object, range_temp, loc);
Expression* len = this->call_builtin(gogo, "len", ref, loc);
Temporary_statement* tslen = Statement::make_temporary(NULL, len, loc);
temp_block->add_statement(tslen);
Expression* zero = Expression::make_integer_ul(0, this->index_var_->type(), loc);
ref = ref->copy();
Expression* elem = Expression::make_array_index(ref, zero, NULL, NULL, loc);
elem->array_index_expression()->set_needs_bounds_check(false);
Expression* e1 = Expression::make_unary(OPERATOR_AND, elem, loc);
Temporary_statement* ts1 = Statement::make_temporary(NULL, e1, loc);
b->add_statement(ts1);
len = Expression::make_temporary_reference(tslen, loc);
Expression* sz = Expression::make_integer_int64(elme_sz, len->type(), loc);
Expression* e2 = Expression::make_binary(OPERATOR_MULT, len, sz, loc);
Temporary_statement* ts2 = Statement::make_temporary(NULL, e2, loc);
b->add_statement(ts2);
Expression* arg1 = Expression::make_temporary_reference(ts1, loc);
Expression* arg2 = Expression::make_temporary_reference(ts2, loc);
Runtime::Function code = (elem_type->has_pointer()
? Runtime::MEMCLRHASPTR
: Runtime::MEMCLRNOPTR);
Expression* call = Runtime::make_call(code, loc, 2, arg1, arg2);
Statement* cs3 = Statement::make_statement(call, true);
b->add_statement(cs3);
len = Expression::make_temporary_reference(tslen, loc);
Expression* one = Expression::make_integer_ul(1, len->type(), loc);
Expression* rhs = Expression::make_binary(OPERATOR_MINUS, len, one, loc);
Expression* lhs = this->index_var_->copy();
Statement* as4 = Statement::make_assignment(lhs, rhs, loc);
b->add_statement(as4);
len = Expression::make_temporary_reference(tslen, loc);
zero = zero->copy();
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, len, zero, loc);
return Statement::make_if_statement(cond, b, NULL, loc);
}
// Return the break LABEL_EXPR. // Return the break LABEL_EXPR.
Unnamed_label* Unnamed_label*
......
...@@ -1622,6 +1622,11 @@ class For_range_statement : public Statement ...@@ -1622,6 +1622,11 @@ class For_range_statement : public Statement
lower_map_range_clear(Type*, Block*, Expression*, Named_object*, lower_map_range_clear(Type*, Block*, Expression*, Named_object*,
Temporary_statement*, Location); Temporary_statement*, Location);
Statement*
lower_array_range_clear(Gogo*, Type*, Expression*, Block*,
Named_object*, Temporary_statement*,
Location);
// The variable which is set to the index value. // The variable which is set to the index value.
Expression* index_var_; Expression* index_var_;
// The variable which is set to the element value. This may be // The variable which is set to the element value. This may be
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
// //
//go:linkname typedmemmove runtime.typedmemmove //go:linkname typedmemmove runtime.typedmemmove
//go:linkname typedslicecopy runtime.typedslicecopy //go:linkname typedslicecopy runtime.typedslicecopy
//go:linkname memclrHasPointers runtime.memclrHasPointers
// Go uses a hybrid barrier that combines a Yuasa-style deletion // Go uses a hybrid barrier that combines a Yuasa-style deletion
// barrier—which shades the object whose reference is being // barrier—which shades the object whose reference is being
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment