Commit 39de1955 by Ian Lance Taylor

compiler: rework static initializer code

    
    Rename is_immutable to is_static_initializer to try to capture what it
    really means.  Be more precise about when an address expression, or a
    binary expression, can be a static initializer.  Don't check whether a
    type has pointers when deciding whether an initializer must be
    read-write, just check whether it is being used to initialize a global
    variable.  To make that work set the Translate_context function to NULL
    for a global variable with a static initializer.
    
    The effect of this is to let more global variables be initialized
    directly, rather than being initialized in the generated init function.
    
    Reviewed-on: https://go-review.googlesource.com/32917

From-SVN: r242024
parent 677aa9b4
afe0456d25e3c6c0d91a8fd4c0fdfdbaa35cc251 cac897bd27885c18a16dacfe27d5efd4526455c5
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.
...@@ -536,10 +536,6 @@ class Error_expression : public Expression ...@@ -536,10 +536,6 @@ class Error_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const
{ return true; }
bool
do_numeric_constant_value(Numeric_constant* nc) const do_numeric_constant_value(Numeric_constant* nc) const
{ {
nc->set_unsigned_long(NULL, 0); nc->set_unsigned_long(NULL, 0);
...@@ -1374,7 +1370,7 @@ class Func_code_reference_expression : public Expression ...@@ -1374,7 +1370,7 @@ class Func_code_reference_expression : public Expression
{ return TRAVERSE_CONTINUE; } { return TRAVERSE_CONTINUE; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
Type* Type*
...@@ -1520,7 +1516,7 @@ class Boolean_expression : public Expression ...@@ -1520,7 +1516,7 @@ class Boolean_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
Type* Type*
...@@ -1889,7 +1885,7 @@ class Integer_expression : public Expression ...@@ -1889,7 +1885,7 @@ class Integer_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
bool bool
...@@ -2285,7 +2281,7 @@ class Float_expression : public Expression ...@@ -2285,7 +2281,7 @@ class Float_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
bool bool
...@@ -2475,7 +2471,7 @@ class Complex_expression : public Expression ...@@ -2475,7 +2471,7 @@ class Complex_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
bool bool
...@@ -2691,7 +2687,7 @@ class Const_expression : public Expression ...@@ -2691,7 +2687,7 @@ class Const_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
bool bool
...@@ -3047,7 +3043,7 @@ class Nil_expression : public Expression ...@@ -3047,7 +3043,7 @@ class Nil_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
Type* Type*
...@@ -3284,10 +3280,11 @@ Type_conversion_expression::do_is_constant() const ...@@ -3284,10 +3280,11 @@ Type_conversion_expression::do_is_constant() const
return true; return true;
} }
// Return whether a type conversion is immutable. // Return whether a type conversion can be used in a constant
// initializer.
bool bool
Type_conversion_expression::do_is_immutable() const Type_conversion_expression::do_is_static_initializer() const
{ {
Type* type = this->type_; Type* type = this->type_;
Type* expr_type = this->expr_->type(); Type* expr_type = this->expr_->type();
...@@ -3296,7 +3293,7 @@ Type_conversion_expression::do_is_immutable() const ...@@ -3296,7 +3293,7 @@ Type_conversion_expression::do_is_immutable() const
|| expr_type->interface_type() != NULL) || expr_type->interface_type() != NULL)
return false; return false;
if (!this->expr_->is_immutable()) if (!this->expr_->is_static_initializer())
return false; return false;
if (Type::are_identical(type, expr_type, false, NULL)) if (Type::are_identical(type, expr_type, false, NULL))
...@@ -3542,10 +3539,11 @@ Unsafe_type_conversion_expression::do_traverse(Traverse* traverse) ...@@ -3542,10 +3539,11 @@ Unsafe_type_conversion_expression::do_traverse(Traverse* traverse)
return TRAVERSE_CONTINUE; return TRAVERSE_CONTINUE;
} }
// Return whether an unsafe type conversion is immutable. // Return whether an unsafe type conversion can be used as a constant
// initializer.
bool bool
Unsafe_type_conversion_expression::do_is_immutable() const Unsafe_type_conversion_expression::do_is_static_initializer() const
{ {
Type* type = this->type_; Type* type = this->type_;
Type* expr_type = this->expr_->type(); Type* expr_type = this->expr_->type();
...@@ -3554,7 +3552,7 @@ Unsafe_type_conversion_expression::do_is_immutable() const ...@@ -3554,7 +3552,7 @@ Unsafe_type_conversion_expression::do_is_immutable() const
|| expr_type->interface_type() != NULL) || expr_type->interface_type() != NULL)
return false; return false;
if (!this->expr_->is_immutable()) if (!this->expr_->is_static_initializer())
return false; return false;
if (Type::are_convertible(type, expr_type, NULL)) if (Type::are_convertible(type, expr_type, NULL))
...@@ -3855,6 +3853,44 @@ Unary_expression::do_is_constant() const ...@@ -3855,6 +3853,44 @@ Unary_expression::do_is_constant() const
return this->expr_->is_constant(); return this->expr_->is_constant();
} }
// Return whether a unary expression can be used as a constant
// initializer.
bool
Unary_expression::do_is_static_initializer() const
{
if (this->op_ == OPERATOR_MULT)
return false;
else if (this->op_ == OPERATOR_AND)
{
// The address of a global variable can used as a static
// initializer.
Var_expression* ve = this->expr_->var_expression();
if (ve != NULL)
{
Named_object* no = ve->named_object();
return no->is_variable() && no->var_value()->is_global();
}
// The address of a composite literal can be used as a static
// initializer if the composite literal is itself usable as a
// static initializer.
if (this->expr_->is_composite_literal()
&& this->expr_->is_static_initializer())
return true;
// The address of a string constant can be used as a static
// initializer. This can not be written in Go itself but this
// is used when building a type descriptor.
if (this->expr_->string_expression() != NULL)
return true;
return false;
}
else
return this->expr_->is_static_initializer();
}
// Apply unary opcode OP to UNC, setting NC. Return true if this // Apply unary opcode OP to UNC, setting NC. Return true if this
// could be done, false if not. Issue errors for overflow. // could be done, false if not. Issue errors for overflow.
...@@ -4207,7 +4243,7 @@ Unary_expression::do_get_backend(Translate_context* context) ...@@ -4207,7 +4243,7 @@ Unary_expression::do_get_backend(Translate_context* context)
// constructor will not do what the programmer expects. // constructor will not do what the programmer expects.
go_assert(!this->expr_->is_composite_literal() go_assert(!this->expr_->is_composite_literal()
|| this->expr_->is_immutable()); || this->expr_->is_static_initializer());
if (this->expr_->classification() == EXPRESSION_UNARY) if (this->expr_->classification() == EXPRESSION_UNARY)
{ {
Unary_expression* ue = Unary_expression* ue =
...@@ -4245,8 +4281,7 @@ Unary_expression::do_get_backend(Translate_context* context) ...@@ -4245,8 +4281,7 @@ Unary_expression::do_get_backend(Translate_context* context)
// initialize the value once, so we can use this directly // initialize the value once, so we can use this directly
// rather than copying it. In that case we can't make it // rather than copying it. In that case we can't make it
// read-only, because the program is permitted to change it. // read-only, because the program is permitted to change it.
copy_to_heap = (at->element_type()->has_pointer() copy_to_heap = context->function() != NULL;
&& !context->is_const());
} }
Bvariable* implicit = Bvariable* implicit =
gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap, gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap,
...@@ -4258,7 +4293,7 @@ Unary_expression::do_get_backend(Translate_context* context) ...@@ -4258,7 +4293,7 @@ Unary_expression::do_get_backend(Translate_context* context)
} }
else if ((this->expr_->is_composite_literal() else if ((this->expr_->is_composite_literal()
|| this->expr_->string_expression() != NULL) || this->expr_->string_expression() != NULL)
&& this->expr_->is_immutable()) && this->expr_->is_static_initializer())
{ {
// Build a decl for a constant constructor. // Build a decl for a constant constructor.
snprintf(buf, sizeof buf, "C%u", counter); snprintf(buf, sizeof buf, "C%u", counter);
...@@ -4426,6 +4461,33 @@ Binary_expression::do_traverse(Traverse* traverse) ...@@ -4426,6 +4461,33 @@ Binary_expression::do_traverse(Traverse* traverse)
return Expression::traverse(&this->right_, traverse); return Expression::traverse(&this->right_, traverse);
} }
// Return whether this expression may be used as a static initializer.
bool
Binary_expression::do_is_static_initializer() const
{
if (!this->left_->is_static_initializer()
|| !this->right_->is_static_initializer())
return false;
// Addresses can be static initializers, but we can't implement
// arbitray binary expressions of them.
Unary_expression* lu = this->left_->unary_expression();
Unary_expression* ru = this->right_->unary_expression();
if (lu != NULL && lu->op() == OPERATOR_AND)
{
if (ru != NULL && ru->op() == OPERATOR_AND)
return this->op_ == OPERATOR_MINUS;
else
return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS;
}
else if (ru != NULL && ru->op() == OPERATOR_AND)
return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS;
// Other cases should resolve in the backend.
return true;
}
// Return the type to use for a binary operation on operands of // Return the type to use for a binary operation on operands of
// LEFT_TYPE and RIGHT_TYPE. These are the types of constants and as // LEFT_TYPE and RIGHT_TYPE. These are the types of constants and as
// such may be NULL or abstract. // such may be NULL or abstract.
...@@ -6325,13 +6387,13 @@ String_concat_expression::do_is_constant() const ...@@ -6325,13 +6387,13 @@ String_concat_expression::do_is_constant() const
} }
bool bool
String_concat_expression::do_is_immutable() 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();
pe != this->exprs_->end(); pe != this->exprs_->end();
++pe) ++pe)
{ {
if (!(*pe)->is_immutable()) if (!(*pe)->is_static_initializer())
return false; return false;
} }
return true; return true;
...@@ -12275,10 +12337,10 @@ Struct_construction_expression::is_constant_struct() const ...@@ -12275,10 +12337,10 @@ Struct_construction_expression::is_constant_struct() const
return true; return true;
} }
// Return whether this struct is immutable. // Return whether this struct can be used as a constant initializer.
bool bool
Struct_construction_expression::do_is_immutable() const Struct_construction_expression::do_is_static_initializer() const
{ {
if (this->vals() == NULL) if (this->vals() == NULL)
return true; return true;
...@@ -12286,7 +12348,7 @@ Struct_construction_expression::do_is_immutable() const ...@@ -12286,7 +12348,7 @@ Struct_construction_expression::do_is_immutable() const
pv != this->vals()->end(); pv != this->vals()->end();
++pv) ++pv)
{ {
if (*pv != NULL && !(*pv)->is_immutable()) if (*pv != NULL && !(*pv)->is_static_initializer())
return false; return false;
} }
return true; return true;
...@@ -12523,10 +12585,10 @@ Array_construction_expression::is_constant_array() const ...@@ -12523,10 +12585,10 @@ Array_construction_expression::is_constant_array() const
return true; return true;
} }
// Return whether this is an immutable array initializer. // Return whether this can be used a constant initializer.
bool bool
Array_construction_expression::do_is_immutable() const Array_construction_expression::do_is_static_initializer() const
{ {
if (this->vals() == NULL) if (this->vals() == NULL)
return true; return true;
...@@ -12534,7 +12596,7 @@ Array_construction_expression::do_is_immutable() const ...@@ -12534,7 +12596,7 @@ Array_construction_expression::do_is_immutable() const
pv != this->vals()->end(); pv != this->vals()->end();
++pv) ++pv)
{ {
if (*pv != NULL && !(*pv)->is_immutable()) if (*pv != NULL && !(*pv)->is_static_initializer())
return false; return false;
} }
return true; return true;
...@@ -12904,19 +12966,12 @@ Slice_construction_expression::do_get_backend(Translate_context* context) ...@@ -12904,19 +12966,12 @@ Slice_construction_expression::do_get_backend(Translate_context* context)
} }
Location loc = this->location(); Location loc = this->location();
Array_type* array_type = this->type()->array_type();
Type* element_type = array_type->element_type();
bool is_constant_initializer = this->array_val_->is_immutable(); bool is_static_initializer = this->array_val_->is_static_initializer();
// We have to copy the initial values into heap memory if we are in // We have to copy the initial values into heap memory if we are in
// a function or if the values are not constants. We also have to // a function or if the values are not constants.
// copy them if they may contain pointers in a non-constant context, bool copy_to_heap = context->function() != NULL || !is_static_initializer;
// as otherwise the garbage collector won't see them.
bool copy_to_heap = (context->function() != NULL
|| !is_constant_initializer
|| (element_type->has_pointer()
&& !context->is_const()));
Expression* space; Expression* space;
...@@ -14206,7 +14261,7 @@ class Type_descriptor_expression : public Expression ...@@ -14206,7 +14261,7 @@ class Type_descriptor_expression : public Expression
{ return Type::make_type_descriptor_ptr_type(); } { return Type::make_type_descriptor_ptr_type(); }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
void void
...@@ -14274,7 +14329,7 @@ class GC_symbol_expression : public Expression ...@@ -14274,7 +14329,7 @@ class GC_symbol_expression : public Expression
{ return Type::lookup_integer_type("uintptr"); } { return Type::lookup_integer_type("uintptr"); }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
void void
...@@ -14332,7 +14387,7 @@ class Type_info_expression : public Expression ...@@ -14332,7 +14387,7 @@ class Type_info_expression : public Expression
protected: protected:
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
Type* Type*
...@@ -14913,7 +14968,7 @@ class Interface_mtable_expression : public Expression ...@@ -14913,7 +14968,7 @@ class Interface_mtable_expression : public Expression
do_type(); do_type();
bool bool
is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
void void
...@@ -15104,7 +15159,7 @@ class Struct_field_offset_expression : public Expression ...@@ -15104,7 +15159,7 @@ class Struct_field_offset_expression : public Expression
protected: protected:
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
Type* Type*
......
...@@ -500,10 +500,20 @@ class Expression ...@@ -500,10 +500,20 @@ class Expression
is_constant() const is_constant() const
{ return this->do_is_constant(); } { return this->do_is_constant(); }
// Return whether this is an immutable expression. // Return whether this expression can be used as a static
bool // initializer. This is true for an expression that has only
is_immutable() const // numbers and pointers to global variables or composite literals
{ return this->do_is_immutable(); } // that do not require runtime initialization. It is false if we
// must generate code to compute this expression when it is used to
// initialize a global variable. This is not a language-level
// concept, but an implementation-level one. If this expression is
// used to initialize a global variable, this is true if we can pass
// an initializer to the backend, false if we must generate code to
// initialize the variable. It is always safe for this method to
// return false, but the resulting code may be less efficient.
bool
is_static_initializer() const
{ return this->do_is_static_initializer(); }
// If this is not a numeric constant, return false. If it is one, // If this is not a numeric constant, return false. If it is one,
// return true, and set VAL to hold the value. // return true, and set VAL to hold the value.
...@@ -991,9 +1001,10 @@ class Expression ...@@ -991,9 +1001,10 @@ class Expression
do_is_constant() const do_is_constant() const
{ return false; } { return false; }
// Return whether this is an immutable expression. // Return whether this expression can be used as a constant
// initializer.
virtual bool virtual bool
do_is_immutable() const do_is_static_initializer() const
{ return false; } { return false; }
// Return whether this is a constant expression of numeric type, and // Return whether this is a constant expression of numeric type, and
...@@ -1508,7 +1519,7 @@ class String_expression : public Expression ...@@ -1508,7 +1519,7 @@ class String_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const do_is_static_initializer() const
{ return true; } { return true; }
bool bool
...@@ -1595,7 +1606,7 @@ class Type_conversion_expression : public Expression ...@@ -1595,7 +1606,7 @@ class Type_conversion_expression : public Expression
do_is_constant() const; do_is_constant() const;
bool bool
do_is_immutable() const; do_is_static_initializer() const;
bool bool
do_numeric_constant_value(Numeric_constant*) const; do_numeric_constant_value(Numeric_constant*) const;
...@@ -1659,7 +1670,7 @@ class Unsafe_type_conversion_expression : public Expression ...@@ -1659,7 +1670,7 @@ class Unsafe_type_conversion_expression : public Expression
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool bool
do_is_immutable() const; do_is_static_initializer() const;
Type* Type*
do_type() do_type()
...@@ -1770,11 +1781,7 @@ class Unary_expression : public Expression ...@@ -1770,11 +1781,7 @@ class Unary_expression : public Expression
do_is_constant() const; do_is_constant() const;
bool bool
do_is_immutable() const do_is_static_initializer() const;
{
return (this->expr_->is_immutable()
|| (this->op_ == OPERATOR_AND && this->expr_->is_variable()));
}
bool bool
do_numeric_constant_value(Numeric_constant*) const; do_numeric_constant_value(Numeric_constant*) const;
...@@ -1913,8 +1920,7 @@ class Binary_expression : public Expression ...@@ -1913,8 +1920,7 @@ class Binary_expression : public Expression
{ return this->left_->is_constant() && this->right_->is_constant(); } { return this->left_->is_constant() && this->right_->is_constant(); }
bool bool
do_is_immutable() const do_is_static_initializer() const;
{ return this->left_->is_immutable() && this->right_->is_immutable(); }
bool bool
do_numeric_constant_value(Numeric_constant*) const; do_numeric_constant_value(Numeric_constant*) const;
...@@ -2029,7 +2035,7 @@ class String_concat_expression : public Expression ...@@ -2029,7 +2035,7 @@ class String_concat_expression : public Expression
do_is_constant() const; do_is_constant() const;
bool bool
do_is_immutable() const; do_is_static_initializer() const;
Type* Type*
do_type(); do_type();
...@@ -3295,7 +3301,7 @@ class Struct_construction_expression : public Expression, ...@@ -3295,7 +3301,7 @@ class Struct_construction_expression : public Expression,
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool bool
do_is_immutable() const; do_is_static_initializer() const;
Type* Type*
do_type() do_type()
...@@ -3370,7 +3376,7 @@ protected: ...@@ -3370,7 +3376,7 @@ protected:
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool bool
do_is_immutable() const; do_is_static_initializer() const;
Type* Type*
do_type() do_type()
......
...@@ -1299,29 +1299,35 @@ Gogo::write_globals() ...@@ -1299,29 +1299,35 @@ Gogo::write_globals()
// The initializer is constant if it is the zero-value of the // The initializer is constant if it is the zero-value of the
// variable's type or if the initial value is an immutable value // variable's type or if the initial value is an immutable value
// that is not copied to the heap. // that is not copied to the heap.
bool is_constant_initializer = false; bool is_static_initializer = false;
if (var->init() == NULL) if (var->init() == NULL)
is_constant_initializer = true; is_static_initializer = true;
else else
{ {
Type* var_type = var->type(); Type* var_type = var->type();
Expression* init = var->init(); Expression* init = var->init();
Expression* init_cast = Expression* init_cast =
Expression::make_cast(var_type, init, var->location()); Expression::make_cast(var_type, init, var->location());
is_constant_initializer = is_static_initializer = init_cast->is_static_initializer();
init_cast->is_immutable() && !var_type->has_pointer();
} }
// Non-constant variable initializations might need to create // Non-constant variable initializations might need to create
// temporary variables, which will need the initialization // temporary variables, which will need the initialization
// function as context. // function as context.
if (!is_constant_initializer && init_fndecl == NULL) Named_object* var_init_fn;
if (is_static_initializer)
var_init_fn = NULL;
else
{
if (init_fndecl == NULL)
init_fndecl = this->initialization_function_decl(); init_fndecl = this->initialization_function_decl();
Bexpression* var_binit = var->get_init(this, init_fndecl); var_init_fn = init_fndecl;
}
Bexpression* var_binit = var->get_init(this, var_init_fn);
if (var_binit == NULL) if (var_binit == NULL)
; ;
else if (is_constant_initializer) else if (is_static_initializer)
{ {
if (expression_requires(var->init(), NULL, if (expression_requires(var->init(), NULL,
this->var_depends_on(var), no)) this->var_depends_on(var), no))
......
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