diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/go/ChangeLog | 11 | ||||
-rw-r--r-- | gcc/go/Make-lang.in | 3 | ||||
-rw-r--r-- | gcc/go/go-backend.c | 2 | ||||
-rw-r--r-- | gcc/go/go-gcc.cc | 18 | ||||
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/backend.h | 5 | ||||
-rw-r--r-- | gcc/go/gofrontend/export.cc | 12 | ||||
-rw-r--r-- | gcc/go/gofrontend/export.h | 6 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 395 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 40 | ||||
-rw-r--r-- | gcc/go/gofrontend/go.cc | 3 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 184 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 39 | ||||
-rw-r--r-- | gcc/go/gofrontend/import-archive.cc | 375 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 13 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 1264 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 136 | ||||
-rw-r--r-- | gcc/go/gofrontend/wb.cc | 465 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/slice3.go | 20 |
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) + } } } |