compiler: Use backend interface for slice construction.
* go-gcc.cc (Gcc_backend::implicit_variable): Rename from gc_root_variable. Add name and is_constant parameters. From-SVN: r210088
This commit is contained in:
parent
3e7b0938f1
commit
fb930306ba
5 changed files with 96 additions and 111 deletions
|
@ -1,3 +1,8 @@
|
|||
2014-05-05 Chris Manghane <cmang@google.com>
|
||||
|
||||
* go-gcc.cc (Gcc_backend::implicit_variable): Rename from
|
||||
gc_root_variable. Add name and is_constant parameters.
|
||||
|
||||
2014-05-05 Chris Manghane <cmang@google.com>
|
||||
|
||||
* go-gcc.cc (Gcc_backend::indirect_expression): Add btype
|
||||
|
|
|
@ -381,7 +381,7 @@ class Gcc_backend : public Backend
|
|||
Location, Bstatement**);
|
||||
|
||||
Bvariable*
|
||||
gc_root_variable(Btype*, Bexpression*);
|
||||
implicit_variable(const std::string&, Btype*, Bexpression*, bool);
|
||||
|
||||
Bvariable*
|
||||
immutable_struct(const std::string&, bool, bool, Btype*, Location);
|
||||
|
@ -2476,10 +2476,12 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
|
|||
return new Bvariable(var);
|
||||
}
|
||||
|
||||
// Make a GC root variable.
|
||||
// Create an implicit variable that is compiler-defined. This is used when
|
||||
// generating GC root variables and storing the values of a slice initializer.
|
||||
|
||||
Bvariable*
|
||||
Gcc_backend::gc_root_variable(Btype* type, Bexpression* init)
|
||||
Gcc_backend::implicit_variable(const std::string& name, Btype* type,
|
||||
Bexpression* init, bool is_constant)
|
||||
{
|
||||
tree type_tree = type->get_tree();
|
||||
tree init_tree = init->get_tree();
|
||||
|
@ -2487,11 +2489,16 @@ Gcc_backend::gc_root_variable(Btype* type, Bexpression* init)
|
|||
return this->error_variable();
|
||||
|
||||
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
|
||||
create_tmp_var_name("gc"), type_tree);
|
||||
get_identifier_from_string(name), type_tree);
|
||||
DECL_EXTERNAL(decl) = 0;
|
||||
TREE_PUBLIC(decl) = 0;
|
||||
TREE_STATIC(decl) = 1;
|
||||
DECL_ARTIFICIAL(decl) = 1;
|
||||
if (is_constant)
|
||||
{
|
||||
TREE_READONLY(decl) = 1;
|
||||
TREE_CONSTANT(decl) = 1;
|
||||
}
|
||||
DECL_INITIAL(decl) = init_tree;
|
||||
rest_of_decl_compilation(decl, 1, 0);
|
||||
|
||||
|
|
|
@ -536,11 +536,16 @@ class Backend
|
|||
bool address_is_taken, Location location,
|
||||
Bstatement** pstatement) = 0;
|
||||
|
||||
// Create a GC root variable. TYPE is the __go_gc_root_list struct described
|
||||
// in Gogo::register_gc_vars. INIT is the composite literal consisting of a
|
||||
// pointer to the next GC root and the global variables registered.
|
||||
// Create an implicit variable that is compiler-defined. This is used when
|
||||
// generating GC root variables and storing the values of a slice constructor.
|
||||
// NAME is the name of the variable, either gc# for GC roots or C# for slice
|
||||
// initializers. TYPE is the type of the implicit variable with an initial
|
||||
// value INIT. IS_CONSTANT is true if the implicit variable should be treated
|
||||
// like it is immutable. For slice initializers, if the values must be copied
|
||||
// to the heap, the variable IS_CONSTANT.
|
||||
virtual Bvariable*
|
||||
gc_root_variable(Btype* type, Bexpression* init) = 0;
|
||||
implicit_variable(const std::string& name, Btype* type, Bexpression* init,
|
||||
bool is_constant) = 0;
|
||||
|
||||
// Create a named immutable initialized data structure. This is
|
||||
// used for type descriptors, map descriptors, and function
|
||||
|
|
|
@ -4113,20 +4113,47 @@ Unary_expression::do_get_tree(Translate_context* context)
|
|||
}
|
||||
}
|
||||
|
||||
if (this->is_gc_root_)
|
||||
static unsigned int counter;
|
||||
char buf[100];
|
||||
if (this->is_gc_root_ || this->is_slice_init_)
|
||||
{
|
||||
// Build a decl for a GC root variable. GC roots are mutable, so they
|
||||
// cannot be represented as an immutable_struct in the backend.
|
||||
Bvariable* gc_root = gogo->backend()->gc_root_variable(btype, bexpr);
|
||||
bexpr = gogo->backend()->var_expression(gc_root, loc);
|
||||
bool copy_to_heap = false;
|
||||
if (this->is_gc_root_)
|
||||
{
|
||||
// Build a decl for a GC root variable. GC roots are mutable, so
|
||||
// they cannot be represented as an immutable_struct in the
|
||||
// backend.
|
||||
static unsigned int root_counter;
|
||||
snprintf(buf, sizeof buf, "gc%u", root_counter);
|
||||
++root_counter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build a decl for a slice value initializer. An immutable slice
|
||||
// value initializer may have to be copied to the heap if it
|
||||
// contains pointers in a non-constant context.
|
||||
snprintf(buf, sizeof buf, "C%u", counter);
|
||||
++counter;
|
||||
|
||||
Array_type* at = this->expr_->type()->array_type();
|
||||
go_assert(at != NULL);
|
||||
|
||||
// If we are not copying the value to the heap, we will only
|
||||
// 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());
|
||||
}
|
||||
Bvariable* implicit =
|
||||
gogo->backend()->implicit_variable(buf, btype, bexpr, copy_to_heap);
|
||||
bexpr = gogo->backend()->var_expression(implicit, loc);
|
||||
}
|
||||
else if ((this->expr_->is_composite_literal()
|
||||
|| this->expr_->string_expression() != NULL)
|
||||
&& this->expr_->is_immutable())
|
||||
{
|
||||
// Build a decl for a constant constructor.
|
||||
static unsigned int counter;
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof buf, "C%u", counter);
|
||||
++counter;
|
||||
|
||||
|
@ -12450,6 +12477,7 @@ Slice_construction_expression::do_get_tree(Translate_context* context)
|
|||
return error_mark_node;
|
||||
}
|
||||
|
||||
Location loc = this->location();
|
||||
Type* element_type = array_type->element_type();
|
||||
if (this->valtype_ == NULL)
|
||||
{
|
||||
|
@ -12464,35 +12492,24 @@ Slice_construction_expression::do_get_tree(Translate_context* context)
|
|||
else
|
||||
mpz_init_set_ui(lenval, this->indexes()->back() + 1);
|
||||
}
|
||||
Location loc = this->location();
|
||||
Type* int_type = Type::lookup_integer_type("int");
|
||||
length = Expression::make_integer(&lenval, int_type, loc);
|
||||
mpz_clear(lenval);
|
||||
this->valtype_ = Type::make_array_type(element_type, length);
|
||||
}
|
||||
|
||||
tree values;
|
||||
Gogo* gogo = context->gogo();
|
||||
Btype* val_btype = this->valtype_->get_backend(gogo);
|
||||
Expression_list* vals = this->vals();
|
||||
if (this->vals() == NULL || this->vals()->empty())
|
||||
{
|
||||
// We need to create a unique value.
|
||||
Btype* int_btype = Type::lookup_integer_type("int")->get_backend(gogo);
|
||||
Bexpression* zero = gogo->backend()->zero_expression(int_btype);
|
||||
std::vector<unsigned long> index(1, 0);
|
||||
std::vector<Bexpression*> val(1, zero);
|
||||
Bexpression* ctor =
|
||||
gogo->backend()->array_constructor_expression(val_btype, index, val,
|
||||
this->location());
|
||||
values = expr_to_tree(ctor);
|
||||
// We need to create a unique value for the empty array literal.
|
||||
vals = new Expression_list;
|
||||
vals->push_back(NULL);
|
||||
}
|
||||
else
|
||||
values = expr_to_tree(this->get_constructor(context, val_btype));
|
||||
Expression* array_val =
|
||||
new Fixed_array_construction_expression(this->valtype_, this->indexes(),
|
||||
vals, loc);
|
||||
|
||||
if (values == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
bool is_constant_initializer = TREE_CONSTANT(values);
|
||||
bool is_constant_initializer = array_val->is_immutable();
|
||||
|
||||
// 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
|
||||
|
@ -12503,89 +12520,22 @@ Slice_construction_expression::do_get_tree(Translate_context* context)
|
|||
|| (element_type->has_pointer()
|
||||
&& !context->is_const()));
|
||||
|
||||
if (is_constant_initializer)
|
||||
{
|
||||
tree tmp = build_decl(this->location().gcc_location(), VAR_DECL,
|
||||
create_tmp_var_name("C"), TREE_TYPE(values));
|
||||
DECL_EXTERNAL(tmp) = 0;
|
||||
TREE_PUBLIC(tmp) = 0;
|
||||
TREE_STATIC(tmp) = 1;
|
||||
DECL_ARTIFICIAL(tmp) = 1;
|
||||
if (copy_to_heap)
|
||||
{
|
||||
// If we are not copying the value to the heap, we will only
|
||||
// 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.
|
||||
TREE_READONLY(tmp) = 1;
|
||||
TREE_CONSTANT(tmp) = 1;
|
||||
}
|
||||
DECL_INITIAL(tmp) = values;
|
||||
rest_of_decl_compilation(tmp, 1, 0);
|
||||
values = tmp;
|
||||
}
|
||||
|
||||
tree space;
|
||||
tree set;
|
||||
Expression* space;
|
||||
if (!copy_to_heap)
|
||||
{
|
||||
// the initializer will only run once.
|
||||
space = build_fold_addr_expr(values);
|
||||
set = NULL_TREE;
|
||||
// The initializer will only run once.
|
||||
space = Expression::make_unary(OPERATOR_AND, array_val, loc);
|
||||
space->unary_expression()->set_is_slice_init();
|
||||
}
|
||||
else
|
||||
{
|
||||
Expression* alloc =
|
||||
context->gogo()->allocate_memory(this->valtype_, this->location());
|
||||
space = save_expr(alloc->get_tree(context));
|
||||
|
||||
tree s = fold_convert(build_pointer_type(TREE_TYPE(values)), space);
|
||||
tree ref = build_fold_indirect_ref_loc(this->location().gcc_location(),
|
||||
s);
|
||||
TREE_THIS_NOTRAP(ref) = 1;
|
||||
set = build2(MODIFY_EXPR, void_type_node, ref, values);
|
||||
}
|
||||
space = Expression::make_heap_expression(array_val, loc);
|
||||
|
||||
// Build a constructor for the slice.
|
||||
|
||||
tree type_tree = type_to_tree(this->type()->get_backend(context->gogo()));
|
||||
if (type_tree == error_mark_node)
|
||||
return error_mark_node;
|
||||
go_assert(TREE_CODE(type_tree) == RECORD_TYPE);
|
||||
|
||||
vec<constructor_elt, va_gc> *init;
|
||||
vec_alloc(init, 3);
|
||||
|
||||
constructor_elt empty = {NULL, NULL};
|
||||
constructor_elt* elt = init->quick_push(empty);
|
||||
tree field = TYPE_FIELDS(type_tree);
|
||||
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
|
||||
elt->index = field;
|
||||
elt->value = fold_convert(TREE_TYPE(field), space);
|
||||
|
||||
tree length_tree = this->valtype_->array_type()->length()->get_tree(context);
|
||||
elt = init->quick_push(empty);
|
||||
field = DECL_CHAIN(field);
|
||||
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
|
||||
elt->index = field;
|
||||
elt->value = fold_convert(TREE_TYPE(field), length_tree);
|
||||
|
||||
elt = init->quick_push(empty);
|
||||
field = DECL_CHAIN(field);
|
||||
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),"__capacity") == 0);
|
||||
elt->index = field;
|
||||
elt->value = fold_convert(TREE_TYPE(field), length_tree);
|
||||
|
||||
tree constructor = build_constructor(type_tree, init);
|
||||
if (constructor == error_mark_node)
|
||||
return error_mark_node;
|
||||
if (!copy_to_heap)
|
||||
TREE_CONSTANT(constructor) = 1;
|
||||
|
||||
if (set == NULL_TREE)
|
||||
return constructor;
|
||||
else
|
||||
return build2(COMPOUND_EXPR, type_tree, set, constructor);
|
||||
Expression* len = this->valtype_->array_type()->length();
|
||||
Expression* slice_val =
|
||||
Expression::make_slice_value(this->type(), space, len, len, loc);
|
||||
return slice_val->get_tree(context);
|
||||
}
|
||||
|
||||
// Make a slice composite literal. This is used by the type
|
||||
|
@ -14802,6 +14752,10 @@ class Struct_field_offset_expression : public Expression
|
|||
{ }
|
||||
|
||||
protected:
|
||||
bool
|
||||
do_is_immutable() const
|
||||
{ return true; }
|
||||
|
||||
Type*
|
||||
do_type()
|
||||
{ return Type::lookup_integer_type("uintptr"); }
|
||||
|
|
|
@ -1305,7 +1305,7 @@ class Unary_expression : public Expression
|
|||
Unary_expression(Operator op, Expression* expr, Location location)
|
||||
: Expression(EXPRESSION_UNARY, location),
|
||||
op_(op), escapes_(true), create_temp_(false), is_gc_root_(false),
|
||||
expr_(expr), issue_nil_check_(false)
|
||||
is_slice_init_(false), expr_(expr), issue_nil_check_(false)
|
||||
{ }
|
||||
|
||||
// Return the operator.
|
||||
|
@ -1344,6 +1344,15 @@ class Unary_expression : public Expression
|
|||
this->is_gc_root_ = true;
|
||||
}
|
||||
|
||||
// Record that this is an address expression of a slice value initializer,
|
||||
// which is mutable if the values are not copied to the heap.
|
||||
void
|
||||
set_is_slice_init()
|
||||
{
|
||||
go_assert(this->op_ == OPERATOR_AND);
|
||||
this->is_slice_init_ = true;
|
||||
}
|
||||
|
||||
// Apply unary opcode OP to UNC, setting NC. Return true if this
|
||||
// could be done, false if not. Issue errors for overflow.
|
||||
static bool
|
||||
|
@ -1427,6 +1436,11 @@ class Unary_expression : public Expression
|
|||
// special struct composite literal that is mutable when addressed, meaning
|
||||
// it cannot be represented as an immutable_struct in the backend.
|
||||
bool is_gc_root_;
|
||||
// True if this is an address expression for a slice value with an immutable
|
||||
// initializer. The initializer for a slice's value pointer has an array
|
||||
// type, meaning it cannot be represented as an immutable_struct in the
|
||||
// backend.
|
||||
bool is_slice_init_;
|
||||
// The operand.
|
||||
Expression* expr_;
|
||||
// Whether or not to issue a nil check for this expression if its address
|
||||
|
|
Loading…
Add table
Reference in a new issue