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
This commit is contained in:
parent
677aa9b4b7
commit
39de19551b
|
@ -1,4 +1,4 @@
|
|||
afe0456d25e3c6c0d91a8fd4c0fdfdbaa35cc251
|
||||
cac897bd27885c18a16dacfe27d5efd4526455c5
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -535,10 +535,6 @@ class Error_expression : public Expression
|
|||
do_is_constant() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant* nc) const
|
||||
{
|
||||
|
@ -1374,7 +1370,7 @@ class Func_code_reference_expression : public Expression
|
|||
{ return TRAVERSE_CONTINUE; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
Type*
|
||||
|
@ -1520,7 +1516,7 @@ class Boolean_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
Type*
|
||||
|
@ -1889,7 +1885,7 @@ class Integer_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
|
@ -2285,7 +2281,7 @@ class Float_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
|
@ -2475,7 +2471,7 @@ class Complex_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
|
@ -2691,7 +2687,7 @@ class Const_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
|
@ -3047,7 +3043,7 @@ class Nil_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
Type*
|
||||
|
@ -3284,10 +3280,11 @@ Type_conversion_expression::do_is_constant() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// Return whether a type conversion is immutable.
|
||||
// Return whether a type conversion can be used in a constant
|
||||
// initializer.
|
||||
|
||||
bool
|
||||
Type_conversion_expression::do_is_immutable() const
|
||||
Type_conversion_expression::do_is_static_initializer() const
|
||||
{
|
||||
Type* type = this->type_;
|
||||
Type* expr_type = this->expr_->type();
|
||||
|
@ -3296,7 +3293,7 @@ Type_conversion_expression::do_is_immutable() const
|
|||
|| expr_type->interface_type() != NULL)
|
||||
return false;
|
||||
|
||||
if (!this->expr_->is_immutable())
|
||||
if (!this->expr_->is_static_initializer())
|
||||
return false;
|
||||
|
||||
if (Type::are_identical(type, expr_type, false, NULL))
|
||||
|
@ -3542,10 +3539,11 @@ Unsafe_type_conversion_expression::do_traverse(Traverse* traverse)
|
|||
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
|
||||
Unsafe_type_conversion_expression::do_is_immutable() const
|
||||
Unsafe_type_conversion_expression::do_is_static_initializer() const
|
||||
{
|
||||
Type* type = this->type_;
|
||||
Type* expr_type = this->expr_->type();
|
||||
|
@ -3554,7 +3552,7 @@ Unsafe_type_conversion_expression::do_is_immutable() const
|
|||
|| expr_type->interface_type() != NULL)
|
||||
return false;
|
||||
|
||||
if (!this->expr_->is_immutable())
|
||||
if (!this->expr_->is_static_initializer())
|
||||
return false;
|
||||
|
||||
if (Type::are_convertible(type, expr_type, NULL))
|
||||
|
@ -3855,6 +3853,44 @@ Unary_expression::do_is_constant() const
|
|||
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
|
||||
// could be done, false if not. Issue errors for overflow.
|
||||
|
||||
|
@ -4207,7 +4243,7 @@ Unary_expression::do_get_backend(Translate_context* context)
|
|||
// constructor will not do what the programmer expects.
|
||||
|
||||
go_assert(!this->expr_->is_composite_literal()
|
||||
|| this->expr_->is_immutable());
|
||||
|| this->expr_->is_static_initializer());
|
||||
if (this->expr_->classification() == EXPRESSION_UNARY)
|
||||
{
|
||||
Unary_expression* ue =
|
||||
|
@ -4245,8 +4281,7 @@ Unary_expression::do_get_backend(Translate_context* context)
|
|||
// initialize the value once, so we can use this directly
|
||||
// rather than copying it. In that case we can't make it
|
||||
// read-only, because the program is permitted to change it.
|
||||
copy_to_heap = (at->element_type()->has_pointer()
|
||||
&& !context->is_const());
|
||||
copy_to_heap = context->function() != NULL;
|
||||
}
|
||||
Bvariable* implicit =
|
||||
gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap,
|
||||
|
@ -4257,8 +4292,8 @@ Unary_expression::do_get_backend(Translate_context* context)
|
|||
bexpr = gogo->backend()->var_expression(implicit, loc);
|
||||
}
|
||||
else if ((this->expr_->is_composite_literal()
|
||||
|| this->expr_->string_expression() != NULL)
|
||||
&& this->expr_->is_immutable())
|
||||
|| this->expr_->string_expression() != NULL)
|
||||
&& this->expr_->is_static_initializer())
|
||||
{
|
||||
// Build a decl for a constant constructor.
|
||||
snprintf(buf, sizeof buf, "C%u", counter);
|
||||
|
@ -4426,6 +4461,33 @@ Binary_expression::do_traverse(Traverse* 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
|
||||
// LEFT_TYPE and RIGHT_TYPE. These are the types of constants and as
|
||||
// such may be NULL or abstract.
|
||||
|
@ -6325,13 +6387,13 @@ String_concat_expression::do_is_constant() const
|
|||
}
|
||||
|
||||
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();
|
||||
pe != this->exprs_->end();
|
||||
++pe)
|
||||
{
|
||||
if (!(*pe)->is_immutable())
|
||||
if (!(*pe)->is_static_initializer())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -12275,10 +12337,10 @@ Struct_construction_expression::is_constant_struct() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// Return whether this struct is immutable.
|
||||
// Return whether this struct can be used as a constant initializer.
|
||||
|
||||
bool
|
||||
Struct_construction_expression::do_is_immutable() const
|
||||
Struct_construction_expression::do_is_static_initializer() const
|
||||
{
|
||||
if (this->vals() == NULL)
|
||||
return true;
|
||||
|
@ -12286,7 +12348,7 @@ Struct_construction_expression::do_is_immutable() const
|
|||
pv != this->vals()->end();
|
||||
++pv)
|
||||
{
|
||||
if (*pv != NULL && !(*pv)->is_immutable())
|
||||
if (*pv != NULL && !(*pv)->is_static_initializer())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -12523,10 +12585,10 @@ Array_construction_expression::is_constant_array() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// Return whether this is an immutable array initializer.
|
||||
// Return whether this can be used a constant initializer.
|
||||
|
||||
bool
|
||||
Array_construction_expression::do_is_immutable() const
|
||||
Array_construction_expression::do_is_static_initializer() const
|
||||
{
|
||||
if (this->vals() == NULL)
|
||||
return true;
|
||||
|
@ -12534,7 +12596,7 @@ Array_construction_expression::do_is_immutable() const
|
|||
pv != this->vals()->end();
|
||||
++pv)
|
||||
{
|
||||
if (*pv != NULL && !(*pv)->is_immutable())
|
||||
if (*pv != NULL && !(*pv)->is_static_initializer())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -12904,19 +12966,12 @@ Slice_construction_expression::do_get_backend(Translate_context* context)
|
|||
}
|
||||
|
||||
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
|
||||
// a function or if the values are not constants. We also have to
|
||||
// copy them if they may contain pointers in a non-constant context,
|
||||
// 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()));
|
||||
// a function or if the values are not constants.
|
||||
bool copy_to_heap = context->function() != NULL || !is_static_initializer;
|
||||
|
||||
Expression* space;
|
||||
|
||||
|
@ -14206,7 +14261,7 @@ class Type_descriptor_expression : public Expression
|
|||
{ return Type::make_type_descriptor_ptr_type(); }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
void
|
||||
|
@ -14274,7 +14329,7 @@ class GC_symbol_expression : public Expression
|
|||
{ return Type::lookup_integer_type("uintptr"); }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
void
|
||||
|
@ -14332,7 +14387,7 @@ class Type_info_expression : public Expression
|
|||
|
||||
protected:
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
Type*
|
||||
|
@ -14913,7 +14968,7 @@ class Interface_mtable_expression : public Expression
|
|||
do_type();
|
||||
|
||||
bool
|
||||
is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
void
|
||||
|
@ -15104,7 +15159,7 @@ class Struct_field_offset_expression : public Expression
|
|||
|
||||
protected:
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
Type*
|
||||
|
|
|
@ -500,10 +500,20 @@ class Expression
|
|||
is_constant() const
|
||||
{ return this->do_is_constant(); }
|
||||
|
||||
// Return whether this is an immutable expression.
|
||||
// Return whether this expression can be used as a static
|
||||
// initializer. This is true for an expression that has only
|
||||
// numbers and pointers to global variables or composite literals
|
||||
// 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_immutable() const
|
||||
{ return this->do_is_immutable(); }
|
||||
is_static_initializer() const
|
||||
{ return this->do_is_static_initializer(); }
|
||||
|
||||
// If this is not a numeric constant, return false. If it is one,
|
||||
// return true, and set VAL to hold the value.
|
||||
|
@ -991,9 +1001,10 @@ class Expression
|
|||
do_is_constant() const
|
||||
{ return false; }
|
||||
|
||||
// Return whether this is an immutable expression.
|
||||
// Return whether this expression can be used as a constant
|
||||
// initializer.
|
||||
virtual bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return false; }
|
||||
|
||||
// Return whether this is a constant expression of numeric type, and
|
||||
|
@ -1508,7 +1519,7 @@ class String_expression : public Expression
|
|||
{ return true; }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
do_is_static_initializer() const
|
||||
{ return true; }
|
||||
|
||||
bool
|
||||
|
@ -1595,7 +1606,7 @@ class Type_conversion_expression : public Expression
|
|||
do_is_constant() const;
|
||||
|
||||
bool
|
||||
do_is_immutable() const;
|
||||
do_is_static_initializer() const;
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant*) const;
|
||||
|
@ -1659,7 +1670,7 @@ class Unsafe_type_conversion_expression : public Expression
|
|||
do_traverse(Traverse* traverse);
|
||||
|
||||
bool
|
||||
do_is_immutable() const;
|
||||
do_is_static_initializer() const;
|
||||
|
||||
Type*
|
||||
do_type()
|
||||
|
@ -1770,11 +1781,7 @@ class Unary_expression : public Expression
|
|||
do_is_constant() const;
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
{
|
||||
return (this->expr_->is_immutable()
|
||||
|| (this->op_ == OPERATOR_AND && this->expr_->is_variable()));
|
||||
}
|
||||
do_is_static_initializer() const;
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant*) const;
|
||||
|
@ -1913,8 +1920,7 @@ class Binary_expression : public Expression
|
|||
{ return this->left_->is_constant() && this->right_->is_constant(); }
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
{ return this->left_->is_immutable() && this->right_->is_immutable(); }
|
||||
do_is_static_initializer() const;
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant*) const;
|
||||
|
@ -2029,7 +2035,7 @@ class String_concat_expression : public Expression
|
|||
do_is_constant() const;
|
||||
|
||||
bool
|
||||
do_is_immutable() const;
|
||||
do_is_static_initializer() const;
|
||||
|
||||
Type*
|
||||
do_type();
|
||||
|
@ -3295,7 +3301,7 @@ class Struct_construction_expression : public Expression,
|
|||
do_traverse(Traverse* traverse);
|
||||
|
||||
bool
|
||||
do_is_immutable() const;
|
||||
do_is_static_initializer() const;
|
||||
|
||||
Type*
|
||||
do_type()
|
||||
|
@ -3370,7 +3376,7 @@ protected:
|
|||
do_traverse(Traverse* traverse);
|
||||
|
||||
bool
|
||||
do_is_immutable() const;
|
||||
do_is_static_initializer() const;
|
||||
|
||||
Type*
|
||||
do_type()
|
||||
|
|
|
@ -1299,29 +1299,35 @@ Gogo::write_globals()
|
|||
// The initializer is constant if it is the zero-value of the
|
||||
// variable's type or if the initial value is an immutable value
|
||||
// that is not copied to the heap.
|
||||
bool is_constant_initializer = false;
|
||||
bool is_static_initializer = false;
|
||||
if (var->init() == NULL)
|
||||
is_constant_initializer = true;
|
||||
is_static_initializer = true;
|
||||
else
|
||||
{
|
||||
Type* var_type = var->type();
|
||||
Expression* init = var->init();
|
||||
Expression* init_cast =
|
||||
Expression::make_cast(var_type, init, var->location());
|
||||
is_constant_initializer =
|
||||
init_cast->is_immutable() && !var_type->has_pointer();
|
||||
is_static_initializer = init_cast->is_static_initializer();
|
||||
}
|
||||
|
||||
// Non-constant variable initializations might need to create
|
||||
// temporary variables, which will need the initialization
|
||||
// function as context.
|
||||
if (!is_constant_initializer && init_fndecl == NULL)
|
||||
init_fndecl = this->initialization_function_decl();
|
||||
Bexpression* var_binit = var->get_init(this, init_fndecl);
|
||||
Named_object* var_init_fn;
|
||||
if (is_static_initializer)
|
||||
var_init_fn = NULL;
|
||||
else
|
||||
{
|
||||
if (init_fndecl == NULL)
|
||||
init_fndecl = this->initialization_function_decl();
|
||||
var_init_fn = init_fndecl;
|
||||
}
|
||||
Bexpression* var_binit = var->get_init(this, var_init_fn);
|
||||
|
||||
if (var_binit == NULL)
|
||||
;
|
||||
else if (is_constant_initializer)
|
||||
else if (is_static_initializer)
|
||||
{
|
||||
if (expression_requires(var->init(), NULL,
|
||||
this->var_depends_on(var), no))
|
||||
|
|
Loading…
Reference in New Issue