Commit f1d2ac4f by Chris Manghane Committed by Ian Lance Taylor

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
parent d2e4feca
2014-09-03 Chris Manghane <cmang@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.
2014-08-08 Ian Lance Taylor <iant@google.com>
* go-gcc.cc (Gcc_backend::compound_statement): Don't return
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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
......
......@@ -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 Expression
{ 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
......
......@@ -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<Bexpression*>& 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;
......
......@@ -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;
......@@ -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 Type
do_reflection(Gogo*, std::string*) const;
void
do_gc_symbol(Gogo* gogo, Expression_list** vals, Expression** offset,
int stack_size)
{ Type::gc_symbol(gogo, this->real_type(), vals, offset, stack_size); }
void
do_mangled_name(Gogo*, std::string* ret) const;
void
......
......@@ -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)
}
......
......@@ -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
......
......@@ -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;
......
......@@ -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 */
......
......@@ -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();
......
......@@ -800,7 +800,7 @@ uintptr runtime_memlimit(void);
enum
{
UseSpanType = 0,
UseSpanType = 1,
};
#define runtime_setitimer setitimer
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment