From d09afc2efb50eeb3a1e55cbe05f42307ca0369b1 Mon Sep 17 00:00:00 2001 From: Jan Ziak <0xe2.0x9a.0x9b@gmail.com> Date: Wed, 12 Sep 2012 12:08:27 -0400 Subject: [PATCH] gc: generate garbage collection info for types R=rsc, nigeltao, minux.ma CC=golang-dev https://golang.org/cl/6290043 --- src/cmd/gc/go.h | 1 + src/cmd/gc/reflect.c | 178 ++++++++++++++++++++++++++++++++++++- src/pkg/runtime/gc_test.go | 16 ++++ src/pkg/runtime/mgc0.h | 42 +++++++++ 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 src/pkg/runtime/mgc0.h diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 4a8d191dc7..5ce9fb9e94 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -347,6 +347,7 @@ enum SymExported = 1<<2, // already written out by export SymUniq = 1<<3, SymSiggen = 1<<4, + SymGcgen = 1<<5, }; struct Sym diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 7496b71bf2..9dbf1ec596 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -5,6 +5,7 @@ #include #include #include "go.h" +#include "../../pkg/runtime/mgc0.h" /* * runtime interface and reflection data structures @@ -14,6 +15,7 @@ static NodeList* signatlist; static Sym* dtypesym(Type*); static Sym* weaktypesym(Type*); static Sym* dalgsym(Type*); +static Sym* dgcsym(Type*); static int sigcmp(Sig *a, Sig *b) @@ -586,7 +588,7 @@ dcommontype(Sym *s, int ot, Type *t) ot = dsymptr(s, ot, algarray, alg*sizeofAlg); else ot = dsymptr(s, ot, algsym, 0); - ot = duintptr(s, ot, 0); // gc + ot = dsymptr(s, ot, dgcsym(t), 0); // gc p = smprint("%-uT", t); //print("dcommontype: %s\n", p); ot = dgostringptr(s, ot, p); // string @@ -970,3 +972,177 @@ dalgsym(Type *t) return s; } +static int +dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size) +{ + Type *t1; + vlong o, off2, fieldoffset; + + if(t->align > 0 && (*off % t->align) != 0) + fatal("dgcsym1: invalid initial alignment, %T", t); + + switch(t->etype) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TFLOAT32: + case TFLOAT64: + case TCOMPLEX64: + case TCOMPLEX128: + *off += t->width; + break; + + case TPTR32: + case TPTR64: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(!haspointers(t->type) || t->type->etype == TUINT8) { + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + } else { + ot = duintptr(s, ot, GC_PTR); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dgcsym(t->type), 0); + } + *off += t->width; + break; + + case TCHAN: + case TUNSAFEPTR: + case TFUNC: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + *off += t->width; + break; + + // struct Hmap* + case TMAP: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_MAP_PTR); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dtypesym(t), 0); + *off += t->width; + break; + + // struct { byte *str; int32 len; } + case TSTRING: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_STRING); + ot = duintptr(s, ot, *off); + *off += t->width; + break; + + // struct { Itab* tab; void* data; } + // struct { Type* type; void* data; } // When isnilinter(t)==true + case TINTER: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(isnilinter(t)) { + ot = duintptr(s, ot, GC_EFACE); + ot = duintptr(s, ot, *off); + } else { + ot = duintptr(s, ot, GC_IFACE); + ot = duintptr(s, ot, *off); + } + *off += t->width; + break; + + case TARRAY: + if(t->bound < -1) + fatal("dgcsym1: invalid bound, %T", t); + if(isslice(t)) { + // struct { byte* array; uint32 len; uint32 cap; } + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(t->type->width != 0) { + ot = duintptr(s, ot, GC_SLICE); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dgcsym(t->type), 0); + } else { + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + } + *off += t->width; + } else { + if(t->bound < 1 || !haspointers(t->type)) { + *off += t->width; + } else if(t->bound == 1) { + ot = dgcsym1(s, ot, t->type, off, stack_size); // recursive call of dgcsym1 + } else { + if(stack_size < GC_STACK_CAPACITY) { + ot = duintptr(s, ot, GC_ARRAY_START); // a stack push during GC + ot = duintptr(s, ot, *off); + ot = duintptr(s, ot, t->bound); + ot = duintptr(s, ot, t->type->width); + off2 = 0; + ot = dgcsym1(s, ot, t->type, &off2, stack_size+1); // recursive call of dgcsym1 + ot = duintptr(s, ot, GC_ARRAY_NEXT); // a stack pop during GC + } else { + ot = duintptr(s, ot, GC_REGION); + ot = duintptr(s, ot, *off); + ot = duintptr(s, ot, t->width); + ot = dsymptr(s, ot, dgcsym(t), 0); + } + *off += t->width; + } + } + break; + + case TSTRUCT: + o = 0; + for(t1=t->type; t1!=T; t1=t1->down) { + fieldoffset = t1->width; + *off += fieldoffset - o; + ot = dgcsym1(s, ot, t1->type, off, stack_size); // recursive call of dgcsym1 + o = fieldoffset + t1->type->width; + } + *off += t->width - o; + break; + + default: + fatal("dgcsym1: unexpected type %T", t); + } + + return ot; +} + +static Sym* +dgcsym(Type *t) +{ + int ot; + vlong off; + Sym *s; + + s = typesymprefix(".gc", t); + if(s->flags & SymGcgen) + return s; + s->flags |= SymGcgen; + + ot = 0; + off = 0; + ot = duintptr(s, ot, t->width); + ot = dgcsym1(s, ot, t, &off, 0); + ot = duintptr(s, ot, GC_END); + ggloblsym(s, ot, 1, 1); + + if(t->align > 0) + off = rnd(off, t->align); + if(off != t->width) + fatal("dgcsym: off=%lld, size=%lld, type %T", off, t->width, t); + + return s; +} + diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go index 783409b689..56dd93819e 100644 --- a/src/pkg/runtime/gc_test.go +++ b/src/pkg/runtime/gc_test.go @@ -42,3 +42,19 @@ func TestGcSys(t *testing.T) { func workthegc() []byte { return make([]byte, 1029) } + +func TestGcDeepNesting(t *testing.T) { + type T [2][2][2][2][2][2][2][2][2][2]*int + a := new(T) + + // Prevent the compiler from applying escape analysis. + // This makes sure new(T) is allocated on heap, not on the stack. + t.Logf("%p", a) + + a[0][0][0][0][0][0][0][0][0][0] = new(int) + *a[0][0][0][0][0][0][0][0][0][0] = 13 + runtime.GC() + if *a[0][0][0][0][0][0][0][0][0][0] != 13 { + t.Fail() + } +} diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h new file mode 100644 index 0000000000..a2798ef34e --- /dev/null +++ b/src/pkg/runtime/mgc0.h @@ -0,0 +1,42 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Garbage collector (GC) + +// GC instruction opcodes. +// +// The opcode of an instruction is followed by zero or more +// arguments to the instruction. +// +// Meaning of arguments: +// off Offset (in bytes) from the start of the current object +// objgc Pointer to GC info of an object +// len Length of an array +// elemsize Size (in bytes) of an element +// size Size (in bytes) +enum { + GC_END, // End of object, loop or subroutine. Args: none + GC_PTR, // A typed pointer. Args: (off, objgc) + GC_APTR, // Pointer to an arbitrary object. Args: (off) + GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize) + GC_ARRAY_NEXT, // The next element of an array. Args: none + GC_CALL, // Call a subroutine. Args: (off, objgc) + GC_MAP_PTR, // Go map. Args: (off, MapType*) + GC_STRING, // Go string. Args: (off) + GC_EFACE, // interface{}. Args: (off) + GC_IFACE, // interface{...}. Args: (off) + GC_SLICE, // Go slice. Args: (off, objgc) + GC_REGION, // A region/part of the current object. Args: (off, size, objgc) + + GC_NUM_INSTR, // Number of instruction opcodes +}; + +enum { + // Size of GC's fixed stack. + // + // The current GC implementation permits: + // - at most 1 stack allocation because of GC_CALL + // - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START + GC_STACK_CAPACITY = 8, +};