aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/go/ChangeLog11
-rw-r--r--gcc/go/Make-lang.in3
-rw-r--r--gcc/go/go-backend.c2
-rw-r--r--gcc/go/go-gcc.cc18
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/backend.h5
-rw-r--r--gcc/go/gofrontend/export.cc12
-rw-r--r--gcc/go/gofrontend/export.h6
-rw-r--r--gcc/go/gofrontend/expressions.cc395
-rw-r--r--gcc/go/gofrontend/expressions.h40
-rw-r--r--gcc/go/gofrontend/go.cc3
-rw-r--r--gcc/go/gofrontend/gogo.cc184
-rw-r--r--gcc/go/gofrontend/gogo.h39
-rw-r--r--gcc/go/gofrontend/import-archive.cc375
-rw-r--r--gcc/go/gofrontend/runtime.def13
-rw-r--r--gcc/go/gofrontend/statements.cc4
-rw-r--r--gcc/go/gofrontend/types.cc1264
-rw-r--r--gcc/go/gofrontend/types.h136
-rw-r--r--gcc/go/gofrontend/wb.cc465
-rw-r--r--gcc/testsuite/go.test/test/slice3.go20
20 files changed, 2331 insertions, 666 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog
index 55d78ef..6b37acd1 100644
--- a/gcc/go/ChangeLog
+++ b/gcc/go/ChangeLog
@@ -1,3 +1,14 @@
+2017-05-10 Than McIntosh <thanm@google.com>
+
+ * go-backend.c: Include "go-c.h".
+ * go-gcc.cc (Gcc_backend::write_export_data): New method.
+
+2017-05-10 Ian Lance Taylor <iant@google.com>
+
+ * go-gcc.cc (Gcc_backend::Gcc_backend): Declare
+ __builtin_prefetch.
+ * Make-lang.in (GO_OBJS): Add go/wb.o.
+
2017-03-28 Than McIntosh <thanm@google.com>
PR go/80226
diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in
index ba88376..b65d347 100644
--- a/gcc/go/Make-lang.in
+++ b/gcc/go/Make-lang.in
@@ -72,7 +72,8 @@ GO_OBJS = \
go/runtime.o \
go/statements.o \
go/types.o \
- go/unsafe.o
+ go/unsafe.o \
+ go/wb.o
go_OBJS = $(GO_OBJS) go/gospec.o
diff --git a/gcc/go/go-backend.c b/gcc/go/go-backend.c
index d60a79e..2f8d2f4 100644
--- a/gcc/go/go-backend.c
+++ b/gcc/go/go-backend.c
@@ -30,7 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h"
#include "output.h" /* for assemble_string */
#include "common/common-target.h"
-
+#include "go-c.h"
/* The segment name we pass to simple_object_start_read to find Go
export data. */
diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc
index 62baa91..7c6147a 100644
--- a/gcc/go/go-gcc.cc
+++ b/gcc/go/go-gcc.cc
@@ -506,6 +506,10 @@ class Gcc_backend : public Backend
const std::vector<Bfunction*>&,
const std::vector<Bvariable*>&);
+ void
+ write_export_data(const char* bytes, unsigned int size);
+
+
private:
// Make a Bexpression from a tree.
Bexpression*
@@ -748,6 +752,13 @@ Gcc_backend::Gcc_backend()
this->define_builtin(BUILT_IN_TRAP, "__builtin_trap", NULL,
build_function_type(void_type_node, void_list_node),
false, true);
+
+ // The runtime uses __builtin_prefetch.
+ this->define_builtin(BUILT_IN_PREFETCH, "__builtin_prefetch", NULL,
+ build_varargs_function_type_list(void_type_node,
+ const_ptr_type_node,
+ NULL_TREE),
+ false, false);
}
// Get an unnamed integer type.
@@ -3212,6 +3223,13 @@ Gcc_backend::write_global_definitions(
delete[] defs;
}
+void
+Gcc_backend::write_export_data(const char* bytes, unsigned int size)
+{
+ go_write_export_data(bytes, size);
+}
+
+
// Define a builtin function. BCODE is the builtin function code
// defined by builtins.def. NAME is the name of the builtin function.
// LIBNAME is the name of the corresponding library function, and is
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 181b018..1082abd 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-a4f445e18fb06a032a4399859f432e03245f1a7d
+822ab419bf7d1c705cdce1c12133e7a11f56be2e
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/backend.h b/gcc/go/gofrontend/backend.h
index 93835d9..e51efe4ef 100644
--- a/gcc/go/gofrontend/backend.h
+++ b/gcc/go/gofrontend/backend.h
@@ -750,6 +750,11 @@ class Backend
const std::vector<Bexpression*>& constant_decls,
const std::vector<Bfunction*>& function_decls,
const std::vector<Bvariable*>& variable_decls) = 0;
+
+ // Write SIZE bytes of export data from BYTES to the proper
+ // section in the output object file.
+ virtual void
+ write_export_data(const char* bytes, unsigned int size) = 0;
};
#endif // !defined(GO_BACKEND_H)
diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc
index 6e08599..27b7680 100644
--- a/gcc/go/gofrontend/export.cc
+++ b/gcc/go/gofrontend/export.cc
@@ -14,6 +14,9 @@
#include "statements.h"
#include "export.h"
+#include "go-linemap.h"
+#include "backend.h"
+
// This file handles exporting global declarations.
// Class Export.
@@ -359,6 +362,10 @@ Export::write_imported_init_fns(const std::string& package_name,
++p)
{
const Import_init* ii = *p;
+
+ if (ii->init_name() == import_init_fn)
+ continue;
+
this->write_c_string(" ");
this->write_string(ii->package_name());
this->write_c_string(" ");
@@ -727,7 +734,8 @@ Export::Stream::write_checksum(const std::string& s)
// Class Stream_to_section.
-Stream_to_section::Stream_to_section()
+Stream_to_section::Stream_to_section(Backend* backend)
+ : backend_(backend)
{
}
@@ -736,5 +744,5 @@ Stream_to_section::Stream_to_section()
void
Stream_to_section::do_write(const char* bytes, size_t length)
{
- go_write_export_data (bytes, length);
+ this->backend_->write_export_data (bytes, length);
}
diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h
index fec73fb..1365677 100644
--- a/gcc/go/gofrontend/export.h
+++ b/gcc/go/gofrontend/export.h
@@ -16,6 +16,7 @@ class Bindings;
class Type;
class Package;
class Import_init_set;
+class Backend;
// Codes used for the builtin types. These are all negative to make
// them easily distinct from the codes assigned by Export::write_type.
@@ -236,11 +237,14 @@ class Export : public String_dump
class Stream_to_section : public Export::Stream
{
public:
- Stream_to_section();
+ Stream_to_section(Backend*);
protected:
void
do_write(const char*, size_t);
+
+ private:
+ Backend* backend_;
};
#endif // !defined(GO_EXPORT_H)
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index fee3203..ecafe16 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -900,8 +900,8 @@ Temporary_reference_expression::do_get_backend(Translate_context* context)
// the circularity down one level.
Type* stype = this->statement_->type();
if (!this->is_lvalue_
- && stype->has_pointer()
- && stype->deref()->is_void_type())
+ && stype->points_to() != NULL
+ && stype->points_to()->is_void_type())
{
Btype* btype = this->type()->base()->get_backend(gogo);
ret = gogo->backend()->convert_expression(btype, ret, this->location());
@@ -3311,7 +3311,18 @@ Type_conversion_expression::do_is_static_initializer() const
if (Type::are_identical(type, expr_type, false, NULL))
return true;
- return type->is_basic_type() && expr_type->is_basic_type();
+ if (type->is_string_type() && expr_type->is_string_type())
+ return true;
+
+ if ((type->is_numeric_type()
+ || type->is_boolean_type()
+ || type->points_to() != NULL)
+ && (expr_type->is_numeric_type()
+ || expr_type->is_boolean_type()
+ || expr_type->points_to() != NULL))
+ return true;
+
+ return false;
}
// Return the constant numeric value if there is one.
@@ -3570,7 +3581,18 @@ Unsafe_type_conversion_expression::do_is_static_initializer() const
if (Type::are_convertible(type, expr_type, NULL))
return true;
- return type->is_basic_type() && expr_type->is_basic_type();
+ if (type->is_string_type() && expr_type->is_string_type())
+ return true;
+
+ if ((type->is_numeric_type()
+ || type->is_boolean_type()
+ || type->points_to() != NULL)
+ && (expr_type->is_numeric_type()
+ || expr_type->is_boolean_type()
+ || expr_type->points_to() != NULL))
+ return true;
+
+ return false;
}
// Convert to backend representation.
@@ -3669,6 +3691,48 @@ Expression::make_unsafe_cast(Type* type, Expression* expr,
// Class Unary_expression.
+// Call the address_taken method of the operand if needed. This is
+// called after escape analysis but before inserting write barriers.
+
+void
+Unary_expression::check_operand_address_taken(Gogo* gogo)
+{
+ if (this->op_ != OPERATOR_AND)
+ return;
+
+ // If this->escapes_ is false at this point, then it was set to
+ // false by an explicit call to set_does_not_escape, and the value
+ // does not escape. If this->escapes_ is true, we may be able to
+ // set it to false if taking the address of a variable that does not
+ // escape.
+ Node* n = Node::make_node(this);
+ if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
+ this->escapes_ = false;
+
+ // When compiling the runtime, the address operator does not cause
+ // local variables to escape. When escape analysis becomes the
+ // default, this should be changed to make it an error if we have an
+ // address operator that escapes.
+ if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
+ this->escapes_ = false;
+
+ Named_object* var = NULL;
+ if (this->expr_->var_expression() != NULL)
+ var = this->expr_->var_expression()->named_object();
+ else if (this->expr_->enclosed_var_expression() != NULL)
+ var = this->expr_->enclosed_var_expression()->variable();
+
+ if (this->escapes_ && var != NULL)
+ {
+ if (var->is_variable())
+ this->escapes_ = var->var_value()->escapes();
+ if (var->is_result_variable())
+ this->escapes_ = var->result_var_value()->escapes();
+ }
+
+ this->expr_->address_taken(this->escapes_);
+}
+
// If we are taking the address of a composite literal, and the
// contents are not constant, then we want to make a heap expression
// instead.
@@ -3795,40 +3859,6 @@ Unary_expression::do_flatten(Gogo* gogo, Named_object*,
}
}
- if (this->op_ == OPERATOR_AND)
- {
- // If this->escapes_ is false at this point, then it was set to
- // false by an explicit call to set_does_not_escape, and the
- // value does not escape. If this->escapes_ is true, we may be
- // able to set it to false if taking the address of a variable
- // that does not escape.
- Node* n = Node::make_node(this);
- if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
- this->escapes_ = false;
-
- // When compiling the runtime, the address operator does not
- // cause local variables to escape. When escape analysis
- // becomes the default, this should be changed to make it an
- // error if we have an address operator that escapes.
- if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
- this->escapes_ = false;
-
- Named_object* var = NULL;
- if (this->expr_->var_expression() != NULL)
- var = this->expr_->var_expression()->named_object();
- else if (this->expr_->enclosed_var_expression() != NULL)
- var = this->expr_->enclosed_var_expression()->variable();
-
- if (this->escapes_ && var != NULL)
- {
- if (var->is_variable())
- this->escapes_ = var->var_value()->escapes();
- if (var->is_result_variable())
- this->escapes_ = var->result_var_value()->escapes();
- }
- this->expr_->address_taken(this->escapes_);
- }
-
if (this->create_temp_ && !this->expr_->is_variable())
{
Temporary_statement* temp =
@@ -7759,7 +7789,16 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
lhs = Expression::make_index(ref, ref2, NULL, NULL, loc);
gogo->lower_expression(function, inserter, &lhs);
gogo->flatten_expression(function, inserter, &lhs);
- assign = Statement::make_assignment(lhs, *pa, loc);
+ // The flatten pass runs after the write barrier pass, so we
+ // need to insert a write barrier here if necessary.
+ if (!gogo->assign_needs_write_barrier(lhs))
+ assign = Statement::make_assignment(lhs, *pa, loc);
+ else
+ {
+ Function* f = function == NULL ? NULL : function->func_value();
+ assign = gogo->assign_with_write_barrier(f, NULL, inserter,
+ lhs, *pa, loc);
+ }
inserter->insert(assign);
}
}
@@ -9539,6 +9578,12 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
Type* varargs_type, size_t param_count,
Slice_storage_escape_disp escape_disp)
{
+ // When compiling the runtime, varargs slices do not escape. When
+ // escape analysis becomes the default, this should be changed to
+ // make it an error if we have a varargs slice that escapes.
+ if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
+ escape_disp = SLICE_STORAGE_DOES_NOT_ESCAPE;
+
if (this->varargs_are_lowered_)
return;
@@ -10250,16 +10295,13 @@ Call_expression::do_get_backend(Translate_context* context)
if (this->results_ != NULL)
{
- go_assert(this->call_temp_ != NULL);
- Expression* call_ref =
- Expression::make_temporary_reference(this->call_temp_, location);
- Bexpression* bcall_ref = call_ref->get_backend(context);
+ Bexpression* bcall_ref = this->call_result_ref(context);
Bfunction* bfunction = context->function()->func_value()->get_decl();
Bstatement* assn_stmt =
gogo->backend()->assignment_statement(bfunction,
bcall_ref, call, location);
- this->call_ = this->set_results(context, bcall_ref);
+ this->call_ = this->set_results(context);
Bexpression* set_and_call =
gogo->backend()->compound_expression(assn_stmt, this->call_,
@@ -10271,16 +10313,32 @@ Call_expression::do_get_backend(Translate_context* context)
return this->call_;
}
+// Return the backend representation of a reference to the struct used
+// to capture the result of a multiple-output call.
+
+Bexpression*
+Call_expression::call_result_ref(Translate_context* context)
+{
+ go_assert(this->call_temp_ != NULL);
+ Location location = this->location();
+ Expression* call_ref =
+ Expression::make_temporary_reference(this->call_temp_, location);
+ Bexpression* bcall_ref = call_ref->get_backend(context);
+ return bcall_ref;
+}
+
// Set the result variables if this call returns multiple results.
Bexpression*
-Call_expression::set_results(Translate_context* context, Bexpression* call)
+Call_expression::set_results(Translate_context* context)
{
Gogo* gogo = context->gogo();
Bexpression* results = NULL;
Location loc = this->location();
+ go_assert(this->call_temp_ != NULL);
+
size_t rc = this->result_count();
for (size_t i = 0; i < rc; ++i)
{
@@ -10296,12 +10354,15 @@ Call_expression::set_results(Translate_context* context, Bexpression* call)
Bfunction* bfunction = context->function()->func_value()->get_decl();
Bexpression* result_ref = ref->get_backend(context);
+ Bexpression* bcall_ref = this->call_result_ref(context);
Bexpression* call_result =
- gogo->backend()->struct_field_expression(call, i, loc);
+ gogo->backend()->struct_field_expression(bcall_ref, i, loc);
Bstatement* assn_stmt =
gogo->backend()->assignment_statement(bfunction,
result_ref, call_result, loc);
+ bcall_ref = this->call_result_ref(context);
+ call_result = gogo->backend()->struct_field_expression(bcall_ref, i, loc);
Bexpression* result =
gogo->backend()->compound_expression(assn_stmt, call_result, loc);
@@ -10860,7 +10921,7 @@ Array_index_expression::do_flatten(Gogo*, Named_object*,
inserter->insert(temp);
this->end_ = Expression::make_temporary_reference(temp, loc);
}
- if (cap!= NULL && !cap->is_variable())
+ if (cap != NULL && !cap->is_variable())
{
temp = Statement::make_temporary(NULL, cap, loc);
inserter->insert(temp);
@@ -11047,16 +11108,28 @@ Array_index_expression::do_get_backend(Translate_context* context)
bad_index, loc);
}
- Expression* valptr = array_type->get_value_pointer(gogo, this->array_);
- Bexpression* val = valptr->get_backend(context);
- val = gogo->backend()->pointer_offset_expression(val, start, loc);
-
Bexpression* result_length =
gogo->backend()->binary_expression(OPERATOR_MINUS, end, start, loc);
Bexpression* result_capacity =
gogo->backend()->binary_expression(OPERATOR_MINUS, cap_arg, start, loc);
+ // If the new capacity is zero, don't change val. Otherwise we can
+ // get a pointer to the next object in memory, keeping it live
+ // unnecessarily. When the capacity is zero, the actual pointer
+ // value doesn't matter.
+ Bexpression* zero =
+ Expression::make_integer_ul(0, int_type, loc)->get_backend(context);
+ Bexpression* cond =
+ gogo->backend()->binary_expression(OPERATOR_EQEQ, result_capacity, zero,
+ loc);
+ Bexpression* offset = gogo->backend()->conditional_expression(bfn, int_btype,
+ cond, zero,
+ start, loc);
+ Expression* valptr = array_type->get_value_pointer(gogo, this->array_);
+ Bexpression* val = valptr->get_backend(context);
+ val = gogo->backend()->pointer_offset_expression(val, offset, loc);
+
Btype* struct_btype = this->type()->get_backend(gogo);
std::vector<Bexpression*> init;
init.push_back(val);
@@ -13065,10 +13138,12 @@ Slice_construction_expression::do_flatten(Gogo* gogo, Named_object* no,
this->Array_construction_expression::do_flatten(gogo, no, inserter);
// Create a stack-allocated storage temp if storage won't escape
- if (!this->storage_escapes_ && this->slice_storage_ == NULL)
+ if (!this->storage_escapes_
+ && this->slice_storage_ == NULL
+ && this->element_count() > 0)
{
Location loc = this->location();
- this->array_val_ = create_array_val();
+ this->array_val_ = this->create_array_val();
go_assert(this->array_val_);
Temporary_statement* temp =
Statement::make_temporary(this->valtype_, this->array_val_, loc);
@@ -13098,7 +13173,7 @@ Bexpression*
Slice_construction_expression::do_get_backend(Translate_context* context)
{
if (this->array_val_ == NULL)
- this->array_val_ = create_array_val();
+ this->array_val_ = this->create_array_val();
if (this->array_val_ == NULL)
{
go_assert(this->type()->is_error());
@@ -14216,14 +14291,15 @@ Heap_expression::do_type()
Bexpression*
Heap_expression::do_get_backend(Translate_context* context)
{
- if (this->expr_->is_error_expression() || this->expr_->type()->is_error())
+ Type* etype = this->expr_->type();
+ if (this->expr_->is_error_expression() || etype->is_error())
return context->backend()->error_expression();
Location loc = this->location();
Gogo* gogo = context->gogo();
Btype* btype = this->type()->get_backend(gogo);
- Expression* alloc = Expression::make_allocation(this->expr_->type(), loc);
+ Expression* alloc = Expression::make_allocation(etype, loc);
Node* n = Node::make_node(this);
if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
alloc->allocation_expression()->set_allocate_on_stack();
@@ -14236,14 +14312,43 @@ Heap_expression::do_get_backend(Translate_context* context)
Bvariable* space_temp =
gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
space, true, loc, &decl);
- space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc);
- Btype* expr_btype = this->expr_->type()->get_backend(gogo);
- Bexpression* ref =
- gogo->backend()->indirect_expression(expr_btype, space, true, loc);
+ Btype* expr_btype = etype->get_backend(gogo);
Bexpression* bexpr = this->expr_->get_backend(context);
- Bstatement* assn = gogo->backend()->assignment_statement(fndecl, ref,
- bexpr, loc);
+
+ // If this assignment needs a write barrier, call typedmemmove. We
+ // don't do this in the write barrier pass because in some cases
+ // backend conversion can introduce new Heap_expression values.
+ Bstatement* assn;
+ if (!etype->has_pointer())
+ {
+ space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc);
+ Bexpression* ref =
+ gogo->backend()->indirect_expression(expr_btype, space, true, loc);
+ assn = gogo->backend()->assignment_statement(fndecl, ref, bexpr, loc);
+ }
+ else
+ {
+ Bstatement* edecl;
+ Bvariable* btemp =
+ gogo->backend()->temporary_variable(fndecl, context->bblock(),
+ expr_btype, bexpr, true, loc,
+ &edecl);
+ Bexpression* btempref = gogo->backend()->var_expression(btemp,
+ VE_lvalue, loc);
+ Bexpression* addr = gogo->backend()->address_expression(btempref, loc);
+
+ Expression* td = Expression::make_type_descriptor(etype, loc);
+ Type* etype_ptr = Type::make_pointer_type(etype);
+ space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc);
+ Expression* elhs = Expression::make_backend(space, etype_ptr, loc);
+ Expression* erhs = Expression::make_backend(addr, etype_ptr, loc);
+ Expression* call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,
+ td, elhs, erhs);
+ Bexpression* bcall = call->get_backend(context);
+ Bstatement* s = gogo->backend()->expression_statement(fndecl, bcall);
+ assn = gogo->backend()->compound_statement(edecl, s);
+ }
decl = gogo->backend()->compound_statement(decl, assn);
space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc);
return gogo->backend()->compound_expression(decl, space, loc);
@@ -14468,7 +14573,7 @@ class GC_symbol_expression : public Expression
protected:
Type*
do_type()
- { return Type::lookup_integer_type("uintptr"); }
+ { return Type::make_pointer_type(Type::lookup_integer_type("uint8")); }
bool
do_is_static_initializer() const
@@ -14513,6 +14618,91 @@ Expression::make_gc_symbol(Type* type)
return new GC_symbol_expression(type);
}
+// An expression that evaluates to a pointer to a symbol holding the
+// ptrmask data of a type.
+
+class Ptrmask_symbol_expression : public Expression
+{
+ public:
+ Ptrmask_symbol_expression(Type* type)
+ : Expression(EXPRESSION_PTRMASK_SYMBOL, Linemap::predeclared_location()),
+ type_(type)
+ {}
+
+ protected:
+ Type*
+ do_type()
+ { return Type::make_pointer_type(Type::lookup_integer_type("uint8")); }
+
+ bool
+ do_is_static_initializer() const
+ { return true; }
+
+ void
+ do_determine_type(const Type_context*)
+ { }
+
+ Expression*
+ do_copy()
+ { return this; }
+
+ Bexpression*
+ do_get_backend(Translate_context*);
+
+ void
+ do_dump_expression(Ast_dump_context*) const;
+
+ private:
+ // The type that this ptrmask symbol describes.
+ Type* type_;
+};
+
+// Return the ptrmask variable.
+
+Bexpression*
+Ptrmask_symbol_expression::do_get_backend(Translate_context* context)
+{
+ Gogo* gogo = context->gogo();
+
+ // If this type does not need a gcprog, then we can use the standard
+ // GC symbol.
+ int64_t ptrsize, ptrdata;
+ if (!this->type_->needs_gcprog(gogo, &ptrsize, &ptrdata))
+ return this->type_->gc_symbol_pointer(gogo);
+
+ // Otherwise we have to build a ptrmask variable, and return a
+ // pointer to it.
+
+ Bvariable* bvar = this->type_->gc_ptrmask_var(gogo, ptrsize, ptrdata);
+ Location bloc = Linemap::predeclared_location();
+ Bexpression* bref = gogo->backend()->var_expression(bvar, VE_rvalue, bloc);
+ Bexpression* baddr = gogo->backend()->address_expression(bref, bloc);
+
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* pointer_uint8_type = Type::make_pointer_type(uint8_type);
+ Btype* ubtype = pointer_uint8_type->get_backend(gogo);
+ return gogo->backend()->convert_expression(ubtype, baddr, bloc);
+}
+
+// Dump AST for a ptrmask symbol expression.
+
+void
+Ptrmask_symbol_expression::do_dump_expression(
+ Ast_dump_context* ast_dump_context) const
+{
+ ast_dump_context->ostream() << "ptrmask(";
+ ast_dump_context->dump_type(this->type_);
+ ast_dump_context->ostream() << ")";
+}
+
+// Make a ptrmask symbol expression.
+
+Expression*
+Expression::make_ptrmask_symbol(Type* type)
+{
+ return new Ptrmask_symbol_expression(type);
+}
+
// An expression which evaluates to some characteristic of a type.
// This is only used to initialize fields of a type descriptor. Using
// a new expression class is slightly inefficient but gives us a good
@@ -14565,6 +14755,8 @@ Type_info_expression::do_type()
switch (this->type_info_)
{
case TYPE_INFO_SIZE:
+ case TYPE_INFO_BACKEND_PTRDATA:
+ case TYPE_INFO_DESCRIPTOR_PTRDATA:
return Type::lookup_integer_type("uintptr");
case TYPE_INFO_ALIGNMENT:
case TYPE_INFO_FIELD_ALIGNMENT:
@@ -14593,6 +14785,12 @@ Type_info_expression::do_get_backend(Translate_context* context)
case TYPE_INFO_FIELD_ALIGNMENT:
ok = this->type_->backend_type_field_align(gogo, &val);
break;
+ case TYPE_INFO_BACKEND_PTRDATA:
+ ok = this->type_->backend_type_ptrdata(gogo, &val);
+ break;
+ case TYPE_INFO_DESCRIPTOR_PTRDATA:
+ ok = this->type_->descriptor_ptrdata(gogo, &val);
+ break;
default:
go_unreachable();
}
@@ -14618,7 +14816,9 @@ Type_info_expression::do_dump_expression(
ast_dump_context->ostream() <<
(this->type_info_ == TYPE_INFO_ALIGNMENT ? "alignment"
: this->type_info_ == TYPE_INFO_FIELD_ALIGNMENT ? "field alignment"
- : this->type_info_ == TYPE_INFO_SIZE ? "size "
+ : this->type_info_ == TYPE_INFO_SIZE ? "size"
+ : this->type_info_ == TYPE_INFO_BACKEND_PTRDATA ? "backend_ptrdata"
+ : this->type_info_ == TYPE_INFO_DESCRIPTOR_PTRDATA ? "descriptor_ptrdata"
: "unknown");
ast_dump_context->ostream() << ")";
}
@@ -15924,6 +16124,73 @@ Numeric_constant::mpfr_to_unsigned_long(const mpfr_t fval,
return ret;
}
+// Express value as memory size if possible.
+
+bool
+Numeric_constant::to_memory_size(int64_t* val) const
+{
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ return this->mpz_to_memory_size(this->u_.int_val, val);
+ case NC_FLOAT:
+ return this->mpfr_to_memory_size(this->u_.float_val, val);
+ case NC_COMPLEX:
+ if (!mpfr_zero_p(mpc_imagref(this->u_.complex_val)))
+ return false;
+ return this->mpfr_to_memory_size(mpc_realref(this->u_.complex_val), val);
+ default:
+ go_unreachable();
+ }
+}
+
+// Express integer as memory size if possible.
+
+bool
+Numeric_constant::mpz_to_memory_size(const mpz_t ival, int64_t* val) const
+{
+ if (mpz_sgn(ival) < 0)
+ return false;
+ if (mpz_fits_slong_p(ival))
+ {
+ *val = static_cast<int64_t>(mpz_get_si(ival));
+ return true;
+ }
+
+ // Test >= 64, not > 64, because an int64_t can hold 63 bits of a
+ // positive value.
+ if (mpz_sizeinbase(ival, 2) >= 64)
+ return false;
+
+ mpz_t q, r;
+ mpz_init(q);
+ mpz_init(r);
+ mpz_tdiv_q_2exp(q, ival, 32);
+ mpz_tdiv_r_2exp(r, ival, 32);
+ go_assert(mpz_fits_ulong_p(q) && mpz_fits_ulong_p(r));
+ *val = ((static_cast<int64_t>(mpz_get_ui(q)) << 32)
+ + static_cast<int64_t>(mpz_get_ui(r)));
+ mpz_clear(r);
+ mpz_clear(q);
+ return true;
+}
+
+// Express floating point value as memory size if possible.
+
+bool
+Numeric_constant::mpfr_to_memory_size(const mpfr_t fval, int64_t* val) const
+{
+ if (!mpfr_integer_p(fval))
+ return false;
+ mpz_t ival;
+ mpz_init(ival);
+ mpfr_get_z(ival, fval, GMP_RNDN);
+ bool ret = this->mpz_to_memory_size(ival, val);
+ mpz_clear(ival);
+ return ret;
+}
+
// Convert value to integer if possible.
bool
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index adf9eab..03bb085 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -128,6 +128,7 @@ class Expression
EXPRESSION_RECEIVE,
EXPRESSION_TYPE_DESCRIPTOR,
EXPRESSION_GC_SYMBOL,
+ EXPRESSION_PTRMASK_SYMBOL,
EXPRESSION_TYPE_INFO,
EXPRESSION_SLICE_INFO,
EXPRESSION_SLICE_VALUE,
@@ -402,6 +403,13 @@ class Expression
static Expression*
make_gc_symbol(Type* type);
+ // Make an expression that evaluates to the address of a ptrmask
+ // symbol for TYPE. For most types this will be the same as
+ // make_gc_symbol, but for larger types make_gc_symbol will return a
+ // gcprog while this will return a ptrmask.
+ static Expression*
+ make_ptrmask_symbol(Type* type);
+
// Make an expression which evaluates to some characteristic of a
// type. These are only used for type descriptors, so there is no
// location parameter.
@@ -413,7 +421,15 @@ class Expression
TYPE_INFO_ALIGNMENT,
// The required alignment of a value of the type when used as a
// field in a struct.
- TYPE_INFO_FIELD_ALIGNMENT
+ TYPE_INFO_FIELD_ALIGNMENT,
+ // The size of the prefix of a value of the type that contains
+ // all the pointers. This is 0 for a type that contains no
+ // pointers. It is always <= TYPE_INFO_SIZE.
+ TYPE_INFO_BACKEND_PTRDATA,
+ // Like TYPE_INFO_BACKEND_PTRDATA, but the ptrdata value that we
+ // want to store in a type descriptor. They are the same for
+ // most types, but can differ for a type that uses a gcprog.
+ TYPE_INFO_DESCRIPTOR_PTRDATA
};
static Expression*
@@ -1774,6 +1790,10 @@ class Unary_expression : public Expression
this->is_slice_init_ = true;
}
+ // Call the address_taken method on the operand if necessary.
+ void
+ check_operand_address_taken(Gogo*);
+
// Apply unary opcode OP to UNC, setting NC. Return true if this
// could be done, false if not. On overflow, issues an error and
// sets *ISSUED_ERROR.
@@ -2270,7 +2290,10 @@ class Call_expression : public Expression
Expression**);
Bexpression*
- set_results(Translate_context*, Bexpression*);
+ set_results(Translate_context*);
+
+ Bexpression*
+ call_result_ref(Translate_context* context);
// The function to call.
Expression* fn_;
@@ -4011,6 +4034,13 @@ class Numeric_constant
To_unsigned_long
to_unsigned_long(unsigned long* val) const;
+ // If the value can be expressed as an integer that describes the
+ // size of an object in memory, set *VAL and return true.
+ // Otherwise, return false. Currently we use int64_t to represent a
+ // memory size, as in Type::backend_type_size.
+ bool
+ to_memory_size(int64_t* val) const;
+
// If the value can be expressed as an int, return true and
// initialize and set VAL. This will return false for a value with
// an explicit float or complex type, even if the value is integral.
@@ -4053,6 +4083,12 @@ class Numeric_constant
mpfr_to_unsigned_long(const mpfr_t fval, unsigned long *val) const;
bool
+ mpz_to_memory_size(const mpz_t ival, int64_t* val) const;
+
+ bool
+ mpfr_to_memory_size(const mpfr_t fval, int64_t* val) const;
+
+ bool
check_int_type(Integer_type*, bool, Location);
bool
diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc
index 7050acc..e3f17bc 100644
--- a/gcc/go/gofrontend/go.cc
+++ b/gcc/go/gofrontend/go.cc
@@ -158,6 +158,9 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
// Write out queued up functions for hash and comparison of types.
::gogo->write_specific_type_functions();
+ // Add write barriers.
+ ::gogo->add_write_barriers();
+
// Flatten the parse tree.
::gogo->flatten();
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index a190917..12135d7 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -720,15 +720,18 @@ Gogo::init_imports(std::vector<Bstatement*>& init_stmts, Bfunction *bfunction)
// roots during the mark phase. We build a struct that is easy to
// hook into a list of roots.
-// struct __go_gc_root_list
-// {
-// struct __go_gc_root_list* __next;
-// struct __go_gc_root
-// {
-// void* __decl;
-// size_t __size;
-// } __roots[];
-// };
+// type gcRoot struct {
+// decl unsafe.Pointer // Pointer to variable.
+// size uintptr // Total size of variable.
+// ptrdata uintptr // Length of variable's gcdata.
+// gcdata *byte // Pointer mask.
+// }
+//
+// type gcRootList struct {
+// next *gcRootList
+// count int
+// roots [...]gcRoot
+// }
// The last entry in the roots array has a NULL decl field.
@@ -737,28 +740,35 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
std::vector<Bstatement*>& init_stmts,
Bfunction* init_bfn)
{
- if (var_gc.empty())
+ if (var_gc.empty() && this->gc_roots_.empty())
return;
Type* pvt = Type::make_pointer_type(Type::make_void_type());
- Type* uint_type = Type::lookup_integer_type("uint");
- Struct_type* root_type = Type::make_builtin_struct_type(2,
- "__decl", pvt,
- "__size", uint_type);
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ Type* byte_type = this->lookup_global("byte")->type_value();
+ Type* pointer_byte_type = Type::make_pointer_type(byte_type);
+ Struct_type* root_type =
+ Type::make_builtin_struct_type(4,
+ "decl", pvt,
+ "size", uintptr_type,
+ "ptrdata", uintptr_type,
+ "gcdata", pointer_byte_type);
Location builtin_loc = Linemap::predeclared_location();
- unsigned roots_len = var_gc.size() + this->gc_roots_.size() + 1;
+ unsigned long roots_len = var_gc.size() + this->gc_roots_.size();
Expression* length = Expression::make_integer_ul(roots_len, NULL,
builtin_loc);
Array_type* root_array_type = Type::make_array_type(root_type, length);
root_array_type->set_is_array_incomparable();
- Type* ptdt = Type::make_type_descriptor_ptr_type();
+
+ Type* int_type = Type::lookup_integer_type("int");
Struct_type* root_list_type =
- Type::make_builtin_struct_type(2,
- "__next", ptdt,
- "__roots", root_array_type);
+ Type::make_builtin_struct_type(3,
+ "next", pvt,
+ "count", int_type,
+ "roots", root_array_type);
- // Build an initializer for the __roots array.
+ // Build an initializer for the roots array.
Expression_list* roots_init = new Expression_list();
@@ -772,11 +782,22 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
Expression* decl = Expression::make_var_reference(*p, no_loc);
Expression* decl_addr =
Expression::make_unary(OPERATOR_AND, decl, no_loc);
+ decl_addr->unary_expression()->set_does_not_escape();
+ decl_addr = Expression::make_cast(pvt, decl_addr, no_loc);
init->push_back(decl_addr);
- Expression* decl_size =
- Expression::make_type_info(decl->type(), Expression::TYPE_INFO_SIZE);
- init->push_back(decl_size);
+ Expression* size =
+ Expression::make_type_info(decl->type(),
+ Expression::TYPE_INFO_SIZE);
+ init->push_back(size);
+
+ Expression* ptrdata =
+ Expression::make_type_info(decl->type(),
+ Expression::TYPE_INFO_BACKEND_PTRDATA);
+ init->push_back(ptrdata);
+
+ Expression* gcdata = Expression::make_ptrmask_symbol(decl->type());
+ init->push_back(gcdata);
Expression* root_ctor =
Expression::make_struct_composite_literal(root_type, init, no_loc);
@@ -791,37 +812,35 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
Expression* expr = *p;
Location eloc = expr->location();
- init->push_back(expr);
+ init->push_back(Expression::make_cast(pvt, expr, eloc));
Type* type = expr->type()->points_to();
go_assert(type != NULL);
+
Expression* size =
- Expression::make_type_info(type, Expression::TYPE_INFO_SIZE);
+ Expression::make_type_info(type,
+ Expression::TYPE_INFO_SIZE);
init->push_back(size);
+ Expression* ptrdata =
+ Expression::make_type_info(type,
+ Expression::TYPE_INFO_BACKEND_PTRDATA);
+ init->push_back(ptrdata);
+
+ Expression* gcdata = Expression::make_ptrmask_symbol(type);
+ init->push_back(gcdata);
+
Expression* root_ctor =
Expression::make_struct_composite_literal(root_type, init, eloc);
roots_init->push_back(root_ctor);
}
- // The list ends with a NULL entry.
-
- Expression_list* null_init = new Expression_list();
- Expression* nil = Expression::make_nil(builtin_loc);
- null_init->push_back(nil);
-
- Expression *zero = Expression::make_integer_ul(0, NULL, builtin_loc);
- null_init->push_back(zero);
-
- Expression* null_root_ctor =
- Expression::make_struct_composite_literal(root_type, null_init,
- builtin_loc);
- roots_init->push_back(null_root_ctor);
-
// Build a constructor for the struct.
Expression_list* root_list_init = new Expression_list();
- root_list_init->push_back(nil);
+ root_list_init->push_back(Expression::make_nil(builtin_loc));
+ root_list_init->push_back(Expression::make_integer_ul(roots_len, int_type,
+ builtin_loc));
Expression* roots_ctor =
Expression::make_array_composite_literal(root_array_type, roots_init,
@@ -1216,24 +1235,31 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits)
}
// VAR_INITS is in the correct order. For each VAR in VAR_INITS,
- // check for a loop of VAR on itself. We only do this if
- // INIT is not NULL and there is no dependency; when INIT is
- // NULL, it means that PREINIT sets VAR, which we will
+ // check for a loop of VAR on itself.
// interpret as a loop.
for (Var_inits::const_iterator p = var_inits->begin();
p != var_inits->end();
++p)
- {
- Named_object* var = p->var();
- Expression* init = var->var_value()->init();
- Block* preinit = var->var_value()->preinit();
- Named_object* dep = gogo->var_depends_on(var->var_value());
- if (init != NULL && dep == NULL
- && expression_requires(init, preinit, NULL, var))
- go_error_at(var->location(),
- "initialization expression for %qs depends upon itself",
- var->message_name().c_str());
- }
+ gogo->check_self_dep(p->var());
+}
+
+// Give an error if the initialization expression for VAR depends on
+// itself. We only check if INIT is not NULL and there is no
+// dependency; when INIT is NULL, it means that PREINIT sets VAR,
+// which we will interpret as a loop.
+
+void
+Gogo::check_self_dep(Named_object* var)
+{
+ Expression* init = var->var_value()->init();
+ Block* preinit = var->var_value()->preinit();
+ Named_object* dep = this->var_depends_on(var->var_value());
+ if (init != NULL
+ && dep == NULL
+ && expression_requires(init, preinit, NULL, var))
+ go_error_at(var->location(),
+ "initialization expression for %qs depends upon itself",
+ var->message_name().c_str());
}
// Write out the global definitions.
@@ -1425,8 +1451,18 @@ Gogo::write_globals()
var_inits.push_back(Var_init(no, zero_stmt));
}
+ // Collect a list of all global variables with pointers,
+ // to register them for the garbage collector.
if (!is_sink && var->type()->has_pointer())
- var_gc.push_back(no);
+ {
+ // Avoid putting runtime.gcRoots itself on the list.
+ if (this->compiling_runtime()
+ && this->package_name() == "runtime"
+ && Gogo::unpack_hidden_name(no->name()) == "gcRoots")
+ ;
+ else
+ var_gc.push_back(no);
+ }
}
}
@@ -3584,14 +3620,14 @@ class Order_eval : public Traverse
// Implement the order of evaluation rules for a statement.
int
-Order_eval::statement(Block* block, size_t* pindex, Statement* s)
+Order_eval::statement(Block* block, size_t* pindex, Statement* stmt)
{
// FIXME: This approach doesn't work for switch statements, because
// we add the new statements before the whole switch when we need to
// instead add them just before the switch expression. The right
// fix is probably to lower switch statements with nonconstant cases
// to a series of conditionals.
- if (s->switch_statement() != NULL)
+ if (stmt->switch_statement() != NULL)
return TRAVERSE_CONTINUE;
Find_eval_ordering find_eval_ordering;
@@ -3599,11 +3635,11 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
// If S is a variable declaration, then ordinary traversal won't do
// anything. We want to explicitly traverse the initialization
// expression if there is one.
- Variable_declaration_statement* vds = s->variable_declaration_statement();
+ Variable_declaration_statement* vds = stmt->variable_declaration_statement();
Expression* init = NULL;
Expression* orig_init = NULL;
if (vds == NULL)
- s->traverse_contents(&find_eval_ordering);
+ stmt->traverse_contents(&find_eval_ordering);
else
{
init = vds->var()->var_value()->init();
@@ -3636,7 +3672,7 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
// usually leave it in place.
if (c == 1)
{
- switch (s->classification())
+ switch (stmt->classification())
{
case Statement::STATEMENT_ASSIGNMENT:
// For an assignment statement, we need to evaluate an
@@ -3653,7 +3689,7 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
// move. We need to move any subexpressions in case they
// are themselves call statements that require passing a
// closure.
- Expression* expr = s->expression_statement()->expr();
+ Expression* expr = stmt->expression_statement()->expr();
if (expr->call_expression() != NULL
&& expr->call_expression()->result_count() == 0)
break;
@@ -3666,7 +3702,8 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
}
}
- bool is_thunk = s->thunk_statement() != NULL;
+ bool is_thunk = stmt->thunk_statement() != NULL;
+ Expression_statement* es = stmt->expression_statement();
for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin();
p != find_eval_ordering.end();
++p)
@@ -3697,9 +3734,14 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
//
// Since a given call expression can be shared by multiple
// Call_result_expressions, avoid hoisting the call the
- // second time we see it here.
+ // second time we see it here. In addition, don't try to
+ // hoist the top-level multi-return call in the statement,
+ // since doing this would result a tree with more than one copy
+ // of the call.
if (this->remember_expression(*pexpr))
s = NULL;
+ else if (es != NULL && *pexpr == es->expr())
+ s = NULL;
else
s = Statement::make_statement(*pexpr, true);
}
@@ -4448,9 +4490,7 @@ Expression*
Gogo::allocate_memory(Type* type, Location location)
{
Expression* td = Expression::make_type_descriptor(type, location);
- Expression* size =
- Expression::make_type_info(type, Expression::TYPE_INFO_SIZE);
- return Runtime::make_call(Runtime::NEW, location, 2, td, size);
+ return Runtime::make_call(Runtime::NEW, location, 1, td);
}
// Traversal class used to check for return statements.
@@ -4502,7 +4542,7 @@ Gogo::do_exports()
{
// For now we always stream to a section. Later we may want to
// support streaming to a separate file.
- Stream_to_section stream;
+ Stream_to_section stream(this->backend());
// Write out either the prefix or pkgpath depending on how we were
// invoked.
@@ -6696,11 +6736,19 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
asm_name.append(n);
}
asm_name = go_encode_id(asm_name);
+
+ bool is_hidden = Gogo::is_hidden_name(name);
+ // Hack to export runtime.writeBarrier. FIXME.
+ // This is because go:linkname doesn't work on variables.
+ if (gogo->compiling_runtime()
+ && var_name == "runtime.writeBarrier")
+ is_hidden = false;
+
bvar = backend->global_variable(var_name,
asm_name,
btype,
package != NULL,
- Gogo::is_hidden_name(name),
+ is_hidden,
this->in_unique_section_,
this->location_);
}
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 7c29828..994f233 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -585,7 +585,10 @@ class Gogo
// variable initializers that would otherwise not be seen.
void
add_gc_root(Expression* expr)
- { this->gc_roots_.push_back(expr); }
+ {
+ this->set_need_init_fn();
+ this->gc_roots_.push_back(expr);
+ }
// Traverse the tree. See the Traverse class.
void
@@ -693,6 +696,23 @@ class Gogo
void
order_evaluations();
+ // Add write barriers as needed.
+ void
+ add_write_barriers();
+
+ // Return whether an assignment that sets LHS to RHS needs a write
+ // barrier.
+ bool
+ assign_needs_write_barrier(Expression* lhs);
+
+ // Return an assignment that sets LHS to RHS using a write barrier.
+ // This returns an if statement that checks whether write barriers
+ // are enabled. If not, it does LHS = RHS, otherwise it calls the
+ // appropriate write barrier function.
+ Statement*
+ assign_with_write_barrier(Function*, Block*, Statement_inserter*,
+ Expression* lhs, Expression* rhs, Location);
+
// Flatten parse tree.
void
flatten();
@@ -737,6 +757,10 @@ class Gogo
named_types_are_converted() const
{ return this->named_types_are_converted_; }
+ // Give an error if the initialization of VAR depends on itself.
+ void
+ check_self_dep(Named_object*);
+
// Write out the global values.
void
write_globals();
@@ -805,6 +829,12 @@ class Gogo
std::vector<Bstatement*>&,
Bfunction* init_bfunction);
+ Named_object*
+ write_barrier_variable();
+
+ Statement*
+ check_write_barrier(Block*, Statement*, Statement*);
+
// Type used to map import names to packages.
typedef std::map<std::string, Package*> Imports;
@@ -1096,6 +1126,11 @@ class Function
set_asm_name(const std::string& asm_name)
{ this->asm_name_ = asm_name; }
+ // Return the pragmas for this function.
+ unsigned int
+ pragmas() const
+ { return this->pragmas_; }
+
// Set the pragmas for this function.
void
set_pragmas(unsigned int pragmas)
@@ -1648,7 +1683,7 @@ class Variable
set_is_used()
{ this->is_used_ = true; }
- // Clear the initial value; used for error handling.
+ // Clear the initial value; used for error handling and write barriers.
void
clear_init()
{ this->init_ = NULL; }
diff --git a/gcc/go/gofrontend/import-archive.cc b/gcc/go/gofrontend/import-archive.cc
index 18d1fdc..a6d5403 100644
--- a/gcc/go/gofrontend/import-archive.cc
+++ b/gcc/go/gofrontend/import-archive.cc
@@ -25,8 +25,33 @@ static const char armagt[] =
'!', '<', 't', 'h', 'i', 'n', '>', '\n'
};
+static const char armagb[] =
+{
+ '<', 'b', 'i', 'g', 'a', 'f', '>', '\n'
+};
+
static const char arfmag[2] = { '`', '\n' };
+// Archive fixed length header for AIX big format.
+
+struct Archive_fl_header
+{
+ // Archive magic string.
+ char fl_magic[8];
+ // Offset to member table.
+ char fl_memoff[20];
+ // Offset to global symbol table.
+ char fl_gstoff[20];
+ // Offset to global symbol table for 64-bit objects.
+ char fl_gst64off[20];
+ // Offset to first archive member.
+ char fl_fstmoff[20];
+ // Offset to last archive member.
+ char fl_lstmoff[20];
+ // Offset to first member on free list.
+ char fl_freeoff[20];
+};
+
// The header of an entry in an archive. This is all readable text,
// padded with spaces where necesary.
@@ -48,6 +73,29 @@ struct Archive_header
char ar_fmag[2];
};
+// The header of an entry in an AIX big archive.
+// This is followed by ar_namlen bytes + 2 bytes for arfmag.
+
+struct Archive_big_header
+{
+ // The file size in decimal.
+ char ar_size[20];
+ // The next member offset in decimal.
+ char ar_nxtmem[20];
+ // The previous member offset in decimal.
+ char ar_prvmem[20];
+ // The file modification time in decimal.
+ char ar_date[12];
+ // The user's UID in decimal.
+ char ar_uid[12];
+ // The user's GID in decimal.
+ char ar_gid[12];
+ // The file mode in octal.
+ char ar_mode[12];
+ // The file name length in decimal.
+ char ar_namlen[4];
+};
+
// The functions in this file extract Go export data from an archive.
const int Import::archive_magic_len;
@@ -59,7 +107,8 @@ bool
Import::is_archive_magic(const char* bytes)
{
return (memcmp(bytes, armag, Import::archive_magic_len) == 0
- || memcmp(bytes, armagt, Import::archive_magic_len) == 0);
+ || memcmp(bytes, armagt, Import::archive_magic_len) == 0
+ || memcmp(bytes, armagb, Import::archive_magic_len) == 0);
}
// An object used to read an archive file.
@@ -68,8 +117,9 @@ class Archive_file
{
public:
Archive_file(const std::string& filename, int fd, Location location)
- : filename_(filename), fd_(fd), filesize_(-1), extended_names_(),
- is_thin_archive_(false), location_(location), nested_archives_()
+ : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0),
+ extended_names_(), is_thin_archive_(false), is_big_archive_(false),
+ location_(location), nested_archives_()
{ }
// Initialize.
@@ -86,11 +136,21 @@ class Archive_file
filesize() const
{ return this->filesize_; }
+ // Return the offset of the first member.
+ off_t
+ first_member_offset() const
+ { return this->first_member_offset_; }
+
// Return whether this is a thin archive.
bool
is_thin_archive() const
{ return this->is_thin_archive_; }
+ // Return whether this is a big archive.
+ bool
+ is_big_archive() const
+ { return this->is_big_archive_; }
+
// Return the location of the import statement.
Location
location() const
@@ -100,10 +160,15 @@ class Archive_file
bool
read(off_t offset, off_t size, char*);
- // Read the archive header at OFF, setting *PNAME, *SIZE, and
- // *NESTED_OFF.
+ // Parse a decimal in readable text.
bool
- read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off);
+ parse_decimal(const char* str, off_t size, long* res) const;
+
+ // Read the archive header at OFF, setting *PNAME, *SIZE,
+ // *NESTED_OFF and *NEXT_OFF.
+ bool
+ read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off,
+ off_t* next_off);
// Interpret the header of HDR, the header of the archive member at
// file offset OFF. Return whether it succeeded. Set *SIZE to the
@@ -120,6 +185,25 @@ class Archive_file
std::string* memname);
private:
+ // Initialize a big archive (AIX)
+ bool
+ initialize_big_archive();
+
+ // Initialize a normal archive
+ bool
+ initialize_archive();
+
+ // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+ bool
+ read_big_archive_header(off_t off, std::string* pname,
+ off_t* size, off_t* next_off);
+
+ // Read the normal archive header at OFF, setting *PNAME, *SIZE,
+ // *NESTED_OFF and *NEXT_OFF.
+ bool
+ read_archive_header(off_t off, std::string* pname, off_t* size,
+ off_t* nested_off, off_t* next_off);
+
// For keeping track of open nested archives in a thin archive file.
typedef std::map<std::string, Archive_file*> Nested_archive_table;
@@ -129,10 +213,14 @@ class Archive_file
int fd_;
// The file size;
off_t filesize_;
+ // The first member offset;
+ off_t first_member_offset_;
// The extended name table.
std::string extended_names_;
// Whether this is a thin archive.
bool is_thin_archive_;
+ // Whether this is a big archive.
+ bool is_big_archive_;
// The location of the import statements.
Location location_;
// Table of nested archives.
@@ -157,9 +245,60 @@ Archive_file::initialize()
go_error_at(this->location_, "%s: %m", this->filename_.c_str());
return false;
}
- this->is_thin_archive_ = memcmp(buf, armagt, sizeof(armagt)) == 0;
+ if (memcmp(buf, armagt, sizeof(armagt)) == 0)
+ this->is_thin_archive_ = true;
+ else if (memcmp(buf, armagb, sizeof(armagb)) == 0)
+ this->is_big_archive_ = true;
- if (this->filesize_ == sizeof(armag))
+ if (this->is_big_archive_)
+ return this->initialize_big_archive();
+ else
+ return this->initialize_archive();
+}
+
+// Initialize a big archive (AIX).
+
+bool
+Archive_file::initialize_big_archive()
+{
+ Archive_fl_header flhdr;
+
+ // Read the fixed length header.
+ if (::lseek(this->fd_, 0, SEEK_SET) < 0
+ || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr))
+ {
+ go_error_at(this->location_, "%s: could not read archive header",
+ this->filename_.c_str());
+ return false;
+ }
+
+ // Parse offset of the first member.
+ long off;
+ if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off))
+ {
+ char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1];
+ memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff));
+ go_error_at(this->location_,
+ ("%s: malformed first member offset in archive header"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), buf);
+ delete[] buf;
+ return false;
+ }
+ if (off == 0) // Empty archive.
+ this->first_member_offset_ = this->filesize_;
+ else
+ this->first_member_offset_ = off;
+ return true;
+}
+
+// Initialize a normal archive.
+
+bool
+Archive_file::initialize_archive()
+{
+ this->first_member_offset_ = sizeof(armag);
+ if (this->first_member_offset_ == this->filesize_)
{
// Empty archive.
return true;
@@ -168,15 +307,14 @@ Archive_file::initialize()
// Look for the extended name table.
std::string filename;
off_t size;
- if (!this->read_header(sizeof(armagt), &filename, &size, NULL))
+ off_t next_off;
+ if (!this->read_header(this->first_member_offset_, &filename,
+ &size, NULL, &next_off))
return false;
if (filename.empty())
{
// We found the symbol table.
- off_t off = sizeof(armagt) + sizeof(Archive_header) + size;
- if ((off & 1) != 0)
- ++off;
- if (!this->read_header(off, &filename, &size, NULL))
+ if (!this->read_header(next_off, &filename, &size, NULL, NULL))
filename.clear();
}
if (filename == "/")
@@ -210,19 +348,142 @@ Archive_file::read(off_t offset, off_t size, char* buf)
return true;
}
+// Parse a decimal in readable text.
+
+bool
+Archive_file::parse_decimal(const char* str, off_t size, long* res) const
+{
+ char* buf = new char[size + 1];
+ memcpy(buf, str, size);
+ char* ps = buf + size;
+ while (ps > buf && ps[-1] == ' ')
+ --ps;
+ *ps = '\0';
+
+ errno = 0;
+ char* end;
+ *res = strtol(buf, &end, 10);
+ if (*end != '\0'
+ || *res < 0
+ || (*res == LONG_MAX && errno == ERANGE))
+ {
+ delete[] buf;
+ return false;
+ }
+ delete[] buf;
+ return true;
+}
+
// Read the header at OFF. Set *PNAME to the name, *SIZE to the size,
-// and *NESTED_OFF to the nested offset.
+// *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset.
bool
Archive_file::read_header(off_t off, std::string* pname, off_t* size,
- off_t* nested_off)
+ off_t* nested_off, off_t* next_off)
{
- Archive_header hdr;
if (::lseek(this->fd_, off, SEEK_SET) < 0)
{
go_error_at(this->location_, "%s: %m", this->filename_.c_str());
return false;
}
+ if (this->is_big_archive_)
+ return this->read_big_archive_header(off, pname, size, next_off);
+ else
+ return this->read_archive_header(off, pname, size, nested_off, next_off);
+}
+
+// Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+
+bool
+Archive_file::read_big_archive_header(off_t off, std::string* pname,
+ off_t* size, off_t* next_off)
+{
+ Archive_big_header hdr;
+ ssize_t got;
+
+ got = ::read(this->fd_, &hdr, sizeof hdr);
+ if (got != sizeof hdr)
+ {
+ if (got < 0)
+ go_error_at(this->location_, "%s: %m", this->filename_.c_str());
+ else if (got > 0)
+ go_error_at(this->location_, "%s: short entry header at %ld",
+ this->filename_.c_str(), static_cast<long>(off));
+ else
+ go_error_at(this->location_, "%s: unexpected EOF at %ld",
+ this->filename_.c_str(), static_cast<long>(off));
+ }
+
+ long local_size;
+ if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size))
+ {
+ char* buf = new char[sizeof(hdr.ar_size) + 1];
+ memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size));
+ go_error_at(this->location_,
+ ("%s: malformed ar_size in entry header at %ld"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), static_cast<long>(off), buf);
+ delete[] buf;
+ return false;
+ }
+ *size = local_size;
+
+ long namlen;
+ if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen))
+ {
+ char* buf = new char[sizeof(hdr.ar_namlen) + 1];
+ memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen));
+ go_error_at(this->location_,
+ ("%s: malformed ar_namlen in entry header at %ld"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), static_cast<long>(off), buf);
+ delete[] buf;
+ return false;
+ }
+ // Read member name following member header.
+ char* rdbuf = new char[namlen];
+ got = ::read(this->fd_, rdbuf, namlen);
+ if (got != namlen)
+ {
+ go_error_at(this->location_,
+ "%s: malformed member name in entry header at %ld",
+ this->filename_.c_str(), static_cast<long>(off));
+ delete[] rdbuf;
+ return false;
+ }
+ pname->assign(rdbuf, namlen);
+ delete[] rdbuf;
+
+ long local_next_off;
+ if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off))
+ {
+ char* buf = new char[sizeof(hdr.ar_nxtmem) + 1];
+ memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem));
+ go_error_at(this->location_,
+ ("%s: malformed ar_nxtmem in entry header at %ld"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), static_cast<long>(off), buf);
+ delete[] buf;
+ return false;
+ }
+ if (next_off != NULL)
+ {
+ if (local_next_off == 0) // Last member.
+ *next_off = this->filesize_;
+ else
+ *next_off = local_next_off;
+ }
+ return true;
+}
+
+// Read the normal archive header at OFF, setting *PNAME, *SIZE,
+// *NESTED_OFF and *NEXT_OFF.
+
+bool
+Archive_file::read_archive_header(off_t off, std::string* pname, off_t* size,
+ off_t* nested_off, off_t* next_off)
+{
+ Archive_header hdr;
ssize_t got = ::read(this->fd_, &hdr, sizeof hdr);
if (got != sizeof hdr)
{
@@ -240,6 +501,17 @@ Archive_file::read_header(off_t off, std::string* pname, off_t* size,
return false;
if (nested_off != NULL)
*nested_off = local_nested_off;
+
+ off_t local_next_off;
+ local_next_off = off + sizeof(Archive_header);
+ if (!this->is_thin_archive_ || pname->empty() || *pname == "/")
+ local_next_off += *size;
+ if ((local_next_off & 1) != 0)
+ ++local_next_off;
+ if (local_next_off > this->filesize_) // Last member.
+ local_next_off = this->filesize_;
+ if (next_off != NULL)
+ *next_off = local_next_off;
return true;
}
@@ -258,25 +530,14 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off,
return false;
}
- const int size_string_size = sizeof hdr->ar_size;
- char size_string[size_string_size + 1];
- memcpy(size_string, hdr->ar_size, size_string_size);
- char* ps = size_string + size_string_size;
- while (ps > size_string && ps[-1] == ' ')
- --ps;
- *ps = '\0';
-
- errno = 0;
- char* end;
- *size = strtol(size_string, &end, 10);
- if (*end != '\0'
- || *size < 0
- || (*size == LONG_MAX && errno == ERANGE))
+ long local_size;
+ if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size))
{
go_error_at(this->location_, "%s: malformed archive header size at %lu",
this->filename_.c_str(), static_cast<unsigned long>(off));
return false;
}
+ *size = local_size;
*nested_off = 0;
if (hdr->ar_name[0] != '/')
@@ -313,6 +574,7 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off,
}
else
{
+ char* end;
errno = 0;
long x = strtol(hdr->ar_name + 1, &end, 10);
long y = 0;
@@ -352,7 +614,17 @@ Archive_file::get_file_and_offset(off_t off, const std::string& hdrname,
off_t nested_off, int* memfd, off_t* memoff,
std::string* memname)
{
- if (!this->is_thin_archive_)
+ if (this->is_big_archive_)
+ {
+ *memfd = this->fd_;
+ *memoff = (off + sizeof(Archive_big_header) + hdrname.length()
+ + sizeof(arfmag));
+ if ((*memoff & 1) != 0)
+ ++*memoff;
+ *memname = this->filename_ + '(' + hdrname + ')';
+ return true;
+ }
+ else if (!this->is_thin_archive_)
{
*memfd = this->fd_;
*memoff = off + sizeof(Archive_header);
@@ -399,7 +671,7 @@ Archive_file::get_file_and_offset(off_t off, const std::string& hdrname,
std::string nname;
off_t nsize;
off_t nnested_off;
- if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off))
+ if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL))
return false;
return nfile->get_file_and_offset(nested_off, nname, nnested_off,
memfd, memoff, memname);
@@ -453,11 +725,7 @@ class Archive_iterator
{
if (this->off_ == this->afile_->filesize())
return *this;
- this->off_ += sizeof(Archive_header);
- if (!this->afile_->is_thin_archive())
- this->off_ += this->header_.size;
- if ((this->off_ & 1) != 0)
- ++this->off_;
+ this->off_ = this->next_off_;
this->read_next_header();
return *this;
}
@@ -486,6 +754,8 @@ class Archive_iterator
Archive_file* afile_;
// The current offset in the file.
off_t off_;
+ // The offset of the next member.
+ off_t next_off_;
// The current archive header.
Header header_;
};
@@ -498,31 +768,16 @@ Archive_iterator::read_next_header()
off_t filesize = this->afile_->filesize();
while (true)
{
- if (filesize - this->off_ < static_cast<off_t>(sizeof(Archive_header)))
- {
- if (filesize != this->off_)
- {
- go_error_at(this->afile_->location(),
- "%s: short archive header at %lu",
- this->afile_->filename().c_str(),
- static_cast<unsigned long>(this->off_));
- this->off_ = filesize;
- }
- this->header_.off = filesize;
- return;
- }
-
- char buf[sizeof(Archive_header)];
- if (!this->afile_->read(this->off_, sizeof(Archive_header), buf))
+ if (this->off_ == filesize)
{
this->header_.off = filesize;
return;
}
- const Archive_header* hdr = reinterpret_cast<const Archive_header*>(buf);
- if (!this->afile_->interpret_header(hdr, this->off_, &this->header_.name,
- &this->header_.size,
- &this->header_.nested_off))
+ if (!this->afile_->read_header(this->off_, &this->header_.name,
+ &this->header_.size,
+ &this->header_.nested_off,
+ &this->next_off_))
{
this->header_.off = filesize;
return;
@@ -533,9 +788,7 @@ Archive_iterator::read_next_header()
if (!this->header_.name.empty() && this->header_.name != "/")
return;
- this->off_ += sizeof(Archive_header) + this->header_.size;
- if ((this->off_ & 1) != 0)
- ++this->off_;
+ this->off_ = this->next_off_;
}
}
@@ -544,7 +797,7 @@ Archive_iterator::read_next_header()
Archive_iterator
archive_begin(Archive_file* afile)
{
- return Archive_iterator(afile, sizeof(armag));
+ return Archive_iterator(afile, afile->first_member_offset());
}
// Final iterator.
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index 90bf34f..635b7fe 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -220,11 +220,11 @@ DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice", P3(TYPE, SLICE, INT), R1(SLICE))
// Register roots (global variables) for the garbage collector.
-DEF_GO_RUNTIME(REGISTER_GC_ROOTS, "__go_register_gc_roots", P1(POINTER), R0())
+DEF_GO_RUNTIME(REGISTER_GC_ROOTS, "runtime.registerGCRoots", P1(POINTER), R0())
// Allocate memory.
-DEF_GO_RUNTIME(NEW, "__go_new", P2(TYPE, UINTPTR), R1(POINTER))
+DEF_GO_RUNTIME(NEW, "runtime.newobject", P1(TYPE), R1(POINTER))
// Start a new goroutine.
DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0())
@@ -315,6 +315,15 @@ DEF_GO_RUNTIME(IFACEEFACEEQ, "runtime.ifaceefaceeq", P2(IFACE, EFACE),
R1(BOOL))
+// Set *dst = src where dst is a pointer to a pointer and src is a pointer.
+DEF_GO_RUNTIME(WRITEBARRIERPTR, "runtime.writebarrierptr",
+ P2(POINTER, POINTER), R0())
+
+// Set *dst = *src for an arbitrary type.
+DEF_GO_RUNTIME(TYPEDMEMMOVE, "runtime.typedmemmove",
+ P3(TYPE, POINTER, POINTER), R0())
+
+
// Lock the printer (for print/println).
DEF_GO_RUNTIME(PRINTLOCK, "runtime.printlock", P0(), R0())
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index d6ab4cc..00367ef 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -510,6 +510,10 @@ Temporary_statement::do_get_backend(Translate_context* context)
binit = init->get_backend(context);
}
+ if (binit != NULL)
+ binit = context->backend()->convert_expression(btype, binit,
+ this->location());
+
Bstatement* statement;
this->bvariable_ =
context->backend()->temporary_variable(bfunction, context->bblock(),
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index f65dbd7..f2056aa 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -1177,7 +1177,12 @@ Type::type_descriptor_pointer(Gogo* gogo, Location location)
Bexpression* var_expr =
gogo->backend()->var_expression(t->type_descriptor_var_,
VE_rvalue, location);
- return gogo->backend()->address_expression(var_expr, location);
+ Bexpression* var_addr =
+ gogo->backend()->address_expression(var_expr, location);
+ Type* td_type = Type::make_type_descriptor_type();
+ Btype* td_btype = td_type->get_backend(gogo);
+ Btype* ptd_btype = gogo->backend()->pointer_type(td_btype);
+ return gogo->backend()->convert_expression(ptd_btype, var_addr, location);
}
// A mapping from unnamed types to type descriptor variables.
@@ -1395,18 +1400,6 @@ Type::named_type_descriptor(Gogo* gogo, Type* type, Named_type* name)
return type->do_type_descriptor(gogo, name);
}
-// Generate the GC symbol for this TYPE. VALS is the data so far in this
-// symbol; extra values will be appended in do_gc_symbol. OFFSET is the
-// offset into the symbol where the GC data is located. STACK_SIZE is the
-// size of the GC stack when dealing with array types.
-
-void
-Type::gc_symbol(Gogo* gogo, Type* type, Expression_list** vals,
- Expression** offset, int stack_size)
-{
- type->do_gc_symbol(gogo, vals, offset, stack_size);
-}
-
// Make a builtin struct type from a list of fields. The fields are
// pairs of a name and a type.
@@ -1477,6 +1470,7 @@ Type::make_type_descriptor_type()
Location bloc = Linemap::predeclared_location();
Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* pointer_uint8_type = Type::make_pointer_type(uint8_type);
Type* uint32_type = Type::lookup_integer_type("uint32");
Type* uintptr_type = Type::lookup_integer_type("uintptr");
Type* string_type = Type::lookup_string_type();
@@ -1543,15 +1537,16 @@ Type::make_type_descriptor_type()
// The type descriptor type.
Struct_type* type_descriptor_type =
- Type::make_builtin_struct_type(11,
+ Type::make_builtin_struct_type(12,
+ "size", uintptr_type,
+ "ptrdata", uintptr_type,
+ "hash", uint32_type,
"kind", uint8_type,
"align", uint8_type,
"fieldAlign", uint8_type,
- "size", uintptr_type,
- "hash", uint32_type,
"hashfn", hash_fntype,
"equalfn", equal_fntype,
- "gc", uintptr_type,
+ "gcdata", pointer_uint8_type,
"string", pointer_string_type,
"", pointer_uncommon_type,
"ptrToThis",
@@ -2307,30 +2302,25 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
const Struct_field_list* fields = td_type->struct_type()->fields();
Expression_list* vals = new Expression_list();
- vals->reserve(9);
+ vals->reserve(12);
if (!this->has_pointer())
runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS;
if (this->points_to() != NULL)
runtime_type_kind |= RUNTIME_TYPE_KIND_DIRECT_IFACE;
- Struct_field_list::const_iterator p = fields->begin();
- go_assert(p->is_field_name("kind"));
- vals->push_back(Expression::make_integer_ul(runtime_type_kind, p->type(),
- bloc));
-
- ++p;
- go_assert(p->is_field_name("align"));
- Expression::Type_info type_info = Expression::TYPE_INFO_ALIGNMENT;
- vals->push_back(Expression::make_type_info(this, type_info));
+ int64_t ptrsize;
+ int64_t ptrdata;
+ if (this->needs_gcprog(gogo, &ptrsize, &ptrdata))
+ runtime_type_kind |= RUNTIME_TYPE_KIND_GC_PROG;
- ++p;
- go_assert(p->is_field_name("fieldAlign"));
- type_info = Expression::TYPE_INFO_FIELD_ALIGNMENT;
+ Struct_field_list::const_iterator p = fields->begin();
+ go_assert(p->is_field_name("size"));
+ Expression::Type_info type_info = Expression::TYPE_INFO_SIZE;
vals->push_back(Expression::make_type_info(this, type_info));
++p;
- go_assert(p->is_field_name("size"));
- type_info = Expression::TYPE_INFO_SIZE;
+ go_assert(p->is_field_name("ptrdata"));
+ type_info = Expression::TYPE_INFO_DESCRIPTOR_PTRDATA;
vals->push_back(Expression::make_type_info(this, type_info));
++p;
@@ -2343,6 +2333,21 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
vals->push_back(Expression::make_integer_ul(h, p->type(), bloc));
++p;
+ go_assert(p->is_field_name("kind"));
+ vals->push_back(Expression::make_integer_ul(runtime_type_kind, p->type(),
+ bloc));
+
+ ++p;
+ go_assert(p->is_field_name("align"));
+ type_info = Expression::TYPE_INFO_ALIGNMENT;
+ vals->push_back(Expression::make_type_info(this, type_info));
+
+ ++p;
+ go_assert(p->is_field_name("fieldAlign"));
+ type_info = Expression::TYPE_INFO_FIELD_ALIGNMENT;
+ vals->push_back(Expression::make_type_info(this, type_info));
+
+ ++p;
go_assert(p->is_field_name("hashfn"));
Function_type* hash_fntype = p->type()->function_type();
@@ -2368,7 +2373,7 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc));
++p;
- go_assert(p->is_field_name("gc"));
+ go_assert(p->is_field_name("gcdata"));
vals->push_back(Expression::make_gc_symbol(this));
++p;
@@ -2413,6 +2418,12 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
return Expression::make_struct_composite_literal(td_type, vals, bloc);
}
+// The maximum length of a GC ptrmask bitmap. This corresponds to the
+// length used by the gc toolchain, and also appears in
+// libgo/go/reflect/type.go.
+
+static const int64_t max_ptrmask_bytes = 2048;
+
// Return a pointer to the Garbage Collection information for this type.
Bexpression*
@@ -2421,6 +2432,10 @@ Type::gc_symbol_pointer(Gogo* gogo)
Type* t = this->forwarded();
while (t->named_type() != NULL && t->named_type()->is_alias())
t = t->named_type()->real_type()->forwarded();
+
+ if (!t->has_pointer())
+ return gogo->backend()->nil_pointer_expression();
+
if (t->gc_symbol_var_ == NULL)
{
t->make_gc_symbol_var(gogo);
@@ -2431,7 +2446,10 @@ Type::gc_symbol_pointer(Gogo* gogo)
gogo->backend()->var_expression(t->gc_symbol_var_, VE_rvalue, bloc);
Bexpression* addr_expr =
gogo->backend()->address_expression(var_expr, bloc);
- Btype* ubtype = Type::lookup_integer_type("uintptr")->get_backend(gogo);
+
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* pointer_uint8_type = Type::make_pointer_type(uint8_type);
+ Btype* ubtype = pointer_uint8_type->get_backend(gogo);
return gogo->backend()->convert_expression(ubtype, addr_expr, bloc);
}
@@ -2466,10 +2484,20 @@ Type::make_gc_symbol_var(Gogo* gogo)
phash = &ins.first->second;
}
+ int64_t ptrsize;
+ int64_t ptrdata;
+ if (!this->needs_gcprog(gogo, &ptrsize, &ptrdata))
+ {
+ this->gc_symbol_var_ = this->gc_ptrmask_var(gogo, ptrsize, ptrdata);
+ if (phash != NULL)
+ *phash = this->gc_symbol_var_;
+ return;
+ }
+
std::string sym_name = this->type_descriptor_var_name(gogo, nt) + "$gc";
// Build the contents of the gc symbol.
- Expression* sym_init = this->gc_symbol_constructor(gogo);
+ Expression* sym_init = this->gcprog_constructor(gogo, ptrsize, ptrdata);
Btype* sym_btype = sym_init->type()->get_backend(gogo);
// If the type descriptor for this type is defined somewhere else, so is the
@@ -2502,28 +2530,13 @@ Type::make_gc_symbol_var(Gogo* gogo)
is_common = true;
}
- // The current garbage collector requires that the GC symbol be
- // aligned to at least a four byte boundary. See the use of PRECISE
- // and LOOP in libgo/runtime/mgc0.c.
- int64_t align;
- if (!sym_init->type()->backend_type_align(gogo, &align))
- go_assert(saw_errors());
- if (align < 4)
- align = 4;
- else
- {
- // Use default alignment.
- align = 0;
- }
-
// Since we are building the GC symbol in this package, we must create the
// variable before converting the initializer to its backend representation
// because the initializer may refer to the GC symbol for this type.
std::string asm_name(go_selectively_encode_id(sym_name));
this->gc_symbol_var_ =
gogo->backend()->implicit_variable(sym_name, asm_name,
- sym_btype, false, true, is_common,
- align);
+ sym_btype, false, true, is_common, 0);
if (phash != NULL)
*phash = this->gc_symbol_var_;
@@ -2535,50 +2548,658 @@ Type::make_gc_symbol_var(Gogo* gogo)
sym_binit);
}
-// Return an array literal for the Garbage Collection information for this type.
+// Return whether this type needs a GC program, and set *PTRDATA to
+// the size of the pointer data in bytes and *PTRSIZE to the size of a
+// pointer.
+
+bool
+Type::needs_gcprog(Gogo* gogo, int64_t* ptrsize, int64_t* ptrdata)
+{
+ if (!this->backend_type_ptrdata(gogo, ptrdata))
+ {
+ go_assert(saw_errors());
+ return false;
+ }
+
+ Type* voidptr = Type::make_pointer_type(Type::make_void_type());
+ if (!voidptr->backend_type_size(gogo, ptrsize))
+ go_unreachable();
+
+ return *ptrdata / *ptrsize > max_ptrmask_bytes;
+}
+
+// A simple class used to build a GC ptrmask for a type.
+
+class Ptrmask
+{
+ public:
+ Ptrmask(size_t count)
+ : bits_((count + 7) / 8, 0)
+ {}
+
+ void
+ set_from(Gogo*, Type*, int64_t ptrsize, int64_t offset);
+
+ std::string
+ symname() const;
+
+ Expression*
+ constructor(Gogo* gogo) const;
+
+ private:
+ void
+ set(size_t index)
+ { this->bits_.at(index / 8) |= 1 << (index % 8); }
+
+ // The actual bits.
+ std::vector<unsigned char> bits_;
+};
+
+// Set bits in ptrmask starting from OFFSET based on TYPE. OFFSET
+// counts in bytes. PTRSIZE is the size of a pointer on the target
+// system.
+
+void
+Ptrmask::set_from(Gogo* gogo, Type* type, int64_t ptrsize, int64_t offset)
+{
+ switch (type->base()->classification())
+ {
+ default:
+ case Type::TYPE_NIL:
+ case Type::TYPE_CALL_MULTIPLE_RESULT:
+ case Type::TYPE_NAMED:
+ case Type::TYPE_FORWARD:
+ go_unreachable();
+
+ case Type::TYPE_ERROR:
+ case Type::TYPE_VOID:
+ case Type::TYPE_BOOLEAN:
+ case Type::TYPE_INTEGER:
+ case Type::TYPE_FLOAT:
+ case Type::TYPE_COMPLEX:
+ case Type::TYPE_SINK:
+ break;
+
+ case Type::TYPE_FUNCTION:
+ case Type::TYPE_POINTER:
+ case Type::TYPE_MAP:
+ case Type::TYPE_CHANNEL:
+ // These types are all a single pointer.
+ go_assert((offset % ptrsize) == 0);
+ this->set(offset / ptrsize);
+ break;
+
+ case Type::TYPE_STRING:
+ // A string starts with a single pointer.
+ go_assert((offset % ptrsize) == 0);
+ this->set(offset / ptrsize);
+ break;
+
+ case Type::TYPE_INTERFACE:
+ // An interface is two pointers.
+ go_assert((offset % ptrsize) == 0);
+ this->set(offset / ptrsize);
+ this->set((offset / ptrsize) + 1);
+ break;
+
+ case Type::TYPE_STRUCT:
+ {
+ if (!type->has_pointer())
+ return;
+
+ const Struct_field_list* fields = type->struct_type()->fields();
+ int64_t soffset = 0;
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf)
+ {
+ int64_t field_align;
+ if (!pf->type()->backend_type_field_align(gogo, &field_align))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+ soffset = (soffset + (field_align - 1)) &~ (field_align - 1);
+
+ this->set_from(gogo, pf->type(), ptrsize, offset + soffset);
+
+ int64_t field_size;
+ if (!pf->type()->backend_type_size(gogo, &field_size))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+ soffset += field_size;
+ }
+ }
+ break;
+
+ case Type::TYPE_ARRAY:
+ if (type->is_slice_type())
+ {
+ // A slice starts with a single pointer.
+ go_assert((offset % ptrsize) == 0);
+ this->set(offset / ptrsize);
+ break;
+ }
+ else
+ {
+ if (!type->has_pointer())
+ return;
+
+ int64_t len;
+ if (!type->array_type()->int_length(&len))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+
+ Type* element_type = type->array_type()->element_type();
+ int64_t ele_size;
+ if (!element_type->backend_type_size(gogo, &ele_size))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+
+ int64_t eoffset = 0;
+ for (int64_t i = 0; i < len; i++, eoffset += ele_size)
+ this->set_from(gogo, element_type, ptrsize, offset + eoffset);
+ break;
+ }
+ }
+}
+
+// Return a symbol name for this ptrmask. This is used to coalesce
+// identical ptrmasks, which are common. The symbol name must use
+// only characters that are valid in symbols. It's nice if it's
+// short. We convert it to a base64 string.
+
+std::string
+Ptrmask::symname() const
+{
+ const char chars[65] =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
+ go_assert(chars[64] == '\0');
+ std::string ret;
+ unsigned int b = 0;
+ int remaining = 0;
+ for (std::vector<unsigned char>::const_iterator p = this->bits_.begin();
+ p != this->bits_.end();
+ ++p)
+ {
+ b |= *p << remaining;
+ remaining += 8;
+ while (remaining >= 6)
+ {
+ ret += chars[b & 0x3f];
+ b >>= 6;
+ remaining -= 6;
+ }
+ }
+ while (remaining > 0)
+ {
+ ret += chars[b & 0x3f];
+ b >>= 6;
+ remaining -= 6;
+ }
+ return ret;
+}
+
+// Return a constructor for this ptrmask. This will be used to
+// initialize the runtime ptrmask value.
Expression*
-Type::gc_symbol_constructor(Gogo* gogo)
+Ptrmask::constructor(Gogo* gogo) const
{
Location bloc = Linemap::predeclared_location();
+ Type* byte_type = gogo->lookup_global("byte")->type_value();
+ Expression* len = Expression::make_integer_ul(this->bits_.size(), NULL,
+ bloc);
+ Array_type* at = Type::make_array_type(byte_type, len);
+ Expression_list* vals = new Expression_list();
+ vals->reserve(this->bits_.size());
+ for (std::vector<unsigned char>::const_iterator p = this->bits_.begin();
+ p != this->bits_.end();
+ ++p)
+ vals->push_back(Expression::make_integer_ul(*p, byte_type, bloc));
+ return Expression::make_array_composite_literal(at, vals, bloc);
+}
- // The common GC Symbol data starts with the width of the type and ends
- // with the GC Opcode GC_END.
- // However, for certain types, the GC symbol may include extra information
- // before the ending opcode, so we pass the expression list into
- // Type::gc_symbol to allow it to add extra information as is necessary.
- Expression_list* vals = new Expression_list;
+// The hash table mapping a ptrmask symbol name to the ptrmask variable.
+Type::GC_gcbits_vars Type::gc_gcbits_vars;
- Type* uintptr_t = Type::lookup_integer_type("uintptr");
- // width
- vals->push_back(Expression::make_type_info(this,
- Expression::TYPE_INFO_SIZE));
+// Return a ptrmask variable for a type. For a type descriptor this
+// is only used for variables that are small enough to not need a
+// gcprog, but for a global variable this is used for a variable of
+// any size. PTRDATA is the number of bytes of the type that contain
+// pointer data. PTRSIZE is the size of a pointer on the target
+// system.
- Expression* offset = Expression::make_integer_ul(0, uintptr_t, bloc);
+Bvariable*
+Type::gc_ptrmask_var(Gogo* gogo, int64_t ptrsize, int64_t ptrdata)
+{
+ Ptrmask ptrmask(ptrdata / ptrsize);
+ ptrmask.set_from(gogo, this, ptrsize, 0);
+ std::string sym_name = "runtime.gcbits." + ptrmask.symname();
+ Bvariable* bvnull = NULL;
+ std::pair<GC_gcbits_vars::iterator, bool> ins =
+ Type::gc_gcbits_vars.insert(std::make_pair(sym_name, bvnull));
+ if (!ins.second)
+ {
+ // We've already built a GC symbol for this set of gcbits.
+ return ins.first->second;
+ }
- this->do_gc_symbol(gogo, &vals, &offset, 0);
+ Expression* val = ptrmask.constructor(gogo);
+ Translate_context context(gogo, NULL, NULL, NULL);
+ context.set_is_const();
+ Bexpression* bval = val->get_backend(&context);
- vals->push_back(Expression::make_integer_ul(GC_END, uintptr_t, bloc));
+ std::string asm_name(go_selectively_encode_id(sym_name));
+ Btype *btype = val->type()->get_backend(gogo);
+ Bvariable* ret = gogo->backend()->implicit_variable(sym_name, asm_name,
+ btype, false, true,
+ true, 0);
+ gogo->backend()->implicit_variable_set_init(ret, sym_name, btype, false,
+ true, true, bval);
+ ins.first->second = ret;
+ return ret;
+}
- Expression* len = Expression::make_integer_ul(vals->size(), NULL,
- bloc);
- Array_type* gc_symbol_type = Type::make_array_type(uintptr_t, len);
- gc_symbol_type->set_is_array_incomparable();
- return Expression::make_array_composite_literal(gc_symbol_type, vals, bloc);
+// A GCProg is used to build a program for the garbage collector.
+// This is used for types with a lot of pointer data, to reduce the
+// size of the data in the compiled program. The program is expanded
+// at runtime. For the format, see runGCProg in libgo/go/runtime/mbitmap.go.
+
+class GCProg
+{
+ public:
+ GCProg()
+ : bytes_(), index_(0), nb_(0)
+ {}
+
+ // The number of bits described so far.
+ int64_t
+ bit_index() const
+ { return this->index_; }
+
+ void
+ set_from(Gogo*, Type*, int64_t ptrsize, int64_t offset);
+
+ void
+ end();
+
+ Expression*
+ constructor(Gogo* gogo) const;
+
+ private:
+ void
+ ptr(int64_t);
+
+ bool
+ should_repeat(int64_t, int64_t);
+
+ void
+ repeat(int64_t, int64_t);
+
+ void
+ zero_until(int64_t);
+
+ void
+ lit(unsigned char);
+
+ void
+ varint(int64_t);
+
+ void
+ flushlit();
+
+ // Add a byte to the program.
+ void
+ byte(unsigned char x)
+ { this->bytes_.push_back(x); }
+
+ // The maximum number of bytes of literal bits.
+ static const int max_literal = 127;
+
+ // The program.
+ std::vector<unsigned char> bytes_;
+ // The index of the last bit described.
+ int64_t index_;
+ // The current set of literal bits.
+ unsigned char b_[max_literal];
+ // The current number of literal bits.
+ int nb_;
+};
+
+// Set data in gcprog starting from OFFSET based on TYPE. OFFSET
+// counts in bytes. PTRSIZE is the size of a pointer on the target
+// system.
+
+void
+GCProg::set_from(Gogo* gogo, Type* type, int64_t ptrsize, int64_t offset)
+{
+ switch (type->base()->classification())
+ {
+ default:
+ case Type::TYPE_NIL:
+ case Type::TYPE_CALL_MULTIPLE_RESULT:
+ case Type::TYPE_NAMED:
+ case Type::TYPE_FORWARD:
+ go_unreachable();
+
+ case Type::TYPE_ERROR:
+ case Type::TYPE_VOID:
+ case Type::TYPE_BOOLEAN:
+ case Type::TYPE_INTEGER:
+ case Type::TYPE_FLOAT:
+ case Type::TYPE_COMPLEX:
+ case Type::TYPE_SINK:
+ break;
+
+ case Type::TYPE_FUNCTION:
+ case Type::TYPE_POINTER:
+ case Type::TYPE_MAP:
+ case Type::TYPE_CHANNEL:
+ // These types are all a single pointer.
+ go_assert((offset % ptrsize) == 0);
+ this->ptr(offset / ptrsize);
+ break;
+
+ case Type::TYPE_STRING:
+ // A string starts with a single pointer.
+ go_assert((offset % ptrsize) == 0);
+ this->ptr(offset / ptrsize);
+ break;
+
+ case Type::TYPE_INTERFACE:
+ // An interface is two pointers.
+ go_assert((offset % ptrsize) == 0);
+ this->ptr(offset / ptrsize);
+ this->ptr((offset / ptrsize) + 1);
+ break;
+
+ case Type::TYPE_STRUCT:
+ {
+ if (!type->has_pointer())
+ return;
+
+ const Struct_field_list* fields = type->struct_type()->fields();
+ int64_t soffset = 0;
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf)
+ {
+ int64_t field_align;
+ if (!pf->type()->backend_type_field_align(gogo, &field_align))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+ soffset = (soffset + (field_align - 1)) &~ (field_align - 1);
+
+ this->set_from(gogo, pf->type(), ptrsize, offset + soffset);
+
+ int64_t field_size;
+ if (!pf->type()->backend_type_size(gogo, &field_size))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+ soffset += field_size;
+ }
+ }
+ break;
+
+ case Type::TYPE_ARRAY:
+ if (type->is_slice_type())
+ {
+ // A slice starts with a single pointer.
+ go_assert((offset % ptrsize) == 0);
+ this->ptr(offset / ptrsize);
+ break;
+ }
+ else
+ {
+ if (!type->has_pointer())
+ return;
+
+ int64_t len;
+ if (!type->array_type()->int_length(&len))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+
+ Type* element_type = type->array_type()->element_type();
+
+ // Flatten array of array to a big array by multiplying counts.
+ while (element_type->array_type() != NULL
+ && !element_type->is_slice_type())
+ {
+ int64_t ele_len;
+ if (!element_type->array_type()->int_length(&ele_len))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+
+ len *= ele_len;
+ element_type = element_type->array_type()->element_type();
+ }
+
+ int64_t ele_size;
+ if (!element_type->backend_type_size(gogo, &ele_size))
+ {
+ go_assert(saw_errors());
+ return;
+ }
+
+ go_assert(len > 0 && ele_size > 0);
+
+ if (!this->should_repeat(ele_size / ptrsize, len))
+ {
+ // Cheaper to just emit the bits.
+ int64_t eoffset = 0;
+ for (int64_t i = 0; i < len; i++, eoffset += ele_size)
+ this->set_from(gogo, element_type, ptrsize, offset + eoffset);
+ }
+ else
+ {
+ go_assert((offset % ptrsize) == 0);
+ go_assert((ele_size % ptrsize) == 0);
+ this->set_from(gogo, element_type, ptrsize, offset);
+ this->zero_until((offset + ele_size) / ptrsize);
+ this->repeat(ele_size / ptrsize, len - 1);
+ }
+
+ break;
+ }
+ }
}
-// Advance the OFFSET of the GC symbol by this type's width.
+// Emit a 1 into the bit stream of a GC program at the given bit index.
void
-Type::advance_gc_offset(Expression** offset)
+GCProg::ptr(int64_t index)
{
- if (this->is_error_type())
+ go_assert(index >= this->index_);
+ this->zero_until(index);
+ this->lit(1);
+}
+
+// Return whether it is worthwhile to use a repeat to describe c
+// elements of n bits each, compared to just emitting c copies of the
+// n-bit description.
+
+bool
+GCProg::should_repeat(int64_t n, int64_t c)
+{
+ // Repeat if there is more than 1 item and if the total data doesn't
+ // fit into four bytes.
+ return c > 1 && c * n > 4 * 8;
+}
+
+// Emit an instruction to repeat the description of the last n words c
+// times (including the initial description, so c + 1 times in total).
+
+void
+GCProg::repeat(int64_t n, int64_t c)
+{
+ if (n == 0 || c == 0)
+ return;
+ this->flushlit();
+ if (n < 128)
+ this->byte(0x80 | static_cast<unsigned char>(n & 0x7f));
+ else
+ {
+ this->byte(0x80);
+ this->varint(n);
+ }
+ this->varint(c);
+ this->index_ += n * c;
+}
+
+// Add zeros to the bit stream up to the given index.
+
+void
+GCProg::zero_until(int64_t index)
+{
+ go_assert(index >= this->index_);
+ int64_t skip = index - this->index_;
+ if (skip == 0)
+ return;
+ if (skip < 4 * 8)
+ {
+ for (int64_t i = 0; i < skip; ++i)
+ this->lit(0);
+ return;
+ }
+ this->lit(0);
+ this->flushlit();
+ this->repeat(1, skip - 1);
+}
+
+// Add a single literal bit to the program.
+
+void
+GCProg::lit(unsigned char x)
+{
+ if (this->nb_ == GCProg::max_literal)
+ this->flushlit();
+ this->b_[this->nb_] = x;
+ ++this->nb_;
+ ++this->index_;
+}
+
+// Emit the varint encoding of x.
+
+void
+GCProg::varint(int64_t x)
+{
+ go_assert(x >= 0);
+ while (x >= 0x80)
+ {
+ this->byte(0x80 | static_cast<unsigned char>(x & 0x7f));
+ x >>= 7;
+ }
+ this->byte(static_cast<unsigned char>(x & 0x7f));
+}
+
+// Flush any pending literal bits.
+
+void
+GCProg::flushlit()
+{
+ if (this->nb_ == 0)
return;
+ this->byte(static_cast<unsigned char>(this->nb_));
+ unsigned char bits = 0;
+ for (int i = 0; i < this->nb_; ++i)
+ {
+ bits |= this->b_[i] << (i % 8);
+ if ((i + 1) % 8 == 0)
+ {
+ this->byte(bits);
+ bits = 0;
+ }
+ }
+ if (this->nb_ % 8 != 0)
+ this->byte(bits);
+ this->nb_ = 0;
+}
+// Mark the end of a GC program.
+
+void
+GCProg::end()
+{
+ this->flushlit();
+ this->byte(0);
+}
+
+// Return an Expression for the bytes in a GC program.
+
+Expression*
+GCProg::constructor(Gogo* gogo) const
+{
Location bloc = Linemap::predeclared_location();
- Expression* width =
- Expression::make_type_info(this, Expression::TYPE_INFO_SIZE);
- *offset = Expression::make_binary(OPERATOR_PLUS, *offset, width, bloc);
+
+ // The first four bytes are the length of the program in target byte
+ // order. Build a struct whose first type is uint32 to make this
+ // work.
+
+ Type* uint32_type = Type::lookup_integer_type("uint32");
+
+ Type* byte_type = gogo->lookup_global("byte")->type_value();
+ Expression* len = Expression::make_integer_ul(this->bytes_.size(), NULL,
+ bloc);
+ Array_type* at = Type::make_array_type(byte_type, len);
+
+ Struct_type* st = Type::make_builtin_struct_type(2, "len", uint32_type,
+ "bytes", at);
+
+ Expression_list* vals = new Expression_list();
+ vals->reserve(this->bytes_.size());
+ for (std::vector<unsigned char>::const_iterator p = this->bytes_.begin();
+ p != this->bytes_.end();
+ ++p)
+ vals->push_back(Expression::make_integer_ul(*p, byte_type, bloc));
+ Expression* bytes = Expression::make_array_composite_literal(at, vals, bloc);
+
+ vals = new Expression_list();
+ vals->push_back(Expression::make_integer_ul(this->bytes_.size(), uint32_type,
+ bloc));
+ vals->push_back(bytes);
+
+ return Expression::make_struct_composite_literal(st, vals, bloc);
+}
+
+// Return a composite literal for the garbage collection program for
+// this type. This is only used for types that are too large to use a
+// ptrmask.
+
+Expression*
+Type::gcprog_constructor(Gogo* gogo, int64_t ptrsize, int64_t ptrdata)
+{
+ Location bloc = Linemap::predeclared_location();
+
+ GCProg prog;
+ prog.set_from(gogo, this, ptrsize, 0);
+ int64_t offset = prog.bit_index() * ptrsize;
+ prog.end();
+
+ int64_t type_size;
+ if (!this->backend_type_size(gogo, &type_size))
+ {
+ go_assert(saw_errors());
+ return Expression::make_error(bloc);
+ }
+
+ go_assert(offset >= ptrdata && offset <= type_size);
+
+ return prog.constructor(gogo);
}
// Return a composite literal for the uncommon type information for
@@ -2946,6 +3567,164 @@ Type::backend_type_field_align(Gogo* gogo, int64_t *palign)
return true;
}
+// Get the ptrdata value for a type. This is the size of the prefix
+// of the type that contains all pointers. Store the ptrdata in
+// *PPTRDATA and return whether we found it.
+
+bool
+Type::backend_type_ptrdata(Gogo* gogo, int64_t* pptrdata)
+{
+ *pptrdata = 0;
+
+ if (!this->has_pointer())
+ return true;
+
+ if (!this->is_backend_type_size_known(gogo))
+ return false;
+
+ switch (this->classification_)
+ {
+ case TYPE_ERROR:
+ return true;
+
+ case TYPE_FUNCTION:
+ case TYPE_POINTER:
+ case TYPE_MAP:
+ case TYPE_CHANNEL:
+ // These types are nothing but a pointer.
+ return this->backend_type_size(gogo, pptrdata);
+
+ case TYPE_INTERFACE:
+ // An interface is a struct of two pointers.
+ return this->backend_type_size(gogo, pptrdata);
+
+ case TYPE_STRING:
+ {
+ // A string is a struct whose first field is a pointer, and
+ // whose second field is not.
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* ptr = Type::make_pointer_type(uint8_type);
+ return ptr->backend_type_size(gogo, pptrdata);
+ }
+
+ case TYPE_NAMED:
+ case TYPE_FORWARD:
+ return this->base()->backend_type_ptrdata(gogo, pptrdata);
+
+ case TYPE_STRUCT:
+ {
+ const Struct_field_list* fields = this->struct_type()->fields();
+ int64_t offset = 0;
+ const Struct_field *ptr = NULL;
+ int64_t ptr_offset = 0;
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf)
+ {
+ int64_t field_align;
+ if (!pf->type()->backend_type_field_align(gogo, &field_align))
+ return false;
+ offset = (offset + (field_align - 1)) &~ (field_align - 1);
+
+ if (pf->type()->has_pointer())
+ {
+ ptr = &*pf;
+ ptr_offset = offset;
+ }
+
+ int64_t field_size;
+ if (!pf->type()->backend_type_size(gogo, &field_size))
+ return false;
+ offset += field_size;
+ }
+
+ if (ptr != NULL)
+ {
+ int64_t ptr_ptrdata;
+ if (!ptr->type()->backend_type_ptrdata(gogo, &ptr_ptrdata))
+ return false;
+ *pptrdata = ptr_offset + ptr_ptrdata;
+ }
+ return true;
+ }
+
+ case TYPE_ARRAY:
+ if (this->is_slice_type())
+ {
+ // A slice is a struct whose first field is a pointer, and
+ // whose remaining fields are not.
+ Type* element_type = this->array_type()->element_type();
+ Type* ptr = Type::make_pointer_type(element_type);
+ return ptr->backend_type_size(gogo, pptrdata);
+ }
+ else
+ {
+ Numeric_constant nc;
+ if (!this->array_type()->length()->numeric_constant_value(&nc))
+ return false;
+ int64_t len;
+ if (!nc.to_memory_size(&len))
+ return false;
+
+ Type* element_type = this->array_type()->element_type();
+ int64_t ele_size;
+ int64_t ele_ptrdata;
+ if (!element_type->backend_type_size(gogo, &ele_size)
+ || !element_type->backend_type_ptrdata(gogo, &ele_ptrdata))
+ return false;
+ go_assert(ele_size > 0 && ele_ptrdata > 0);
+
+ *pptrdata = (len - 1) * ele_size + ele_ptrdata;
+ return true;
+ }
+
+ default:
+ case TYPE_VOID:
+ case TYPE_BOOLEAN:
+ case TYPE_INTEGER:
+ case TYPE_FLOAT:
+ case TYPE_COMPLEX:
+ case TYPE_SINK:
+ case TYPE_NIL:
+ case TYPE_CALL_MULTIPLE_RESULT:
+ go_unreachable();
+ }
+}
+
+// Get the ptrdata value to store in a type descriptor. This is
+// normally the same as backend_type_ptrdata, but for a type that is
+// large enough to use a gcprog we may need to store a different value
+// if it ends with an array. If the gcprog uses a repeat descriptor
+// for the array, and if the array element ends with non-pointer data,
+// then the gcprog will produce a value that describes the complete
+// array where the backend ptrdata will omit the non-pointer elements
+// of the final array element. This is a subtle difference but the
+// run time code checks it to verify that it has expanded a gcprog as
+// expected.
+
+bool
+Type::descriptor_ptrdata(Gogo* gogo, int64_t* pptrdata)
+{
+ int64_t backend_ptrdata;
+ if (!this->backend_type_ptrdata(gogo, &backend_ptrdata))
+ return false;
+
+ int64_t ptrsize;
+ if (!this->needs_gcprog(gogo, &ptrsize, &backend_ptrdata))
+ {
+ *pptrdata = backend_ptrdata;
+ return true;
+ }
+
+ GCProg prog;
+ prog.set_from(gogo, this, ptrsize, 0);
+ int64_t offset = prog.bit_index() * ptrsize;
+
+ go_assert(offset >= backend_ptrdata);
+ *pptrdata = offset;
+ return true;
+}
+
// Default function to export a type.
void
@@ -3008,10 +3787,6 @@ class Error_type : public Type
{ go_assert(saw_errors()); }
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
- { go_assert(saw_errors()); }
-
- void
do_mangled_name(Gogo*, std::string* ret) const
{ ret->push_back('E'); }
};
@@ -3050,10 +3825,6 @@ class Void_type : public Type
{ }
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
- { }
-
- void
do_mangled_name(Gogo*, std::string* ret) const
{ ret->push_back('v'); }
};
@@ -3092,9 +3863,6 @@ class Boolean_type : public Type
{ ret->append("bool"); }
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string* ret) const
{ ret->push_back('b'); }
};
@@ -3114,12 +3882,6 @@ Boolean_type::do_type_descriptor(Gogo* gogo, Named_type* name)
}
}
-// Update the offset of the GC symbol.
-
-void
-Boolean_type::do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
-{ this->advance_gc_offset(offset); }
-
Type*
Type::make_boolean_type()
{
@@ -3629,20 +4391,6 @@ String_type::do_reflection(Gogo*, std::string* ret) const
ret->append("string");
}
-// Generate GC symbol for strings.
-
-void
-String_type::do_gc_symbol(Gogo*, Expression_list** vals,
- Expression** offset, int)
-{
- Location bloc = Linemap::predeclared_location();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
- (*vals)->push_back(Expression::make_integer_ul(GC_STRING, uintptr_type,
- bloc));
- (*vals)->push_back(*offset);
- this->advance_gc_offset(offset);
-}
-
// Mangled name of a string type.
void
@@ -3714,10 +4462,6 @@ class Sink_type : public Type
{ go_unreachable(); }
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
- { go_unreachable(); }
-
- void
do_mangled_name(Gogo*, std::string*) const
{ go_unreachable(); }
};
@@ -4300,22 +5044,6 @@ Function_type::do_reflection(Gogo* gogo, std::string* ret) const
}
}
-// Generate GC symbol for a function type.
-
-void
-Function_type::do_gc_symbol(Gogo*, Expression_list** vals,
- Expression** offset, int)
-{
- Location bloc = Linemap::predeclared_location();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
-
- // We use GC_APTR here because we do not currently have a way to describe the
- // the type of the possible function closure. FIXME.
- (*vals)->push_back(Expression::make_integer_ul(GC_APTR, uintptr_type, bloc));
- (*vals)->push_back(*offset);
- this->advance_gc_offset(offset);
-}
-
// Mangled name.
void
@@ -4718,26 +5446,6 @@ Pointer_type::do_reflection(Gogo* gogo, std::string* ret) const
this->append_reflection(this->to_type_, gogo, ret);
}
-// Generate GC symbol for pointer types.
-
-void
-Pointer_type::do_gc_symbol(Gogo*, Expression_list** vals,
- Expression** offset, int)
-{
- Location loc = Linemap::predeclared_location();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
-
- unsigned long opval = this->to_type_->has_pointer() ? GC_PTR : GC_APTR;
- (*vals)->push_back(Expression::make_integer_ul(opval, uintptr_type, loc));
- (*vals)->push_back(*offset);
-
- if (this->to_type_->has_pointer())
- (*vals)->push_back(Expression::make_gc_symbol(this->to_type_));
- this->advance_gc_offset(offset);
-}
-
-// Mangled name.
-
void
Pointer_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
@@ -4816,10 +5524,6 @@ class Nil_type : public Type
{ go_unreachable(); }
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
- { go_unreachable(); }
-
- void
do_mangled_name(Gogo*, std::string* ret) const
{ ret->push_back('n'); }
};
@@ -4874,10 +5578,6 @@ class Call_multiple_result_type : public Type
{ go_assert(saw_errors()); }
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
- { go_unreachable(); }
-
- void
do_mangled_name(Gogo*, std::string*) const
{ go_assert(saw_errors()); }
@@ -5890,27 +6590,6 @@ Struct_type::do_reflection(Gogo* gogo, std::string* ret) const
ret->push_back('}');
}
-// Generate GC symbol for struct types.
-
-void
-Struct_type::do_gc_symbol(Gogo* gogo, Expression_list** vals,
- Expression** offset, int stack_size)
-{
- Location bloc = Linemap::predeclared_location();
- const Struct_field_list* sfl = this->fields();
- for (Struct_field_list::const_iterator p = sfl->begin();
- p != sfl->end();
- ++p)
- {
- Expression* field_offset =
- Expression::make_struct_field_offset(this, &*p);
- Expression* o =
- Expression::make_binary(OPERATOR_PLUS, *offset, field_offset, bloc);
- Type::gc_symbol(gogo, p->type(), vals, &o, stack_size);
- }
- this->advance_gc_offset(offset);
-}
-
// Mangled name.
void
@@ -6085,6 +6764,7 @@ Struct_type::can_write_to_c_header(
const Struct_field_list* fields = this->fields_;
if (fields == NULL || fields->empty())
return false;
+ int sinks = 0;
for (Struct_field_list::const_iterator p = fields->begin();
p != fields->end();
++p)
@@ -6093,7 +6773,11 @@ Struct_type::can_write_to_c_header(
return false;
if (!this->can_write_type_to_c_header(p->type(), requires, declare))
return false;
+ if (Gogo::message_name(p->field_name()) == "_")
+ sinks++;
}
+ if (sinks > 1)
+ return false;
return true;
}
@@ -6360,6 +7044,20 @@ Type::make_struct_type(Struct_field_list* fields,
// Class Array_type.
+// Store the length of an array as an int64_t into *PLEN. Return
+// false if the length can not be determined. This will assert if
+// called for a slice.
+
+bool
+Array_type::int_length(int64_t* plen)
+{
+ go_assert(this->length_ != NULL);
+ Numeric_constant nc;
+ if (!this->length_->numeric_constant_value(&nc))
+ return false;
+ return nc.to_memory_size(plen);
+}
+
// Whether two array types are identical.
bool
@@ -6504,6 +7202,38 @@ Array_type::do_verify()
return true;
}
+// Whether the type contains pointers. This is always true for a
+// slice. For an array it is true if the element type has pointers
+// and the length is greater than zero.
+
+bool
+Array_type::do_has_pointer() const
+{
+ if (this->length_ == NULL)
+ return true;
+ if (!this->element_type_->has_pointer())
+ return false;
+
+ Numeric_constant nc;
+ if (!this->length_->numeric_constant_value(&nc))
+ {
+ // Error reported elsewhere.
+ return false;
+ }
+
+ unsigned long val;
+ switch (nc.to_unsigned_long(&val))
+ {
+ case Numeric_constant::NC_UL_VALID:
+ return val > 0;
+ case Numeric_constant::NC_UL_BIG:
+ return true;
+ default:
+ // Error reported elsewhere.
+ return false;
+ }
+}
+
// Whether we can use memcmp to compare this array.
bool
@@ -7093,120 +7823,6 @@ Array_type::do_reflection(Gogo* gogo, std::string* ret) const
this->append_reflection(this->element_type_, gogo, ret);
}
-// GC Symbol construction for array types.
-
-void
-Array_type::do_gc_symbol(Gogo* gogo, Expression_list** vals,
- Expression** offset, int stack_size)
-{
- if (this->length_ == NULL)
- this->slice_gc_symbol(gogo, vals, offset, stack_size);
- else
- this->array_gc_symbol(gogo, vals, offset, stack_size);
-}
-
-// Generate the GC Symbol for a slice.
-
-void
-Array_type::slice_gc_symbol(Gogo* gogo, Expression_list** vals,
- Expression** offset, int)
-{
- Location bloc = Linemap::predeclared_location();
-
- // Differentiate between slices with zero-length and non-zero-length values.
- Type* element_type = this->element_type();
- int64_t element_size;
- bool ok = element_type->backend_type_size(gogo, &element_size);
- if (!ok) {
- go_assert(saw_errors());
- element_size = 4;
- }
-
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
- unsigned long opval = element_size == 0 ? GC_APTR : GC_SLICE;
- (*vals)->push_back(Expression::make_integer_ul(opval, uintptr_type, bloc));
- (*vals)->push_back(*offset);
-
- if (element_size != 0 && ok)
- (*vals)->push_back(Expression::make_gc_symbol(element_type));
- this->advance_gc_offset(offset);
-}
-
-// Generate the GC symbol for an array.
-
-void
-Array_type::array_gc_symbol(Gogo* gogo, Expression_list** vals,
- Expression** offset, int stack_size)
-{
- Location bloc = Linemap::predeclared_location();
-
- Numeric_constant nc;
- unsigned long bound;
- if (!this->length_->numeric_constant_value(&nc)
- || nc.to_unsigned_long(&bound) == Numeric_constant::NC_UL_NOTINT)
- {
- go_assert(saw_errors());
- return;
- }
-
- Btype* pbtype = gogo->backend()->pointer_type(gogo->backend()->void_type());
- int64_t pwidth = gogo->backend()->type_size(pbtype);
- int64_t iwidth;
- bool ok = this->backend_type_size(gogo, &iwidth);
- if (!ok)
- {
- go_assert(saw_errors());
- iwidth = 4;
- }
-
- Type* element_type = this->element_type();
- if (bound < 1 || !element_type->has_pointer())
- this->advance_gc_offset(offset);
- else if (ok && (bound == 1 || iwidth <= 4 * pwidth))
- {
- for (unsigned int i = 0; i < bound; ++i)
- Type::gc_symbol(gogo, element_type, vals, offset, stack_size);
- }
- else
- {
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
-
- if (stack_size < GC_STACK_CAPACITY)
- {
- (*vals)->push_back(Expression::make_integer_ul(GC_ARRAY_START,
- uintptr_type, bloc));
- (*vals)->push_back(*offset);
- Expression* uintptr_len =
- Expression::make_cast(uintptr_type, this->length_, bloc);
- (*vals)->push_back(uintptr_len);
-
- Expression* width =
- Expression::make_type_info(element_type,
- Expression::TYPE_INFO_SIZE);
- (*vals)->push_back(width);
-
- Expression* offset2 = Expression::make_integer_ul(0, uintptr_type,
- bloc);
-
- Type::gc_symbol(gogo, element_type, vals, &offset2, stack_size + 1);
- (*vals)->push_back(Expression::make_integer_ul(GC_ARRAY_NEXT,
- uintptr_type, bloc));
- }
- else
- {
- (*vals)->push_back(Expression::make_integer_ul(GC_REGION,
- uintptr_type, bloc));
- (*vals)->push_back(*offset);
-
- Expression* width =
- Expression::make_type_info(this, Expression::TYPE_INFO_SIZE);
- (*vals)->push_back(width);
- (*vals)->push_back(Expression::make_gc_symbol(this));
- }
- this->advance_gc_offset(offset);
- }
-}
-
// Mangled name.
void
@@ -7832,21 +8448,6 @@ Map_type::do_reflection(Gogo* gogo, std::string* ret) const
this->append_reflection(this->val_type_, gogo, ret);
}
-// Generate GC symbol for a map.
-
-void
-Map_type::do_gc_symbol(Gogo*, Expression_list** vals,
- Expression** offset, int)
-{
- // TODO(cmang): Generate GC data for the Map elements.
- Location bloc = Linemap::predeclared_location();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
-
- (*vals)->push_back(Expression::make_integer_ul(GC_APTR, uintptr_type, bloc));
- (*vals)->push_back(*offset);
- this->advance_gc_offset(offset);
-}
-
// Mangled name for a map.
void
@@ -8017,28 +8618,6 @@ Channel_type::do_reflection(Gogo* gogo, std::string* ret) const
this->append_reflection(this->element_type_, gogo, ret);
}
-// Generate GC symbol for channels.
-
-void
-Channel_type::do_gc_symbol(Gogo*, Expression_list** vals,
- Expression** offset, int)
-{
- Location bloc = Linemap::predeclared_location();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
-
- (*vals)->push_back(Expression::make_integer_ul(GC_CHAN_PTR, uintptr_type,
- bloc));
- (*vals)->push_back(*offset);
-
- Type* unsafeptr_type = Type::make_pointer_type(Type::make_void_type());
- Expression* type_descriptor =
- Expression::make_type_descriptor(this, bloc);
- type_descriptor =
- Expression::make_unsafe_cast(unsafeptr_type, type_descriptor, bloc);
- (*vals)->push_back(type_descriptor);
- this->advance_gc_offset(offset);
-}
-
// Mangled name.
void
@@ -8975,21 +9554,6 @@ Interface_type::do_reflection(Gogo* gogo, std::string* ret) const
ret->append("}");
}
-// Generate GC symbol for interface types.
-
-void
-Interface_type::do_gc_symbol(Gogo*, Expression_list** vals,
- Expression** offset, int)
-{
- Location bloc = Linemap::predeclared_location();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
-
- unsigned long opval = this->is_empty() ? GC_EFACE : GC_IFACE;
- (*vals)->push_back(Expression::make_integer_ul(opval, uintptr_type, bloc));
- (*vals)->push_back(*offset);
- this->advance_gc_offset(offset);
-}
-
// Mangled name.
void
@@ -10464,20 +11028,6 @@ Named_type::append_reflection_type_name(Gogo* gogo, bool use_alias,
ret->append(Gogo::unpack_hidden_name(this->named_object_->name()));
}
-// Generate GC symbol for named types.
-
-void
-Named_type::do_gc_symbol(Gogo* gogo, Expression_list** vals,
- Expression** offset, int stack)
-{
- if (!this->seen_)
- {
- this->seen_ = true;
- Type::gc_symbol(gogo, this->real_type(), vals, offset, stack);
- this->seen_ = false;
- }
-}
-
// Get the mangled name.
void
diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h
index 47a70fc..e0fcf0c 100644
--- a/gcc/go/gofrontend/types.h
+++ b/gcc/go/gofrontend/types.h
@@ -89,28 +89,6 @@ static const int RUNTIME_TYPE_KIND_DIRECT_IFACE = (1 << 5);
static const int RUNTIME_TYPE_KIND_GC_PROG = (1 << 6);
static const int RUNTIME_TYPE_KIND_NO_POINTERS = (1 << 7);
-// GC instruction opcodes. These must match the values in libgo/runtime/mgc0.h.
-enum GC_Opcode
-{
- GC_END = 0, // End of object, loop or subroutine.
- GC_PTR, // A typed pointer.
- GC_APTR, // Pointer to an arbitrary object.
- GC_ARRAY_START, // Start an array with a fixed length.
- GC_ARRAY_NEXT, // The next element of an array.
- GC_CALL, // Call a subroutine.
- GC_CHAN_PTR, // Go channel.
- GC_STRING, // Go string.
- GC_EFACE, // interface{}.
- GC_IFACE, // interface{...}.
- GC_SLICE, // Go slice.
- GC_REGION, // A region/part of the current object.
-
- GC_NUM_INSTR // Number of instruction opcodes
-};
-
-// The GC Stack Capacity must match the value in libgo/runtime/mgc0.h.
-static const int GC_STACK_CAPACITY = 8;
-
// To build the complete list of methods for a named type we need to
// gather all methods from anonymous fields. Those methods may
// require an arbitrary set of indirections and field offsets. There
@@ -944,6 +922,15 @@ class Type
Bexpression*
gc_symbol_pointer(Gogo* gogo);
+ // Return whether this type needs a garbage collection program.
+ // Sets *PTRSIZE and *PTRDATA.
+ bool
+ needs_gcprog(Gogo*, int64_t* ptrsize, int64_t* ptrdata);
+
+ // Return a ptrmask variable for this type.
+ Bvariable*
+ gc_ptrmask_var(Gogo*, int64_t ptrsize, int64_t ptrdata);
+
// Return the type reflection string for this type.
std::string
reflection(Gogo*) const;
@@ -971,6 +958,20 @@ class Type
bool
backend_type_field_align(Gogo*, int64_t* palign);
+ // Determine the ptrdata size for the backend version of this type:
+ // the length of the prefix of the type that can contain a pointer
+ // value. If it can be determined, set *PPTRDATA to the value in
+ // bytes and return true. Otherwise, return false.
+ bool
+ backend_type_ptrdata(Gogo*, int64_t* pptrdata);
+
+ // Determine the ptrdata size that we are going to set in the type
+ // descriptor. This is normally the same as backend_type_ptrdata,
+ // but differs if we use a gcprog for an array. The arguments and
+ // results are as for backend_type_ptrdata.
+ bool
+ descriptor_ptrdata(Gogo*, int64_t* pptrdata);
+
// Whether the backend size is known.
bool
is_backend_type_size_known(Gogo*);
@@ -1044,9 +1045,6 @@ class Type
do_type_descriptor(Gogo*, Named_type* name) = 0;
virtual void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int) = 0;
-
- virtual void
do_reflection(Gogo*, std::string*) const = 0;
virtual void
@@ -1101,22 +1099,6 @@ class Type
type_descriptor_constructor(Gogo*, int runtime_type_kind, Named_type*,
const Methods*, bool only_value_methods);
- // Generate the GC symbol for this TYPE. VALS is the data so far in this
- // symbol; extra values will be appended in do_gc_symbol. OFFSET is the
- // offset into the symbol where the GC data is located. STACK_SIZE is the
- // size of the GC stack when dealing with array types.
- static void
- gc_symbol(Gogo*, Type* type, Expression_list** vals, Expression** offset,
- int stack_size);
-
- // Build a composite literal for the GC symbol of this type.
- Expression*
- gc_symbol_constructor(Gogo*);
-
- // Advance the OFFSET of the GC symbol by the size of this type.
- void
- advance_gc_offset(Expression** offset);
-
// For the benefit of child class reflection string generation.
void
append_reflection(const Type* type, Gogo* gogo, std::string* ret) const
@@ -1194,6 +1176,11 @@ class Type
static GC_symbol_vars gc_symbol_vars;
+ // Map ptrmask symbol names to the ptrmask variable.
+ typedef Unordered_map(std::string, Bvariable*) GC_gcbits_vars;
+
+ static GC_gcbits_vars gc_gcbits_vars;
+
// Build the GC symbol for this type.
void
make_gc_symbol_var(Gogo*);
@@ -1210,6 +1197,11 @@ class Type
bool
type_descriptor_defined_elsewhere(Named_type* name, const Package** package);
+ // Make a composite literal for the garbage collection program for
+ // this type.
+ Expression*
+ gcprog_constructor(Gogo*, int64_t ptrsize, int64_t ptrdata);
+
// Build the hash and equality type functions for a type which needs
// specific functions.
void
@@ -1606,10 +1598,6 @@ protected:
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
- { this->advance_gc_offset(offset); }
-
- void
do_mangled_name(Gogo*, std::string*) const;
private:
@@ -1696,10 +1684,6 @@ class Float_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
- { this->advance_gc_offset(offset); }
-
- void
do_mangled_name(Gogo*, std::string*) const;
private:
@@ -1778,10 +1762,6 @@ class Complex_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
- { this->advance_gc_offset(offset); }
-
- void
do_mangled_name(Gogo*, std::string*) const;
private:
@@ -1836,9 +1816,6 @@ class String_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string* ret) const;
private:
@@ -1994,9 +1971,6 @@ class Function_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -2120,9 +2094,6 @@ class Pointer_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -2442,9 +2413,6 @@ class Struct_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -2519,6 +2487,12 @@ class Array_type : public Type
length() const
{ return this->length_; }
+ // Store the length as an int64_t into *PLEN. Return false if the
+ // length can not be determined. This will assert if called for a
+ // slice.
+ bool
+ int_length(int64_t* plen);
+
// Whether this type is identical with T.
bool
is_identical(const Array_type* t, bool errors_are_identical) const;
@@ -2583,10 +2557,7 @@ class Array_type : public Type
do_verify();
bool
- do_has_pointer() const
- {
- return this->length_ == NULL || this->element_type_->has_pointer();
- }
+ do_has_pointer() const;
bool
do_compare_is_identity(Gogo*);
@@ -2614,9 +2585,6 @@ class Array_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -2632,12 +2600,6 @@ class Array_type : public Type
Expression*
slice_type_descriptor(Gogo*, Named_type*);
- void
- slice_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
- array_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
// The type of elements of the array.
Type* element_type_;
// The number of elements. This may be NULL.
@@ -2737,9 +2699,6 @@ class Map_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -2852,9 +2811,6 @@ class Channel_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -2999,9 +2955,6 @@ class Interface_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
-
- void
do_mangled_name(Gogo*, std::string*) const;
void
@@ -3307,10 +3260,6 @@ class Named_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo* gogo, Expression_list** vals, Expression** offset,
- int stack);
-
- void
do_mangled_name(Gogo*, std::string* ret) const;
void
@@ -3467,11 +3416,6 @@ class Forward_declaration_type : public Type
do_reflection(Gogo*, std::string*) const;
void
- do_gc_symbol(Gogo* gogo, Expression_list** vals, Expression** offset,
- int stack_size)
- { Type::gc_symbol(gogo, this->real_type(), vals, offset, stack_size); }
-
- void
do_mangled_name(Gogo*, std::string* ret) const;
void
diff --git a/gcc/go/gofrontend/wb.cc b/gcc/go/gofrontend/wb.cc
new file mode 100644
index 0000000..5a49961
--- /dev/null
+++ b/gcc/go/gofrontend/wb.cc
@@ -0,0 +1,465 @@
+// wb.cc -- Add write barriers as needed.
+
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go-system.h"
+
+#include "go-c.h"
+#include "go-diagnostics.h"
+#include "operator.h"
+#include "lex.h"
+#include "types.h"
+#include "expressions.h"
+#include "statements.h"
+#include "runtime.h"
+#include "gogo.h"
+
+// Mark variables whose addresses are taken. This has to be done
+// before the write barrier pass and after the escape analysis pass.
+// It would be nice to do this elsewhere but there isn't an obvious
+// place.
+
+class Mark_address_taken : public Traverse
+{
+ public:
+ Mark_address_taken(Gogo* gogo)
+ : Traverse(traverse_expressions),
+ gogo_(gogo)
+ { }
+
+ int
+ expression(Expression**);
+
+ private:
+ Gogo* gogo_;
+};
+
+// Mark variable addresses taken.
+
+int
+Mark_address_taken::expression(Expression** pexpr)
+{
+ Expression* expr = *pexpr;
+ Unary_expression* ue = expr->unary_expression();
+ if (ue != NULL)
+ ue->check_operand_address_taken(this->gogo_);
+ return TRAVERSE_CONTINUE;
+}
+
+// Add write barriers to the IR. This are required by the concurrent
+// garbage collector. A write barrier is needed for any write of a
+// pointer into memory controlled by the garbage collector. Write
+// barriers are not required for writes to local variables that live
+// on the stack. Write barriers are only required when the runtime
+// enables them, which can be checked using a run time check on
+// runtime.writeBarrier.enabled.
+//
+// Essentially, for each assignment A = B, where A is or contains a
+// pointer, and where A is not, or at any rate may not be, a stack
+// variable, we rewrite it into
+// if runtime.writeBarrier.enabled {
+// typedmemmove(typeof(A), &A, &B)
+// } else {
+// A = B
+// }
+//
+// The test of runtime.writeBarrier.Enabled is implemented by treating
+// the variable as a *uint32, and testing *runtime.writeBarrier != 0.
+// This is compatible with the definition in the runtime package.
+//
+// For types that are pointer shared (pointers, maps, chans, funcs),
+// we replaced the call to typedmemmove with writebarrierptr(&A, B).
+// As far as the GC is concerned, all pointers are the same, so it
+// doesn't need the type descriptor.
+//
+// There are possible optimizations that are not implemented.
+//
+// runtime.writeBarrier can only change when the goroutine is
+// preempted, which in practice means when a call is made into the
+// runtime package, so we could optimize by only testing it once
+// between function calls.
+//
+// A slice could be handled with a call to writebarrierptr plus two
+// integer moves.
+
+// Traverse the IR adding write barriers.
+
+class Write_barriers : public Traverse
+{
+ public:
+ Write_barriers(Gogo* gogo)
+ : Traverse(traverse_functions | traverse_variables | traverse_statements),
+ gogo_(gogo), function_(NULL)
+ { }
+
+ int
+ function(Named_object*);
+
+ int
+ variable(Named_object*);
+
+ int
+ statement(Block*, size_t* pindex, Statement*);
+
+ private:
+ // General IR.
+ Gogo* gogo_;
+ // Current function.
+ Function* function_;
+};
+
+// Traverse a function. Just record it for later.
+
+int
+Write_barriers::function(Named_object* no)
+{
+ go_assert(this->function_ == NULL);
+ this->function_ = no->func_value();
+ int t = this->function_->traverse(this);
+ this->function_ = NULL;
+
+ if (t == TRAVERSE_EXIT)
+ return t;
+ return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Insert write barriers for a global variable: ensure that variable
+// initialization is handled correctly. This is rarely needed, since
+// we currently don't enable background GC until after all global
+// variables are initialized. But we do need this if an init function
+// calls runtime.GC.
+
+int
+Write_barriers::variable(Named_object* no)
+{
+ // We handle local variables in the variable declaration statement.
+ // We only have to handle global variables here.
+ if (!no->is_variable())
+ return TRAVERSE_CONTINUE;
+ Variable* var = no->var_value();
+ if (!var->is_global())
+ return TRAVERSE_CONTINUE;
+
+ // Nothing to do if there is no initializer.
+ Expression* init = var->init();
+ if (init == NULL)
+ return TRAVERSE_CONTINUE;
+
+ // Nothing to do for variables that do not contain any pointers.
+ if (!var->type()->has_pointer())
+ return TRAVERSE_CONTINUE;
+
+ // Nothing to do if the initializer is static.
+ init = Expression::make_cast(var->type(), init, var->location());
+ if (!var->has_pre_init() && init->is_static_initializer())
+ return TRAVERSE_CONTINUE;
+
+ // Otherwise change the initializer into a pre_init assignment
+ // statement with a write barrier.
+
+ // We can't check for a dependency of the variable on itself after
+ // we make this change, because the preinit statement will always
+ // depend on the variable (since it assigns to it). So check for a
+ // self-dependency now.
+ this->gogo_->check_self_dep(no);
+
+ // Replace the initializer.
+ Location loc = init->location();
+ Expression* ref = Expression::make_var_reference(no, loc);
+ ref->var_expression()->set_in_lvalue_pos();
+
+ Statement_inserter inserter(this->gogo_, var);
+ Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter,
+ ref, init, loc);
+
+ var->add_preinit_statement(this->gogo_, s);
+ var->clear_init();
+
+ return TRAVERSE_CONTINUE;
+}
+
+// Insert write barriers for statements.
+
+int
+Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
+{
+ switch (s->classification())
+ {
+ default:
+ break;
+
+ case Statement::STATEMENT_VARIABLE_DECLARATION:
+ {
+ Variable_declaration_statement* vds =
+ s->variable_declaration_statement();
+ Named_object* no = vds->var();
+ Variable* var = no->var_value();
+
+ // We may need to emit a write barrier for the initialization
+ // of the variable.
+
+ // Nothing to do for a variable with no initializer.
+ Expression* init = var->init();
+ if (init == NULL)
+ break;
+
+ // Nothing to do if the variable is not in the heap. Only
+ // local variables get declaration statements, and local
+ // variables on the stack do not require write barriers.
+ if (!var->is_in_heap())
+ break;
+
+ // Nothing to do if the variable does not contain any pointers.
+ if (!var->type()->has_pointer())
+ break;
+
+ // Otherwise initialize the variable with a write barrier.
+
+ Function* function = this->function_;
+ Location loc = init->location();
+ Statement_inserter inserter(block, pindex);
+
+ // Insert the variable declaration statement with no
+ // initializer, so that the variable exists.
+ var->clear_init();
+ inserter.insert(s);
+
+ // Create a statement that initializes the variable with a
+ // write barrier.
+ Expression* ref = Expression::make_var_reference(no, loc);
+ Statement* assign = this->gogo_->assign_with_write_barrier(function,
+ block,
+ &inserter,
+ ref, init,
+ loc);
+
+ // Replace the old variable declaration statement with the new
+ // initialization.
+ block->replace_statement(*pindex, assign);
+ }
+ break;
+
+ case Statement::STATEMENT_ASSIGNMENT:
+ {
+ Assignment_statement* as = s->assignment_statement();
+ Expression* lhs = as->lhs();
+ Expression* rhs = as->rhs();
+
+ // We may need to emit a write barrier for the assignment.
+
+ if (!this->gogo_->assign_needs_write_barrier(lhs))
+ break;
+
+ // Change the assignment to use a write barrier.
+ Function* function = this->function_;
+ Location loc = as->location();
+ Statement_inserter inserter = Statement_inserter(block, pindex);
+ Statement* assign = this->gogo_->assign_with_write_barrier(function,
+ block,
+ &inserter,
+ lhs, rhs,
+ loc);
+ block->replace_statement(*pindex, assign);
+ }
+ break;
+ }
+
+ return TRAVERSE_CONTINUE;
+}
+
+// The write barrier pass.
+
+void
+Gogo::add_write_barriers()
+{
+ Mark_address_taken mat(this);
+ this->traverse(&mat);
+
+ Write_barriers wb(this);
+ this->traverse(&wb);
+}
+
+// Return the runtime.writeBarrier variable.
+
+Named_object*
+Gogo::write_barrier_variable()
+{
+ static Named_object* write_barrier_var;
+ if (write_barrier_var == NULL)
+ {
+ Location bloc = Linemap::predeclared_location();
+
+ // We pretend that writeBarrier is a uint32, so that we do a
+ // 32-bit load. That is what the gc toolchain does.
+ Type* uint32_type = Type::lookup_integer_type("uint32");
+ Variable* var = new Variable(uint32_type, NULL, true, false, false,
+ bloc);
+
+ bool add_to_globals;
+ Package* package = this->add_imported_package("runtime", "_", false,
+ "runtime", "runtime",
+ bloc, &add_to_globals);
+ write_barrier_var = Named_object::make_variable("writeBarrier",
+ package, var);
+ }
+
+ return write_barrier_var;
+}
+
+// Return whether an assignment that sets LHS needs a write barrier.
+
+bool
+Gogo::assign_needs_write_barrier(Expression* lhs)
+{
+ // Nothing to do if the variable does not contain any pointers.
+ if (!lhs->type()->has_pointer())
+ return false;
+
+ // Nothing to do for an assignment to a temporary.
+ if (lhs->temporary_reference_expression() != NULL)
+ return false;
+
+ // Nothing to do for an assignment to a sink.
+ if (lhs->is_sink_expression())
+ return false;
+
+ // Nothing to do for an assignment to a local variable that is not
+ // on the heap.
+ Var_expression* ve = lhs->var_expression();
+ if (ve != NULL)
+ {
+ Named_object* no = ve->named_object();
+ if (no->is_variable())
+ {
+ Variable* var = no->var_value();
+ if (!var->is_global() && !var->is_in_heap())
+ return false;
+ }
+ else if (no->is_result_variable())
+ {
+ Result_variable* rvar = no->result_var_value();
+ if (!rvar->is_in_heap())
+ return false;
+ }
+ }
+
+ // Write barrier needed in other cases.
+ return true;
+}
+
+// Return a statement that sets LHS to RHS using a write barrier.
+// ENCLOSING is the enclosing block.
+
+Statement*
+Gogo::assign_with_write_barrier(Function* function, Block* enclosing,
+ Statement_inserter* inserter, Expression* lhs,
+ Expression* rhs, Location loc)
+{
+ if (function != NULL
+ && ((function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0
+ || (function->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0))
+ go_error_at(loc, "write barrier prohibited");
+
+ Type* type = lhs->type();
+ go_assert(type->has_pointer());
+
+ Expression* addr;
+ if (lhs->unary_expression() != NULL
+ && lhs->unary_expression()->op() == OPERATOR_MULT)
+ addr = lhs->unary_expression()->operand();
+ else
+ {
+ addr = Expression::make_unary(OPERATOR_AND, lhs, loc);
+ addr->unary_expression()->set_does_not_escape();
+ }
+ Temporary_statement* lhs_temp = Statement::make_temporary(NULL, addr, loc);
+ inserter->insert(lhs_temp);
+ lhs = Expression::make_temporary_reference(lhs_temp, loc);
+
+ if (!Type::are_identical(type, rhs->type(), false, NULL)
+ && rhs->type()->interface_type() != NULL
+ && !rhs->is_variable())
+ {
+ // May need a temporary for interface conversion.
+ Temporary_statement* temp = Statement::make_temporary(NULL, rhs, loc);
+ inserter->insert(temp);
+ rhs = Expression::make_temporary_reference(temp, loc);
+ }
+ rhs = Expression::convert_for_assignment(this, type, rhs, loc);
+ Temporary_statement* rhs_temp = NULL;
+ if (!rhs->is_variable() && !rhs->is_constant())
+ {
+ rhs_temp = Statement::make_temporary(NULL, rhs, loc);
+ inserter->insert(rhs_temp);
+ rhs = Expression::make_temporary_reference(rhs_temp, loc);
+ }
+
+ Expression* indir = Expression::make_unary(OPERATOR_MULT, lhs, loc);
+ Statement* assign = Statement::make_assignment(indir, rhs, loc);
+
+ lhs = Expression::make_temporary_reference(lhs_temp, loc);
+ if (rhs_temp != NULL)
+ rhs = Expression::make_temporary_reference(rhs_temp, loc);
+
+ Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
+ lhs = Expression::make_unsafe_cast(unsafe_ptr_type, lhs, loc);
+
+ Expression* call;
+ switch (type->base()->classification())
+ {
+ default:
+ go_unreachable();
+
+ case Type::TYPE_ERROR:
+ return assign;
+
+ case Type::TYPE_POINTER:
+ case Type::TYPE_FUNCTION:
+ case Type::TYPE_MAP:
+ case Type::TYPE_CHANNEL:
+ // These types are all represented by a single pointer.
+ call = Runtime::make_call(Runtime::WRITEBARRIERPTR, loc, 2, lhs, rhs);
+ break;
+
+ case Type::TYPE_STRING:
+ case Type::TYPE_STRUCT:
+ case Type::TYPE_ARRAY:
+ case Type::TYPE_INTERFACE:
+ {
+ rhs = Expression::make_unary(OPERATOR_AND, rhs, loc);
+ rhs->unary_expression()->set_does_not_escape();
+ call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,
+ Expression::make_type_descriptor(type, loc),
+ lhs, rhs);
+ }
+ break;
+ }
+
+ return this->check_write_barrier(enclosing, assign,
+ Statement::make_statement(call, false));
+}
+
+// Return a statement that tests whether write barriers are enabled
+// and executes either the efficient code or the write barrier
+// function call, depending.
+
+Statement*
+Gogo::check_write_barrier(Block* enclosing, Statement* without,
+ Statement* with)
+{
+ Location loc = without->location();
+ Named_object* wb = this->write_barrier_variable();
+ Expression* ref = Expression::make_var_reference(wb, loc);
+ Expression* zero = Expression::make_integer_ul(0, ref->type(), loc);
+ Expression* cond = Expression::make_binary(OPERATOR_EQEQ, ref, zero, loc);
+
+ Block* then_block = new Block(enclosing, loc);
+ then_block->add_statement(without);
+
+ Block* else_block = new Block(enclosing, loc);
+ else_block->add_statement(with);
+
+ return Statement::make_if_statement(cond, then_block, else_block, loc);
+}
diff --git a/gcc/testsuite/go.test/test/slice3.go b/gcc/testsuite/go.test/test/slice3.go
index 3cf34b5..8a184d1 100644
--- a/gcc/testsuite/go.test/test/slice3.go
+++ b/gcc/testsuite/go.test/test/slice3.go
@@ -1,6 +1,6 @@
// runoutput
-// Copyright 2013 The Go Authors. All rights reserved.
+// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -19,10 +19,10 @@ var bout *bufio.Writer
func main() {
bout = bufio.NewWriter(os.Stdout)
-
+
fmt.Fprintf(bout, "%s", programTop)
fmt.Fprintf(bout, "func main() {\n")
-
+
index := []string{
"0",
"1",
@@ -38,7 +38,7 @@ func main() {
"v10",
"v20",
}
-
+
parse := func(s string) (n int, isconst bool) {
if s == "vminus1" {
return -1, false
@@ -69,7 +69,7 @@ func main() {
iconst && kconst && iv > kv,
iconst && base == "array" && iv > Cap,
jconst && base == "array" && jv > Cap,
- kconst && base == "array" && kv > Cap:
+ kconst && base == "array" && kv > Cap:
continue
}
@@ -82,7 +82,7 @@ func main() {
xlen = jv - iv
xcap = kv - iv
}
- fmt.Fprintf(bout, "\tcheckSlice(%q, func() []byte { return %s }, %d, %d, %d)\n", expr, expr, xbase, xlen, xcap)
+ fmt.Fprintf(bout, "\tcheckSlice(%q, func() []byte { return %s }, %d, %d, %d)\n", expr, expr, xbase, xlen, xcap)
}
}
}
@@ -147,9 +147,13 @@ func checkSlice(desc string, f func() []byte, xbase, xlen, xcap int) {
println(desc, "=", base, len, cap, "want panic")
return
}
- if base != uintptr(xbase) || len != uintptr(xlen) || cap != uintptr(xcap) {
+ if cap != 0 && base != uintptr(xbase) || base >= 10 || len != uintptr(xlen) || cap != uintptr(xcap) {
notOK()
- println(desc, "=", base, len, cap, "want", xbase, xlen, xcap)
+ if cap == 0 {
+ println(desc, "=", base, len, cap, "want", "0-9", xlen, xcap)
+ } else {
+ println(desc, "=", base, len, cap, "want", xbase, xlen, xcap)
+ }
}
}