From f1d2ac4f84b4b5354779b3b0a0e125b8302ebb49 Mon Sep 17 00:00:00 2001 From: Chris Manghane Date: Wed, 3 Sep 2014 22:56:09 +0000 Subject: [PATCH] compiler: Add precise type information on the heap. * go-gcc.cc (Gcc_backend::implicit_variable): Remove init parameter. Add is_hidden parameter. (Gcc_backend::implicit_variable_set_init): New method. (Gcc_backend::implicit_variable_reference): New method. From-SVN: r214894 --- gcc/go/ChangeLog | 7 + gcc/go/go-gcc.cc | 91 +++++- gcc/go/gofrontend/backend.h | 55 +++- gcc/go/gofrontend/expressions.cc | 88 +++++- gcc/go/gofrontend/expressions.h | 10 + gcc/go/gofrontend/gogo.cc | 12 +- gcc/go/gofrontend/types.cc | 470 +++++++++++++++++++++++++++++- gcc/go/gofrontend/types.h | 109 +++++++ libgo/go/reflect/type.go | 45 ++- libgo/go/runtime/type.go | 3 +- libgo/runtime/go-type.h | 5 +- libgo/runtime/go-unsafe-pointer.c | 10 + libgo/runtime/mgc0.c | 79 ++--- libgo/runtime/runtime.h | 2 +- 14 files changed, 891 insertions(+), 95 deletions(-) diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index f7c44c5f112..9a759ac0af8 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,10 @@ +2014-09-03 Chris Manghane + + * go-gcc.cc (Gcc_backend::implicit_variable): Remove init + parameter. Add is_hidden parameter. + (Gcc_backend::implicit_variable_set_init): New method. + (Gcc_backend::implicit_variable_reference): New method. + 2014-08-08 Ian Lance Taylor * go-gcc.cc (Gcc_backend::compound_statement): Don't return diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index 059706e039f..6bac84f2565 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -389,9 +389,16 @@ class Gcc_backend : public Backend Location, Bstatement**); Bvariable* - implicit_variable(const std::string&, Btype*, Bexpression*, bool, bool, + implicit_variable(const std::string&, Btype*, bool, bool, bool, size_t); + void + implicit_variable_set_init(Bvariable*, const std::string&, Btype*, + bool, bool, bool, Bexpression*); + + Bvariable* + implicit_variable_reference(const std::string&, Btype*); + Bvariable* immutable_struct(const std::string&, bool, bool, Btype*, Location); @@ -2505,45 +2512,101 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock, Bvariable* Gcc_backend::implicit_variable(const std::string& name, Btype* type, - Bexpression* init, bool is_constant, + bool is_hidden, bool is_constant, bool is_common, size_t alignment) { tree type_tree = type->get_tree(); - tree init_tree; - if (init == NULL) - init_tree = NULL_TREE; - else - init_tree = init->get_tree(); - if (type_tree == error_mark_node || init_tree == error_mark_node) + if (type_tree == error_mark_node) return this->error_variable(); tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, get_identifier_from_string(name), type_tree); DECL_EXTERNAL(decl) = 0; - TREE_PUBLIC(decl) = 0; + TREE_PUBLIC(decl) = !is_hidden; TREE_STATIC(decl) = 1; + TREE_USED(decl) = 1; DECL_ARTIFICIAL(decl) = 1; if (is_common) { DECL_COMMON(decl) = 1; - TREE_PUBLIC(decl) = 1; - gcc_assert(init_tree == NULL_TREE); + + // When the initializer for one implicit_variable refers to another, + // it needs to know the visibility of the referenced struct so that + // compute_reloc_for_constant will return the right value. On many + // systems calling make_decl_one_only will mark the decl as weak, + // which will change the return value of compute_reloc_for_constant. + // We can't reliably call make_decl_one_only yet, because we don't + // yet know the initializer. This issue doesn't arise in C because + // Go initializers, unlike C initializers, can be indirectly + // recursive. To ensure that compute_reloc_for_constant computes + // the right value if some other initializer refers to this one, we + // mark this symbol as weak here. We undo that below in + // immutable_struct_set_init before calling mark_decl_one_only. + DECL_WEAK(decl) = 1; } - else if (is_constant) + if (is_constant) { TREE_READONLY(decl) = 1; TREE_CONSTANT(decl) = 1; } - DECL_INITIAL(decl) = init_tree; - if (alignment != 0) { DECL_ALIGN(decl) = alignment * BITS_PER_UNIT; DECL_USER_ALIGN(decl) = 1; } + go_preserve_from_gc(decl); + return new Bvariable(decl); +} + +// Set the initalizer for a variable created by implicit_variable. +// This is where we finish compiling the variable. + +void +Gcc_backend::implicit_variable_set_init(Bvariable* var, const std::string&, + Btype*, bool, bool, bool is_common, + Bexpression* init) +{ + tree decl = var->get_tree(); + tree init_tree; + if (init == NULL) + init_tree = NULL_TREE; + else + init_tree = init->get_tree(); + if (decl == error_mark_node || init_tree == error_mark_node) + return; + + DECL_INITIAL(decl) = init_tree; + + // Now that DECL_INITIAL is set, we can't call make_decl_one_only. + // See the comment where DECL_WEAK is set in implicit_variable. + if (is_common) + { + DECL_WEAK(decl) = 0; + make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl)); + } + + resolve_unique_section(decl, 2, 1); + rest_of_decl_compilation(decl, 1, 0); +} +// Return a reference to an implicit variable defined in another package. + +Bvariable* +Gcc_backend::implicit_variable_reference(const std::string& name, Btype* btype) +{ + tree type_tree = btype->get_tree(); + if (type_tree == error_mark_node) + return this->error_variable(); + + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier_from_string(name), type_tree); + DECL_EXTERNAL(decl) = 0; + TREE_PUBLIC(decl) = 1; + TREE_STATIC(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + go_preserve_from_gc(decl); return new Bvariable(decl); } diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index 323ac2e75b5..98c36c1f5f8 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -545,24 +545,55 @@ class Backend Bstatement** pstatement) = 0; // Create an implicit variable that is compiler-defined. This is - // used when generating GC root variables, when storing the values - // of a slice constructor, and for the zero value of types. NAME is - // the name of the variable, either gc# for GC roots or C# for slice - // initializers. TYPE is the type of the implicit variable with an - // initial value INIT. IS_CONSTANT is true if the implicit variable - // should be treated like it is immutable. For slice initializers, - // if the values must be copied to the heap, the variable - // IS_CONSTANT. IS_COMMON is true if the implicit variable should + // used when generating GC data and roots, when storing the values + // of a slice constructor, and for the zero value of types. This returns a + // Bvariable because it corresponds to an initialized variable in C. + // + // NAME is the name to use for the initialized variable this will create. + // + // TYPE is the type of the implicit variable. + // + // IS_HIDDEN will be true if the descriptor should only be visible + // within the current object. + // + // IS_CONSTANT is true if the implicit variable should be treated like it is + // immutable. For slice initializers, if the values must be copied to the + // heap, the variable IS_CONSTANT. + // + // IS_COMMON is true if the implicit variable should // be treated as a common variable (multiple definitions with // different sizes permitted in different object files, all merged // into the largest definition at link time); this will be true for - // the zero value. If IS_COMMON is true, INIT will be NULL, and the - // variable should be initialized to all zeros. If ALIGNMENT is not - // zero, it is the desired alignment of the variable. + // the zero value. IS_HIDDEN and IS_COMMON will never both be true. + // + // If ALIGNMENT is not zero, it is the desired alignment of the variable. virtual Bvariable* - implicit_variable(const std::string& name, Btype* type, Bexpression* init, + implicit_variable(const std::string& name, Btype* type, bool is_hidden, bool is_constant, bool is_common, size_t alignment) = 0; + + // Set the initial value of a variable created by implicit_variable. + // This must be called even if there is no initializer, i.e., INIT is NULL. + // The NAME, TYPE, IS_HIDDEN, IS_CONSTANT, and IS_COMMON parameters are + // the same ones passed to implicit_variable. INIT will be a composite + // literal of type TYPE. It will not contain any function calls or anything + // else that can not be put into a read-only data section. + // It may contain the address of variables created by implicit_variable. + // + // If IS_COMMON is true, INIT will be NULL, and the + // variable should be initialized to all zeros. + virtual void + implicit_variable_set_init(Bvariable*, const std::string& name, Btype* type, + bool is_hidden, bool is_constant, bool is_common, + Bexpression* init) = 0; + + // Create a reference to a named implicit variable defined in some other + // package. This will be a variable created by a call to implicit_variable + // with the same NAME and TYPE and with IS_COMMON passed as false. This + // corresponds to an extern global variable in C. + virtual Bvariable* + implicit_variable_reference(const std::string& name, Btype* type) = 0; + // Create a named immutable initialized data structure. This is // used for type descriptors, map descriptors, and function // descriptors. This returns a Bvariable because it corresponds to diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 6414136fed0..df1650a172a 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3440,6 +3440,9 @@ class Unsafe_type_conversion_expression : public Expression int do_traverse(Traverse* traverse); + bool + do_is_immutable() const; + Type* do_type() { return this->type_; } @@ -3480,6 +3483,27 @@ Unsafe_type_conversion_expression::do_traverse(Traverse* traverse) return TRAVERSE_CONTINUE; } +// Return whether an unsafe type conversion is immutable. + +bool +Unsafe_type_conversion_expression::do_is_immutable() const +{ + Type* type = this->type_; + Type* expr_type = this->expr_->type(); + + if (type->interface_type() != NULL + || expr_type->interface_type() != NULL) + return false; + + if (!this->expr_->is_immutable()) + return false; + + if (Type::are_convertible(type, expr_type, NULL)) + return true; + + return type->is_basic_type() && expr_type->is_basic_type(); +} + // Convert to backend representation. Bexpression* @@ -4115,8 +4139,11 @@ Unary_expression::do_get_backend(Translate_context* context) && !context->is_const()); } Bvariable* implicit = - gogo->backend()->implicit_variable(buf, btype, bexpr, copy_to_heap, + gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap, false, 0); + gogo->backend()->implicit_variable_set_init(implicit, buf, btype, + true, copy_to_heap, false, + bexpr); bexpr = gogo->backend()->var_expression(implicit, loc); } else if ((this->expr_->is_composite_literal() @@ -13987,6 +14014,65 @@ Expression::make_type_descriptor(Type* type, Location location) return new Type_descriptor_expression(type, location); } +// An expression which evaluates to a pointer to the Garbage Collection symbol +// of a type. + +class GC_symbol_expression : public Expression +{ + public: + GC_symbol_expression(Type* type) + : Expression(EXPRESSION_GC_SYMBOL, Linemap::predeclared_location()), + type_(type) + {} + + protected: + Type* + do_type() + { return Type::make_pointer_type(Type::make_void_type()); } + + bool + do_is_immutable() const + { return true; } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return this; } + + Bexpression* + do_get_backend(Translate_context* context) + { return this->type_->gc_symbol_pointer(context->gogo()); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type which this gc symbol describes. + Type* type_; +}; + +// Dump ast representation for a gc symbol expression. + +void +GC_symbol_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "gcdata("; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ")"; +} + +// Make a gc symbol expression. + +Expression* +Expression::make_gc_symbol(Type* type) +{ + return new GC_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 diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 0ce6f22706a..77153dbd58f 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -103,6 +103,7 @@ class Expression EXPRESSION_HEAP, EXPRESSION_RECEIVE, EXPRESSION_TYPE_DESCRIPTOR, + EXPRESSION_GC_SYMBOL, EXPRESSION_TYPE_INFO, EXPRESSION_SLICE_INFO, EXPRESSION_SLICE_VALUE, @@ -349,6 +350,11 @@ class Expression static Expression* make_type_descriptor(Type* type, Location); + // Make an expression which evaluates to the address of the gc + // symbol for TYPE. + static Expression* + make_gc_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. @@ -1512,6 +1518,10 @@ class Binary_expression : public Expression do_is_constant() const { return this->left_->is_constant() && this->right_->is_constant(); } + bool + do_is_immutable() const + { return this->left_->is_immutable() && this->right_->is_immutable(); } + bool do_numeric_constant_value(Numeric_constant*) const; diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 654b6c3df93..dcc2ae64961 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -655,9 +655,13 @@ Gogo::backend_zero_value() Btype* barray_type = this->backend()->array_type(bbtype_type, blength); - return this->backend()->implicit_variable(this->zero_value_->name(), - barray_type, NULL, true, true, - this->zero_value_align_); + std::string zname = this->zero_value_->name(); + Bvariable* zvar = + this->backend()->implicit_variable(zname, barray_type, false, + true, true, this->zero_value_align_); + this->backend()->implicit_variable_set_init(zvar, zname, barray_type, + false, true, true, NULL); + return zvar; } // Add statements to INIT_STMTS which run the initialization @@ -6837,8 +6841,10 @@ Named_object::get_backend(Gogo* gogo, std::vector& const_decls, { named_type-> type_descriptor_pointer(gogo, Linemap::predeclared_location()); + named_type->gc_symbol_pointer(gogo); Type* pn = Type::make_pointer_type(named_type); pn->type_descriptor_pointer(gogo, Linemap::predeclared_location()); + pn->gc_symbol_pointer(gogo); } } break; diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 395b5e55081..302faeee353 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -36,7 +36,8 @@ get_backend_interface_fields(Gogo* gogo, Interface_type* type, // Class Type. Type::Type(Type_classification classification) - : classification_(classification), btype_(NULL), type_descriptor_var_(NULL) + : classification_(classification), btype_(NULL), type_descriptor_var_(NULL), + gc_symbol_var_(NULL) { } @@ -1236,7 +1237,7 @@ Type::make_type_descriptor_var(Gogo* gogo) Type::type_descriptor_vars.insert(std::make_pair(this, bvnull)); if (!ins.second) { - // We've already build a type descriptor for this type. + // We've already built a type descriptor for this type. this->type_descriptor_var_ = ins.first->second; return; } @@ -1405,6 +1406,18 @@ 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. @@ -1519,14 +1532,15 @@ Type::make_type_descriptor_type() // The type descriptor type. Struct_type* type_descriptor_type = - Type::make_builtin_struct_type(11, - "Kind", uint8_type, + Type::make_builtin_struct_type(12, + "kind", uint8_type, "align", uint8_type, "fieldAlign", uint8_type, "size", uintptr_type, "hash", uint32_type, "hashfn", uintptr_type, "equalfn", uintptr_type, + "gc", unsafe_pointer_type, "string", pointer_string_type, "", pointer_uncommon_type, "ptrToThis", @@ -1973,7 +1987,7 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, if (!this->has_pointer()) runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS; Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("Kind")); + go_assert(p->is_field_name("kind")); mpz_t iv; mpz_init_set_ui(iv, runtime_type_kind); vals->push_back(Expression::make_integer(&iv, p->type(), bloc)); @@ -2018,6 +2032,10 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, vals->push_back(Expression::make_func_code_reference(hash_fn, bloc)); vals->push_back(Expression::make_func_code_reference(equal_fn, bloc)); + ++p; + go_assert(p->is_field_name("gc")); + vals->push_back(Expression::make_gc_symbol(this)); + ++p; go_assert(p->is_field_name("string")); Expression* s = Expression::make_string((name != NULL @@ -2067,6 +2085,160 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, return Expression::make_struct_composite_literal(td_type, vals, bloc); } +// Return a pointer to the Garbage Collection information for this type. + +Bexpression* +Type::gc_symbol_pointer(Gogo* gogo) +{ + Type* t = this->forwarded(); + if (t->named_type() != NULL && t->named_type()->is_alias()) + t = t->named_type()->real_type(); + if (t->gc_symbol_var_ == NULL) + { + t->make_gc_symbol_var(gogo); + go_assert(t->gc_symbol_var_ != NULL); + } + Location bloc = Linemap::predeclared_location(); + Bexpression* var_expr = + gogo->backend()->var_expression(t->gc_symbol_var_, bloc); + return gogo->backend()->address_expression(var_expr, bloc); +} + +// A mapping from unnamed types to GC symbol variables. + +Type::GC_symbol_vars Type::gc_symbol_vars; + +// Build the GC symbol for this type. + +void +Type::make_gc_symbol_var(Gogo* gogo) +{ + go_assert(this->gc_symbol_var_ == NULL); + + Named_type* nt = this->named_type(); + + // We can have multiple instances of unnamed types and similar to type + // descriptors, we only want to the emit the GC data once, so we use a + // hash table. + Bvariable** phash = NULL; + if (nt == NULL) + { + Bvariable* bvnull = NULL; + std::pair ins = + Type::gc_symbol_vars.insert(std::make_pair(this, bvnull)); + if (!ins.second) + { + // We've already built a gc symbol for this type. + this->gc_symbol_var_ = ins.first->second; + return; + } + phash = &ins.first->second; + } + + 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); + Btype* sym_btype = sym_init->type()->get_backend(gogo); + + // If the type descriptor for this type is defined somewhere else, so is the + // GC symbol. + const Package* dummy; + if (this->type_descriptor_defined_elsewhere(nt, &dummy)) + { + this->gc_symbol_var_ = + gogo->backend()->implicit_variable_reference(sym_name, sym_btype); + if (phash != NULL) + *phash = this->gc_symbol_var_; + return; + } + + // See if this gc symbol can appear in multiple packages. + bool is_common = false; + if (nt != NULL) + { + // We create the symbol for a builtin type whenever we need + // it. + is_common = nt->is_builtin(); + } + else + { + // This is an unnamed type. The descriptor could be defined in + // any package where it is needed, and the linker will pick one + // descriptor to keep. + is_common = true; + } + + // 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. + this->gc_symbol_var_ = + gogo->backend()->implicit_variable(sym_name, sym_btype, false, true, is_common, 0); + if (phash != NULL) + *phash = this->gc_symbol_var_; + + Translate_context context(gogo, NULL, NULL, NULL); + context.set_is_const(); + Bexpression* sym_binit = sym_init->get_backend(&context); + gogo->backend()->implicit_variable_set_init(this->gc_symbol_var_, sym_name, + sym_btype, false, true, is_common, + sym_binit); +} + +// Return an array literal for the Garbage Collection information for this type. + +Expression* +Type::gc_symbol_constructor(Gogo* gogo) +{ + Location bloc = Linemap::predeclared_location(); + + // 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; + + Type* uintptr_t = Type::lookup_integer_type("uintptr"); + // width + vals->push_back(Expression::make_type_info(this, + Expression::TYPE_INFO_SIZE)); + + mpz_t off; + mpz_init_set_ui(off, 0UL); + Expression* offset = Expression::make_integer(&off, uintptr_t, bloc); + mpz_clear(off); + + this->do_gc_symbol(gogo, &vals, &offset, 0); + + mpz_t end; + mpz_init_set_ui(end, GC_END); + vals->push_back(Expression::make_integer(&end, uintptr_t, bloc)); + mpz_clear(end); + + mpz_t lenval; + mpz_init_set_ui(lenval, vals->size() + 1); + Expression* len = Expression::make_integer(&lenval, NULL, bloc); + mpz_clear(lenval); + + Array_type* gc_symbol_type = Type::make_array_type(uintptr_t, len); + return Expression::make_array_composite_literal(gc_symbol_type, vals, bloc); +} + +// Advance the OFFSET of the GC symbol by this type's width. + +void +Type::advance_gc_offset(Expression** offset) +{ + if (this->is_error_type()) + return; + + 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); +} + // Return a composite literal for the uncommon type information for // this type. UNCOMMON_STRUCT_TYPE is the type of the uncommon type // struct. If name is not NULL, it is the name of the type. If @@ -2497,6 +2669,10 @@ class Error_type : public Type do_reflection(Gogo*, std::string*) const { 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'); } @@ -2535,6 +2711,10 @@ class Void_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 { ret->push_back('v'); } @@ -2573,6 +2753,9 @@ class Boolean_type : public Type do_reflection(Gogo*, std::string* ret) const { 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'); } @@ -2593,6 +2776,12 @@ 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() { @@ -3102,6 +3291,22 @@ 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"); + mpz_t opval; + mpz_init_set_ui(opval, GC_STRING); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc)); + mpz_clear(opval); + (*vals)->push_back(*offset); + this->advance_gc_offset(offset); +} + // Mangled name of a string type. void @@ -3172,6 +3377,10 @@ class Sink_type : public Type do_reflection(Gogo*, std::string*) const { go_unreachable(); } + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int) + { go_unreachable(); } + void do_mangled_name(Gogo*, std::string*) const { go_unreachable(); } @@ -3754,6 +3963,25 @@ 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. + mpz_t opval; + mpz_init_set_ui(opval, GC_APTR); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc)); + mpz_clear(opval); + (*vals)->push_back(*offset); + this->advance_gc_offset(offset); +} + // Mangled name. void @@ -4156,6 +4384,26 @@ 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"); + + mpz_t opval; + mpz_init_set_ui(opval, this->to_type_->has_pointer() ? GC_PTR : GC_APTR); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, loc)); + mpz_clear(opval); + (*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 @@ -4235,6 +4483,10 @@ class Nil_type : public Type do_reflection(Gogo*, std::string*) const { 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'); } @@ -4292,6 +4544,10 @@ class Call_multiple_result_type : public Type do_reflection(Gogo*, std::string*) const { 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()); } @@ -5319,6 +5575,27 @@ 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 @@ -6204,6 +6481,115 @@ 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(); + Btype* ebtype = element_type->get_backend(gogo); + size_t element_size = gogo->backend()->type_size(ebtype); + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + mpz_t opval; + mpz_init_set_ui(opval, element_size == 0 ? GC_APTR : GC_SLICE); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc)); + mpz_clear(opval); + (*vals)->push_back(*offset); + + if (element_size != 0) + (*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()); + + Btype* pbtype = gogo->backend()->pointer_type(gogo->backend()->void_type()); + size_t pwidth = gogo->backend()->type_size(pbtype); + size_t iwidth = gogo->backend()->type_size(this->get_backend(gogo)); + + Type* element_type = this->element_type(); + if (bound < 1 || !element_type->has_pointer()) + this->advance_gc_offset(offset); + else if (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"); + + mpz_t op; + if (stack_size < GC_STACK_CAPACITY) + { + mpz_init_set_ui(op, GC_ARRAY_START); + (*vals)->push_back(Expression::make_integer(&op, uintptr_type, bloc)); + mpz_clear(op); + (*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); + + mpz_t zero; + mpz_init_set_ui(zero, 0UL); + Expression* offset2 = + Expression::make_integer(&zero, uintptr_type, bloc); + mpz_clear(zero); + + Type::gc_symbol(gogo, element_type, vals, &offset2, stack_size + 1); + mpz_init_set_ui(op, GC_ARRAY_NEXT); + (*vals)->push_back(Expression::make_integer(&op, uintptr_type, bloc)); + } + else + { + mpz_init_set_ui(op, GC_REGION); + (*vals)->push_back(Expression::make_integer(&op, 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)); + } + mpz_clear(op); + this->advance_gc_offset(offset); + } +} + // Mangled name. void @@ -6513,6 +6899,24 @@ 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"); + + mpz_t opval; + mpz_init_set_ui(opval, GC_APTR); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc)); + mpz_clear(opval); + (*vals)->push_back(*offset); + this->advance_gc_offset(offset); +} + // Mangled name for a map. void @@ -6686,6 +7090,30 @@ 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"); + + mpz_t opval; + mpz_init_set_ui(opval, GC_CHAN_PTR); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc)); + mpz_clear(opval); + (*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 @@ -7574,6 +8002,24 @@ 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"); + + mpz_t opval; + mpz_init_set_ui(opval, this->is_empty() ? GC_EFACE : GC_IFACE); + (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, + bloc)); + mpz_clear(opval); + (*vals)->push_back(*offset); + this->advance_gc_offset(offset); +} + // Mangled name. void @@ -8810,6 +9256,20 @@ Named_type::do_reflection(Gogo* gogo, std::string* ret) const 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 6fa65133a0e..447861c4846 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -83,6 +83,28 @@ static const int RUNTIME_TYPE_KIND_UNSAFE_POINTER = 26; 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 @@ -911,6 +933,10 @@ class Type Bexpression* type_descriptor_pointer(Gogo* gogo, Location); + // Build the Garbage Collection symbol for this type. Return a pointer to it. + Bexpression* + gc_symbol_pointer(Gogo* gogo); + // Return the type reflection string for this type. std::string reflection(Gogo*) const; @@ -995,6 +1021,9 @@ class Type virtual Expression* 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; @@ -1050,6 +1079,22 @@ 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 @@ -1126,6 +1171,16 @@ class Type void make_type_descriptor_var(Gogo*); + // Map unnamed types to type descriptor decls. + typedef Unordered_map_hash(const Type*, Bvariable*, Type_hash_identical, + Type_identical) GC_symbol_vars; + + static GC_symbol_vars gc_symbol_vars; + + // Build the GC symbol for this type. + void + make_gc_symbol_var(Gogo*); + // Return the name of the type descriptor variable. If NAME is not // NULL, it is the name to use. std::string @@ -1253,6 +1308,9 @@ class Type // The type descriptor for this type. This starts out as NULL and // is filled in as needed. Bvariable* type_descriptor_var_; + // The GC symbol for this type. This starts out as NULL and + // is filled in as needed. + Bvariable* gc_symbol_var_; }; // Type hash table operations. @@ -1506,6 +1564,10 @@ protected: void 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; @@ -1583,6 +1645,10 @@ class Float_type : public Type void 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; @@ -1652,6 +1718,10 @@ class Complex_type : public Type void 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; @@ -1701,6 +1771,9 @@ class String_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string* ret) const; @@ -1836,6 +1909,9 @@ class Function_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -1952,6 +2028,9 @@ class Pointer_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -2250,6 +2329,9 @@ class Struct_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -2392,6 +2474,9 @@ class Array_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -2408,6 +2493,12 @@ 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. @@ -2484,6 +2575,9 @@ class Map_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -2570,6 +2664,9 @@ class Channel_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -2703,6 +2800,9 @@ class Interface_type : public Type void do_reflection(Gogo*, std::string*) const; + void + do_gc_symbol(Gogo*, Expression_list**, Expression**, int); + void do_mangled_name(Gogo*, std::string*) const; @@ -2988,6 +3088,10 @@ class Named_type : public Type void 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; @@ -3132,6 +3236,11 @@ class Forward_declaration_type : public Type void 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; diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index 74cf2946a01..91697c4b56b 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -255,6 +255,7 @@ type rtype struct { hashfn uintptr // hash function code equalfn uintptr // equality function code + gc unsafe.Pointer // garbage collection data string *string // string form; unnecessary but undeniably useful *uncommonType // (relatively) uncommon fields ptrToThis *rtype // type for pointer to this type, if used in binary or has methods @@ -1130,6 +1131,18 @@ func (t *rtype) ptrTo() *rtype { p.zero = unsafe.Pointer(&make([]byte, p.size)[0]) p.elem = t + if t.kind&kindNoPointers != 0 { + p.gc = unsafe.Pointer(&ptrDataGCProg) + } else { + p.gc = unsafe.Pointer(&ptrGC{ + width: p.size, + op: _GC_PTR, + off: 0, + elemgc: t.gc, + end: _GC_END, + }) + } + q := canonicalize(&p.rtype) p = (*ptrType)(unsafe.Pointer(q.(*rtype))) @@ -1471,8 +1484,16 @@ func ChanOf(dir ChanDir, t Type) Type { ch.ptrToThis = nil ch.zero = unsafe.Pointer(&make([]byte, ch.size)[0]) + ch.gc = unsafe.Pointer(&chanGC{ + width: ch.size, + op: _GC_CHAN_PTR, + off: 0, + typ: &ch.rtype, + end: _GC_END, + }) + // INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong. - //ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END}) + // ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END}) return cachePut(ckey, &ch.rtype) } @@ -1524,10 +1545,13 @@ func MapOf(key, elem Type) Type { // width: unsafe.Sizeof(uintptr(0)), // op: _GC_PTR, // off: 0, - // elemgc: mt.hmap.gc, + // elemgc: nil, // end: _GC_END, // }) + // TODO(cmang): Generate GC data for Map elements. + mt.gc = unsafe.Pointer(&ptrDataGCProg) + // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues // fail when mt.gc is wrong. //mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END}) @@ -1593,8 +1617,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype { // Take the GC program for "t" and append it to the GC program "gc". func appendGCProgram(gc []uintptr, t *rtype) []uintptr { - // p := t.gc - var p unsafe.Pointer + p := t.gc p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size loop: for { @@ -1707,8 +1730,20 @@ func SliceOf(t Type) Type { slice.ptrToThis = nil slice.zero = unsafe.Pointer(&make([]byte, slice.size)[0]) + if typ.size == 0 { + slice.gc = unsafe.Pointer(&sliceEmptyGCProg) + } else { + slice.gc = unsafe.Pointer(&sliceGC{ + width: slice.size, + op: _GC_SLICE, + off: 0, + elemgc: typ.gc, + end: _GC_END, + }) + } + // INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong. - //slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END}) + // slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END}) return cachePut(ckey, &slice.rtype) } diff --git a/libgo/go/runtime/type.go b/libgo/go/runtime/type.go index 1211f222575..a5ed8af7a85 100644 --- a/libgo/go/runtime/type.go +++ b/libgo/go/runtime/type.go @@ -15,7 +15,7 @@ package runtime import "unsafe" type rtype struct { - Kind uint8 + kind uint8 align uint8 fieldAlign uint8 size uintptr @@ -24,6 +24,7 @@ type rtype struct { hashfn func(unsafe.Pointer, uintptr) uintptr equalfn func(unsafe.Pointer, unsafe.Pointer, uintptr) bool + gc unsafe.Pointer string *string *uncommonType ptrToThis *rtype diff --git a/libgo/runtime/go-type.h b/libgo/runtime/go-type.h index 51c2355772d..74e83400598 100644 --- a/libgo/runtime/go-type.h +++ b/libgo/runtime/go-type.h @@ -59,7 +59,7 @@ struct String; #define GO_CODE_MASK 0x7f /* For each Go type the compiler constructs one of these structures. - This is used for type reflectin, interfaces, maps, and reference + This is used for type reflection, interfaces, maps, and reference counting. */ struct __go_type_descriptor @@ -93,6 +93,9 @@ struct __go_type_descriptor size of this type, and returns whether the values are equal. */ _Bool (*__equalfn) (const void *, const void *, uintptr_t); + /* The garbage collection data. */ + const uintptr *__gc; + /* A string describing this type. This is only used for debugging. */ const struct String *__reflection; diff --git a/libgo/runtime/go-unsafe-pointer.c b/libgo/runtime/go-unsafe-pointer.c index b71804ac741..67b2999df5d 100644 --- a/libgo/runtime/go-unsafe-pointer.c +++ b/libgo/runtime/go-unsafe-pointer.c @@ -8,6 +8,7 @@ #include "runtime.h" #include "go-type.h" +#include "mgc0.h" /* A pointer with a zero value. */ static void *zero_pointer; @@ -20,6 +21,9 @@ static void *zero_pointer; extern const struct __go_type_descriptor unsafe_Pointer __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer"); +extern const uintptr unsafe_Pointer_gc[] + __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer$gc"); + /* Used to determine the field alignment. */ struct field_align { @@ -35,6 +39,8 @@ static const String reflection_string = sizeof REFLECTION - 1 }; +const uintptr unsafe_Pointer_gc[] = {8, GC_APTR, 0, GC_END}; + const struct __go_type_descriptor unsafe_Pointer = { /* __code */ @@ -51,6 +57,8 @@ const struct __go_type_descriptor unsafe_Pointer = __go_type_hash_identity, /* __equalfn */ __go_type_equal_identity, + /* __gc */ + unsafe_Pointer_gc, /* __reflection */ &reflection_string, /* __uncommon */ @@ -94,6 +102,8 @@ const struct __go_ptr_type pointer_unsafe_Pointer = __go_type_hash_identity, /* __equalfn */ __go_type_equal_identity, + /* __gc */ + unsafe_Pointer_gc, /* __reflection */ &preflection_string, /* __uncommon */ diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index 2d6328fbac3..6864a833dc4 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -181,7 +181,7 @@ struct Finalizer FuncVal *fn; void *arg; const struct __go_func_type *ft; - const struct __go_ptr_type *ot; + const PtrType *ot; }; typedef struct FinBlock FinBlock; @@ -403,8 +403,6 @@ struct BufferList }; static BufferList bufferList[MaxGcproc]; -static Type *itabtype; - static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); // flushptrbuf moves data from the PtrTarget buffer to the work buffer. @@ -649,23 +647,22 @@ flushobjbuf(Scanbuf *sbuf) // Program that scans the whole block and treats every block element as a potential pointer static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR}; -#if 0 // Hchan program static uintptr chanProg[2] = {0, GC_CHAN}; -#endif // Local variables of a program fragment or loop typedef struct Frame Frame; struct Frame { uintptr count, elemsize, b; - uintptr *loop_or_ret; + const uintptr *loop_or_ret; }; // Sanity check for the derived type info objti. static void checkptr(void *obj, uintptr objti) { - uintptr type, tisize, i, x; + uintptr *pc1, type, tisize, i, j, x; + const uintptr *pc2; byte *objstart; Type *t; MSpan *s; @@ -703,9 +700,8 @@ checkptr(void *obj, uintptr objti) (runtime_strcmp((const char *)t->string->str, (const char*)"unsafe.Pointer") && // Runtime and gc think differently about closures. runtime_strstr((const char *)t->string->str, (const char*)"struct { F uintptr") != (const char *)t->string->str)) { -#if 0 pc1 = (uintptr*)objti; - pc2 = (uintptr*)t->gc; + pc2 = (const uintptr*)t->__gc; // A simple best-effort check until first GC_END. for(j = 1; pc1[j] != GC_END && pc2[j] != GC_END; j++) { if(pc1[j] != pc2[j]) { @@ -714,7 +710,6 @@ checkptr(void *obj, uintptr objti) runtime_throw("invalid gc type info"); } } -#endif } } @@ -728,11 +723,10 @@ static void scanblock(Workbuf *wbuf, bool keepworking) { byte *b, *arena_start, *arena_used; - uintptr n, i, end_b, elemsize, size, ti, objti, count, /* type, */ nobj; - uintptr *pc, precise_type, nominal_size; -#if 0 - uintptr *chan_ret, chancap; -#endif + uintptr n, i, end_b, elemsize, size, ti, objti, count, type, nobj; + uintptr precise_type, nominal_size; + const uintptr *pc, *chan_ret; + uintptr chancap; void *obj; const Type *t, *et; Slice *sliceptr; @@ -742,10 +736,8 @@ scanblock(Workbuf *wbuf, bool keepworking) Scanbuf sbuf; Eface *eface; Iface *iface; -#if 0 Hchan *chan; - ChanType *chantype; -#endif + const ChanType *chantype; Obj *wp; if(sizeof(Workbuf) % WorkbufSize != 0) @@ -782,11 +774,9 @@ scanblock(Workbuf *wbuf, bool keepworking) sbuf.nobj = nobj; // (Silence the compiler) -#if 0 chan = nil; chantype = nil; chan_ret = nil; -#endif goto next_block; @@ -800,7 +790,7 @@ scanblock(Workbuf *wbuf, bool keepworking) runtime_xadd64(&gcstats.obj.cnt, 1); } - if(ti != 0 && false) { + if(ti != 0) { if(Debug > 1) { runtime_printf("scanblock %p %D ti %p\n", b, (int64)n, ti); } @@ -826,11 +816,10 @@ scanblock(Workbuf *wbuf, bool keepworking) runtime_throw("invalid gc type info"); } } - } else if(UseSpanType && false) { + } else if(UseSpanType) { if(CollectStats) runtime_xadd64(&gcstats.obj.notype, 1); -#if 0 type = runtime_gettype(b); if(type != 0) { if(CollectStats) @@ -839,13 +828,13 @@ scanblock(Workbuf *wbuf, bool keepworking) t = (Type*)(type & ~(uintptr)(PtrSize-1)); switch(type & (PtrSize-1)) { case TypeInfo_SingleObject: - pc = (uintptr*)t->gc; + pc = (const uintptr*)t->__gc; precise_type = true; // type information about 'b' is precise stack_top.count = 1; stack_top.elemsize = pc[0]; break; case TypeInfo_Array: - pc = (uintptr*)t->gc; + pc = (const uintptr*)t->__gc; if(pc[0] == 0) goto next_block; precise_type = true; // type information about 'b' is precise @@ -855,7 +844,7 @@ scanblock(Workbuf *wbuf, bool keepworking) break; case TypeInfo_Chan: chan = (Hchan*)b; - chantype = (ChanType*)t; + chantype = (const ChanType*)t; chan_ret = nil; pc = chanProg; break; @@ -872,7 +861,6 @@ scanblock(Workbuf *wbuf, bool keepworking) if(Debug > 1) runtime_printf("scanblock %p %D unknown type\n", b, (int64)n); } -#endif } else { pc = defaultProg; if(Debug > 1) @@ -954,7 +942,7 @@ scanblock(Workbuf *wbuf, bool keepworking) // eface->__object if((byte*)eface->__object >= arena_start && (byte*)eface->__object < arena_used) { - if(t->__size <= sizeof(void*)) { + if(__go_is_pointer_type(t)) { if((t->__code & KindNoPointers)) continue; @@ -965,13 +953,11 @@ scanblock(Workbuf *wbuf, bool keepworking) // dgcsym1 in case TPTR32/case TPTR64. See rationale there. et = ((const PtrType*)t)->elem; if(!(et->__code & KindNoPointers)) - // objti = (uintptr)((const PtrType*)t)->elem->gc; - objti = 0; + objti = (uintptr)((const PtrType*)t)->elem->__gc; } } else { obj = eface->__object; - // objti = (uintptr)t->gc; - objti = 0; + objti = (uintptr)t->__gc; } } break; @@ -986,16 +972,15 @@ scanblock(Workbuf *wbuf, bool keepworking) // iface->tab if((byte*)iface->tab >= arena_start && (byte*)iface->tab < arena_used) { - *sbuf.ptr.pos++ = (PtrTarget){iface->tab, /* (uintptr)itabtype->gc */ 0}; + *sbuf.ptr.pos++ = (PtrTarget){iface->tab, 0}; if(sbuf.ptr.pos == sbuf.ptr.end) flushptrbuf(&sbuf); } // iface->data if((byte*)iface->__object >= arena_start && (byte*)iface->__object < arena_used) { - // t = iface->tab->type; - t = nil; - if(t->__size <= sizeof(void*)) { + t = (const Type*)iface->tab[0]; + if(__go_is_pointer_type(t)) { if((t->__code & KindNoPointers)) continue; @@ -1006,13 +991,11 @@ scanblock(Workbuf *wbuf, bool keepworking) // dgcsym1 in case TPTR32/case TPTR64. See rationale there. et = ((const PtrType*)t)->elem; if(!(et->__code & KindNoPointers)) - // objti = (uintptr)((const PtrType*)t)->elem->gc; - objti = 0; + objti = (uintptr)((const PtrType*)t)->elem->__gc; } } else { obj = iface->__object; - // objti = (uintptr)t->gc; - objti = 0; + objti = (uintptr)t->__gc; } } break; @@ -1092,7 +1075,7 @@ scanblock(Workbuf *wbuf, bool keepworking) // Stack push. *stack_ptr-- = stack_top; stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/}; - pc = (uintptr*)((byte*)pc + *(int32*)(pc+2)); // target of the CALL instruction + pc = (const uintptr*)((const byte*)pc + *(const int32*)(pc+2)); // target of the CALL instruction continue; case GC_REGION: @@ -1108,7 +1091,6 @@ scanblock(Workbuf *wbuf, bool keepworking) flushobjbuf(&sbuf); continue; -#if 0 case GC_CHAN_PTR: chan = *(Hchan**)(stack_top.b + pc[1]); if(Debug > 2 && chan != nil) @@ -1141,8 +1123,8 @@ scanblock(Workbuf *wbuf, bool keepworking) // in-use part of the circular buffer is scanned. // (Channel routines zero the unused part, so the current // code does not lead to leaks, it's just a little inefficient.) - *sbuf.obj.pos++ = (Obj){(byte*)chan+runtime_Hchansize, chancap*chantype->elem->size, - (uintptr)chantype->elem->gc | PRECISE | LOOP}; + *sbuf.obj.pos++ = (Obj){(byte*)chan+runtime_Hchansize, chancap*chantype->elem->__size, + (uintptr)chantype->elem->__gc | PRECISE | LOOP}; if(sbuf.obj.pos == sbuf.obj.end) flushobjbuf(&sbuf); } @@ -1151,7 +1133,6 @@ scanblock(Workbuf *wbuf, bool keepworking) goto next_block; pc = chan_ret; continue; -#endif default: runtime_printf("runtime: invalid GC instruction %p at %p\n", pc[0], pc); @@ -1828,7 +1809,7 @@ runtime_MSpan_Sweep(MSpan *s) } // State of background sweep. -// Pretected by gclock. +// Protected by gclock. static struct { G* g; @@ -2260,12 +2241,6 @@ gc(struct gc_args *args) work.markfor = runtime_parforalloc(MaxGcproc); m->locks--; - if(itabtype == nil) { - // get C pointer to the Go type "itab" - // runtime_gc_itab_ptr(&eface); - // itabtype = ((PtrType*)eface.__type_descriptor)->elem; - } - t1 = 0; if(runtime_debug.gctrace) t1 = runtime_nanotime(); diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 6650be1b3d3..c96290a0b06 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -800,7 +800,7 @@ uintptr runtime_memlimit(void); enum { - UseSpanType = 0, + UseSpanType = 1, }; #define runtime_setitimer setitimer -- 2.30.2