This patch from Chris Manghane adds support for precise type information to the gccgo garbage collector. This is only the first step, as the compiler does not pass the type information to the allocator in most cases. I think that right now the precise type information will only be available for slices. Also the precise type information is only ever available on the heap, not on the stack, and not yet for global variables. Still, this is a key first step.
Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline. Ian 2014-09-03 Chris Manghane <cm...@google.com> * 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.
Index: gcc/go/go-gcc.cc =================================================================== --- gcc/go/go-gcc.cc (revision 214893) +++ gcc/go/go-gcc.cc (revision 214894) @@ -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(Bfunctio 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); } Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 214893) +++ gcc/go/gofrontend/gogo.cc (revision 214894) @@ -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, st { 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; Index: gcc/go/gofrontend/types.h =================================================================== --- gcc/go/gofrontend/types.h (revision 214893) +++ gcc/go/gofrontend/types.h (revision 214894) @@ -83,6 +83,28 @@ static const int RUNTIME_TYPE_KIND_UNSAF 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; @@ -996,6 +1022,9 @@ 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 @@ -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. @@ -1507,6 +1565,10 @@ 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: @@ -1584,6 +1646,10 @@ 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: @@ -1653,6 +1719,10 @@ 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: @@ -1702,6 +1772,9 @@ 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: @@ -1837,6 +1910,9 @@ 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 @@ -1953,6 +2029,9 @@ 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 @@ -2251,6 +2330,9 @@ 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 @@ -2393,6 +2475,9 @@ 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 @@ -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. @@ -2485,6 +2576,9 @@ 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 @@ -2571,6 +2665,9 @@ 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 @@ -2704,6 +2801,9 @@ 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 @@ -2989,6 +3089,10 @@ 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 @@ -3133,6 +3237,11 @@ class Forward_declaration_type : public 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 Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 214893) +++ gcc/go/gofrontend/expressions.h (revision 214894) @@ -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. @@ -1513,6 +1519,10 @@ class Binary_expression : public Express { 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; bool Index: gcc/go/gofrontend/backend.h =================================================================== --- gcc/go/gofrontend/backend.h (revision 214893) +++ gcc/go/gofrontend/backend.h (revision 214894) @@ -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 Index: gcc/go/gofrontend/types.cc =================================================================== --- gcc/go/gofrontend/types.cc (revision 214893) +++ gcc/go/gofrontend/types.cc (revision 214894) @@ -36,7 +36,8 @@ get_backend_interface_fields(Gogo* gogo, // 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* gog 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, 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* 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)); @@ -2019,6 +2033,10 @@ Type::type_descriptor_constructor(Gogo* 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 ? name->reflection(gogo) @@ -2067,6 +2085,160 @@ Type::type_descriptor_constructor(Gogo* 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<GC_symbol_vars::iterator, bool> 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 @@ -2498,6 +2670,10 @@ 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'); } }; @@ -2536,6 +2712,10 @@ 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'); } }; @@ -2574,6 +2754,9 @@ 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'); } }; @@ -2593,6 +2776,12 @@ Boolean_type::do_type_descriptor(Gogo* g } } +// 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::s 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 @@ -3173,6 +3378,10 @@ 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(); } }; @@ -3754,6 +3963,25 @@ Function_type::do_reflection(Gogo* gogo, } } +// 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, 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 @@ -4236,6 +4484,10 @@ 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'); } }; @@ -4293,6 +4545,10 @@ class Call_multiple_result_type : public { 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, s 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, st 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: 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, 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 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, st 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 Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 214893) +++ gcc/go/gofrontend/expressions.cc (revision 214894) @@ -3440,6 +3440,9 @@ class Unsafe_type_conversion_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_tr 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(Transla && !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* t 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 Index: libgo/runtime/go-unsafe-pointer.c =================================================================== --- libgo/runtime/go-unsafe-pointer.c (revision 214893) +++ libgo/runtime/go-unsafe-pointer.c (revision 214894) @@ -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 __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_unsaf __go_type_hash_identity, /* __equalfn */ __go_type_equal_identity, + /* __gc */ + unsafe_Pointer_gc, /* __reflection */ &preflection_string, /* __uncommon */ Index: libgo/runtime/go-type.h =================================================================== --- libgo/runtime/go-type.h (revision 214893) +++ libgo/runtime/go-type.h (revision 214894) @@ -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; Index: libgo/runtime/runtime.h =================================================================== --- libgo/runtime/runtime.h (revision 214893) +++ libgo/runtime/runtime.h (revision 214894) @@ -800,7 +800,7 @@ uintptr runtime_memlimit(void); enum { - UseSpanType = 0, + UseSpanType = 1, }; #define runtime_setitimer setitimer Index: libgo/runtime/mgc0.c =================================================================== --- libgo/runtime/mgc0.c (revision 214893) +++ libgo/runtime/mgc0.c (revision 214894) @@ -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 keepworkin 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 keepworkin 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 keepworkin 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 keepworkin 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 keepworkin 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 keepworkin 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 keepworkin 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 keepworkin // 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 keepworkin // 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 keepworkin // 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 keepworkin // 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 keepworkin // 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 keepworkin 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 keepworkin // 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 keepworkin 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(); Index: libgo/go/runtime/type.go =================================================================== --- libgo/go/runtime/type.go (revision 214893) +++ libgo/go/runtime/type.go (revision 214894) @@ -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 Index: libgo/go/reflect/type.go =================================================================== --- libgo/go/reflect/type.go (revision 214893) +++ libgo/go/reflect/type.go (revision 214894) @@ -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) }