From 2d9e8ded9464932b8a296164506b64f58908c6cf Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 9 Oct 2023 13:34:51 -0700 Subject: [PATCH] cmd/compile: reorganize compiler type descriptor generation Use the new rttype mechanism to share internal/abi types between the compiler and runtime. Change-Id: I2bbba4d8090c6f7ff20dca15b7b665f5d04e5bfd Reviewed-on: https://go-review.googlesource.com/c/go/+/534936 Reviewed-by: David Chase LUCI-TryBot-Result: Go LUCI TryBot-Result: Gopher Robot Reviewed-by: Carlos Amedee Run-TryBot: David Chase --- .../compile/internal/reflectdata/reflect.go | 337 ++++++++++-------- src/cmd/compile/internal/rttype/rttype.go | 241 ++++++++----- src/cmd/compile/internal/types/type.go | 15 + src/cmd/compile/internal/walk/switch.go | 2 +- src/internal/abi/compiletype.go | 133 ------- 5 files changed, 355 insertions(+), 373 deletions(-) diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index e23d2fb401d..c2407af017e 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -73,15 +73,13 @@ const ( MAXELEMSIZE = abi.MapMaxElemBytes ) -func structfieldSize() int { return abi.StructFieldSize(types.PtrSize) } // Sizeof(runtime.structfield{}) -func imethodSize() int { return abi.IMethodSize(types.PtrSize) } // Sizeof(runtime.imethod{}) -func commonSize() int { return int(rttype.Type.Size()) } // Sizeof(runtime._type{}) +func commonSize() int { return int(rttype.Type.Size()) } // Sizeof(runtime._type{}) func uncommonSize(t *types.Type) int { // Sizeof(runtime.uncommontype{}) if t.Sym() == nil && len(methods(t)) == 0 { return 0 } - return int(abi.UncommonSize()) + return int(rttype.UncommonType.Size()) } func makefield(name string, t *types.Type) *types.Field { @@ -432,32 +430,35 @@ func dimportpath(p *types.Pkg) { p.Pathsym = s } -func dgopkgpath(s *obj.LSym, ot int, pkg *types.Pkg) int { +func dgopkgpath(c rttype.Cursor, pkg *types.Pkg) { + c = c.Field("Bytes") if pkg == nil { - return objw.Uintptr(s, ot, 0) + c.WritePtr(nil) + return } dimportpath(pkg) - return objw.SymPtr(s, ot, pkg.Pathsym, 0) + c.WritePtr(pkg.Pathsym) } -// dgopkgpathOff writes an offset relocation in s at offset ot to the pkg path symbol. -func dgopkgpathOff(s *obj.LSym, ot int, pkg *types.Pkg) int { +// dgopkgpathOff writes an offset relocation to the pkg path symbol to c. +func dgopkgpathOff(c rttype.Cursor, pkg *types.Pkg) { if pkg == nil { - return objw.Uint32(s, ot, 0) + c.WriteInt32(0) + return } dimportpath(pkg) - return objw.SymPtrOff(s, ot, pkg.Pathsym) + c.WriteSymPtrOff(pkg.Pathsym, false) } // dnameField dumps a reflect.name for a struct field. -func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int { +func dnameField(c rttype.Cursor, spkg *types.Pkg, ft *types.Field) { if !types.IsExported(ft.Sym.Name) && ft.Sym.Pkg != spkg { base.Fatalf("package mismatch for %v", ft.Sym) } nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name), ft.Embedded != 0) - return objw.SymPtr(lsym, ot, nsym, 0) + c.Field("Bytes").WritePtr(nsym) } // dnameData writes the contents of a reflect.name into s at offset ot. @@ -502,7 +503,9 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported, ot = int(s.WriteBytes(base.Ctxt, int64(ot), b)) if pkg != nil { - ot = dgopkgpathOff(s, ot, pkg) + c := rttype.NewCursor(s, int64(ot), types.Types[types.TUINT32]) + dgopkgpathOff(c, pkg) + ot += 4 } return ot @@ -553,14 +556,14 @@ func dname(name, tag string, pkg *types.Pkg, exported, embedded bool) *obj.LSym // dextratype dumps the fields of a runtime.uncommontype. // dataAdd is the offset in bytes after the header where the -// backing array of the []method field is written (by dextratypeData). -func dextratype(lsym *obj.LSym, ot int, t *types.Type, dataAdd int) int { +// backing array of the []method field should be written. +func dextratype(lsym *obj.LSym, off int64, t *types.Type, dataAdd int) { m := methods(t) if t.Sym() == nil && len(m) == 0 { - return ot + base.Fatalf("extra requested of type with no extra info %v", t) } - noff := int(types.RoundUp(int64(ot), int64(types.PtrSize))) - if noff != ot { + noff := types.RoundUp(off, int64(types.PtrSize)) + if noff != off { base.Fatalf("unexpected alignment in dextratype for %v", t) } @@ -568,7 +571,8 @@ func dextratype(lsym *obj.LSym, ot int, t *types.Type, dataAdd int) int { writeType(a.type_) } - ot = dgopkgpathOff(lsym, ot, typePkg(t)) + c := rttype.NewCursor(lsym, off, rttype.UncommonType) + dgopkgpathOff(c.Field("PkgPath"), typePkg(t)) dataAdd += uncommonSize(t) mcount := len(m) @@ -580,11 +584,27 @@ func dextratype(lsym *obj.LSym, ot int, t *types.Type, dataAdd int) int { base.Fatalf("methods are too far away on %v: %d", t, dataAdd) } - ot = objw.Uint16(lsym, ot, uint16(mcount)) - ot = objw.Uint16(lsym, ot, uint16(xcount)) - ot = objw.Uint32(lsym, ot, uint32(dataAdd)) - ot = objw.Uint32(lsym, ot, 0) - return ot + c.Field("Mcount").WriteUint16(uint16(mcount)) + c.Field("Xcount").WriteUint16(uint16(xcount)) + c.Field("Moff").WriteUint32(uint32(dataAdd)) + // Note: there is an unused uint32 field here. + + // Write the backing array for the []method field. + array := rttype.NewArrayCursor(lsym, off+int64(dataAdd), rttype.Method, mcount) + for i, a := range m { + exported := types.IsExported(a.name.Name) + var pkg *types.Pkg + if !exported && a.name.Pkg != typePkg(t) { + pkg = a.name.Pkg + } + nsym := dname(a.name.Name, "", pkg, exported, false) + + e := array.Elem(i) + e.Field("Name").WriteSymPtrOff(nsym, false) + dmethodptrOff(e.Field("Mtyp"), writeType(a.mtype)) + dmethodptrOff(e.Field("Ifn"), a.isym) + dmethodptrOff(e.Field("Tfn"), a.tsym) + } } func typePkg(t *types.Type) *types.Pkg { @@ -603,34 +623,11 @@ func typePkg(t *types.Type) *types.Pkg { return nil } -// dextratypeData dumps the backing array for the []method field of -// runtime.uncommontype. -func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int { - for _, a := range methods(t) { - // ../../../../runtime/type.go:/method - exported := types.IsExported(a.name.Name) - var pkg *types.Pkg - if !exported && a.name.Pkg != typePkg(t) { - pkg = a.name.Pkg - } - nsym := dname(a.name.Name, "", pkg, exported, false) - - ot = objw.SymPtrOff(lsym, ot, nsym) - ot = dmethodptrOff(lsym, ot, writeType(a.mtype)) - ot = dmethodptrOff(lsym, ot, a.isym) - ot = dmethodptrOff(lsym, ot, a.tsym) - } - return ot -} - -func dmethodptrOff(s *obj.LSym, ot int, x *obj.LSym) int { - objw.Uint32(s, ot, 0) - r := obj.Addrel(s) - r.Off = int32(ot) - r.Siz = 4 +func dmethodptrOff(c rttype.Cursor, x *obj.LSym) { + c.WriteInt32(0) + r := c.Reloc() r.Sym = x r.Type = objabi.R_METHODOFF - return ot + 4 } var kinds = []int{ @@ -667,8 +664,8 @@ var ( memequalvarlen *obj.LSym ) -// dcommontype dumps the contents of a reflect.rtype (runtime._type). -func dcommontype(lsym *obj.LSym, t *types.Type) int { +// dcommontype dumps the contents of a reflect.rtype (runtime._type) to c. +func dcommontype(c rttype.Cursor, t *types.Type) { types.CalcSize(t) eqfunc := geneq(t) @@ -700,10 +697,9 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { // str nameOff // ptrToThis typeOff // } - rt := rttype.Type - rt.WriteUintptr(lsym, "Size_", uint64(t.Size())) - rt.WriteUintptr(lsym, "PtrBytes", uint64(ptrdata)) - rt.WriteUint32(lsym, "Hash", types.TypeHash(t)) + c.Field("Size_").WriteUintptr(uint64(t.Size())) + c.Field("PtrBytes").WriteUintptr(uint64(ptrdata)) + c.Field("Hash").WriteUint32(types.TypeHash(t)) var tflag abi.TFlag if uncommonSize(t) != 0 { @@ -739,7 +735,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { // this should optimize away completely panic("Unexpected change in size of abi.TFlag") } - rt.WriteUint8(lsym, "TFlag", uint8(tflag)) + c.Field("TFlag").WriteUint8(uint8(tflag)) // runtime (and common sense) expects alignment to be a power of two. i := int(uint8(t.Alignment())) @@ -750,8 +746,8 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { if i&(i-1) != 0 { base.Fatalf("invalid alignment %d for %v", uint8(t.Alignment()), t) } - rt.WriteUint8(lsym, "Align_", uint8(t.Alignment())) - rt.WriteUint8(lsym, "FieldAlign_", uint8(t.Alignment())) + c.Field("Align_").WriteUint8(uint8(t.Alignment())) + c.Field("FieldAlign_").WriteUint8(uint8(t.Alignment())) i = kinds[t.Kind()] if types.IsDirectIface(t) { @@ -760,16 +756,14 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { if useGCProg { i |= objabi.KindGCProg } - rt.WriteUint8(lsym, "Kind_", uint8(i)) + c.Field("Kind_").WriteUint8(uint8(i)) - rt.WritePtr(lsym, "Equal", eqfunc) - rt.WritePtr(lsym, "GCData", gcsym) + c.Field("Equal").WritePtr(eqfunc) + c.Field("GCData").WritePtr(gcsym) nsym := dname(p, "", nil, exported, false) - rt.WriteSymPtrOff(lsym, "Str", nsym, false) - rt.WriteSymPtrOff(lsym, "PtrToThis", sptr, sptrWeak) - - return int(rt.Size()) + c.Field("Str").WriteSymPtrOff(nsym, false) + c.Field("PtrToThis").WriteSymPtrOff(sptr, sptrWeak) } // TrackSym returns the symbol for tracking use of field/method f, assumed @@ -973,87 +967,137 @@ func writeType(t *types.Type) *obj.LSym { return lsym } - ot := 0 + // Type layout Written by Marker + // +--------------------------------+ - 0 + // | abi/internal.Type | dcommontype + // +--------------------------------+ - A + // | additional type-dependent | code in the switch below + // | fields, e.g. | + // | abi/internal.ArrayType.Len | + // +--------------------------------+ - B + // | internal/abi.UncommonType | dextratype + // | This section is optional, | + // | if type has a name or methods | + // +--------------------------------+ - C + // | variable-length data | code in the switch below + // | referenced by | + // | type-dependent fields, e.g. | + // | abi/internal.StructType.Fields | + // | dataAdd = size of this section | + // +--------------------------------+ - D + // | method list, if any | dextratype + // +--------------------------------+ - E + + // UncommonType section is included if we have a name or a method. + extra := t.Sym() != nil || len(methods(t)) != 0 + + // Decide the underlying type of the descriptor, and remember + // the size we need for variable-length data. + var rt *types.Type + dataAdd := 0 switch t.Kind() { default: - ot = dcommontype(lsym, t) - ot = dextratype(lsym, ot, t, 0) - + rt = rttype.Type case types.TARRAY: - // ../../../../runtime/type.go:/arrayType + rt = rttype.ArrayType + case types.TSLICE: + rt = rttype.SliceType + case types.TCHAN: + rt = rttype.ChanType + case types.TFUNC: + rt = rttype.FuncType + dataAdd = (t.NumRecvs() + t.NumParams() + t.NumResults()) * types.PtrSize + case types.TINTER: + rt = rttype.InterfaceType + dataAdd = len(imethods(t)) * int(rttype.IMethod.Size()) + case types.TMAP: + rt = rttype.MapType + case types.TPTR: + rt = rttype.PtrType + // TODO: use rttype.Type for Elem() is ANY? + case types.TSTRUCT: + rt = rttype.StructType + dataAdd = t.NumFields() * int(rttype.StructField.Size()) + } + + // Compute offsets of each section. + B := rt.Size() + C := B + if extra { + C = B + rttype.UncommonType.Size() + } + D := C + int64(dataAdd) + E := D + int64(len(methods(t)))*rttype.Method.Size() + + // Write the runtime._type + c := rttype.NewCursor(lsym, 0, rt) + if rt == rttype.Type { + dcommontype(c, t) + } else { + dcommontype(c.Field("Type"), t) + } + + // Write additional type-specific data + // (Both the fixed size and variable-sized sections.) + switch t.Kind() { + case types.TARRAY: + // internal/abi.ArrayType s1 := writeType(t.Elem()) t2 := types.NewSlice(t.Elem()) s2 := writeType(t2) - ot = dcommontype(lsym, t) - ot = objw.SymPtr(lsym, ot, s1, 0) - ot = objw.SymPtr(lsym, ot, s2, 0) - ot = objw.Uintptr(lsym, ot, uint64(t.NumElem())) - ot = dextratype(lsym, ot, t, 0) + c.Field("Elem").WritePtr(s1) + c.Field("Slice").WritePtr(s2) + c.Field("Len").WriteUintptr(uint64(t.NumElem())) case types.TSLICE: - // ../../../../runtime/type.go:/sliceType + // internal/abi.SliceType s1 := writeType(t.Elem()) - ot = dcommontype(lsym, t) - ot = objw.SymPtr(lsym, ot, s1, 0) - ot = dextratype(lsym, ot, t, 0) + c.Field("Elem").WritePtr(s1) case types.TCHAN: - // ../../../../runtime/type.go:/chanType + // internal/abi.ChanType s1 := writeType(t.Elem()) - ot = dcommontype(lsym, t) - ot = objw.SymPtr(lsym, ot, s1, 0) - ot = objw.Uintptr(lsym, ot, uint64(t.ChanDir())) - ot = dextratype(lsym, ot, t, 0) + c.Field("Elem").WritePtr(s1) + c.Field("Dir").WriteInt(int64(t.ChanDir())) case types.TFUNC: + // internal/abi.FuncType for _, t1 := range t.RecvParamsResults() { writeType(t1.Type) } - - ot = dcommontype(lsym, t) inCount := t.NumRecvs() + t.NumParams() outCount := t.NumResults() if t.IsVariadic() { outCount |= 1 << 15 } - ot = objw.Uint16(lsym, ot, uint16(inCount)) - ot = objw.Uint16(lsym, ot, uint16(outCount)) - if types.PtrSize == 8 { - ot += 4 // align for *rtype - } - dataAdd := (inCount + t.NumResults()) * types.PtrSize - ot = dextratype(lsym, ot, t, dataAdd) + c.Field("InCount").WriteUint16(uint16(inCount)) + c.Field("OutCount").WriteUint16(uint16(outCount)) // Array of rtype pointers follows funcType. - for _, t1 := range t.RecvParamsResults() { - ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0) + typs := t.RecvParamsResults() + array := rttype.NewArrayCursor(lsym, C, types.Types[types.TUNSAFEPTR], len(typs)) + for i, t1 := range typs { + array.Elem(i).WritePtr(writeType(t1.Type)) } case types.TINTER: + // internal/abi.InterfaceType m := imethods(t) n := len(m) for _, a := range m { writeType(a.type_) } - // ../../../../runtime/type.go:/interfaceType - ot = dcommontype(lsym, t) - var tpkg *types.Pkg if t.Sym() != nil && t != types.Types[t.Kind()] && t != types.ErrorType { tpkg = t.Sym().Pkg } - ot = dgopkgpath(lsym, ot, tpkg) + dgopkgpath(c.Field("PkgPath"), tpkg) + c.Field("Methods").WriteSlice(lsym, C, int64(n), int64(n)) - ot = objw.SymPtr(lsym, ot, lsym, ot+3*types.PtrSize+uncommonSize(t)) - ot = objw.Uintptr(lsym, ot, uint64(n)) - ot = objw.Uintptr(lsym, ot, uint64(n)) - dataAdd := imethodSize() * n - ot = dextratype(lsym, ot, t, dataAdd) - - for _, a := range m { - // ../../../../runtime/type.go:/imethod + array := rttype.NewArrayCursor(lsym, C, rttype.IMethod, n) + for i, a := range m { exported := types.IsExported(a.name.Name) var pkg *types.Pkg if !exported && a.name.Pkg != tpkg { @@ -1061,39 +1105,39 @@ func writeType(t *types.Type) *obj.LSym { } nsym := dname(a.name.Name, "", pkg, exported, false) - ot = objw.SymPtrOff(lsym, ot, nsym) - ot = objw.SymPtrOff(lsym, ot, writeType(a.type_)) + e := array.Elem(i) + e.Field("Name").WriteSymPtrOff(nsym, false) + e.Field("Typ").WriteSymPtrOff(writeType(a.type_), false) } - // ../../../../runtime/type.go:/mapType case types.TMAP: + // internal/abi.MapType s1 := writeType(t.Key()) s2 := writeType(t.Elem()) s3 := writeType(MapBucketType(t)) hasher := genhash(t.Key()) - ot = dcommontype(lsym, t) - ot = objw.SymPtr(lsym, ot, s1, 0) - ot = objw.SymPtr(lsym, ot, s2, 0) - ot = objw.SymPtr(lsym, ot, s3, 0) - ot = objw.SymPtr(lsym, ot, hasher, 0) + c.Field("Key").WritePtr(s1) + c.Field("Elem").WritePtr(s2) + c.Field("Bucket").WritePtr(s3) + c.Field("Hasher").WritePtr(hasher) var flags uint32 // Note: flags must match maptype accessors in ../../../../runtime/type.go // and maptype builder in ../../../../reflect/type.go:MapOf. if t.Key().Size() > MAXKEYSIZE { - ot = objw.Uint8(lsym, ot, uint8(types.PtrSize)) + c.Field("KeySize").WriteUint8(uint8(types.PtrSize)) flags |= 1 // indirect key } else { - ot = objw.Uint8(lsym, ot, uint8(t.Key().Size())) + c.Field("KeySize").WriteUint8(uint8(t.Key().Size())) } if t.Elem().Size() > MAXELEMSIZE { - ot = objw.Uint8(lsym, ot, uint8(types.PtrSize)) + c.Field("ValueSize").WriteUint8(uint8(types.PtrSize)) flags |= 2 // indirect value } else { - ot = objw.Uint8(lsym, ot, uint8(t.Elem().Size())) + c.Field("ValueSize").WriteUint8(uint8(t.Elem().Size())) } - ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Size())) + c.Field("BucketSize").WriteUint16(uint16(MapBucketType(t).Size())) if types.IsReflexive(t.Key()) { flags |= 4 // reflexive key } @@ -1103,8 +1147,8 @@ func writeType(t *types.Type) *obj.LSym { if hashMightPanic(t.Key()) { flags |= 16 // hash might panic } - ot = objw.Uint32(lsym, ot, flags) - ot = dextratype(lsym, ot, t, 0) + c.Field("Flags").WriteUint32(flags) + if u := t.Underlying(); u != t { // If t is a named map type, also keep the underlying map // type live in the binary. This is important to make sure that @@ -1116,24 +1160,16 @@ func writeType(t *types.Type) *obj.LSym { } case types.TPTR: + // internal/abi.PtrType if t.Elem().Kind() == types.TANY { - // ../../../../runtime/type.go:/UnsafePointerType - ot = dcommontype(lsym, t) - ot = dextratype(lsym, ot, t, 0) - - break + base.Fatalf("bad pointer base type") } - // ../../../../runtime/type.go:/ptrType s1 := writeType(t.Elem()) + c.Field("Elem").WritePtr(s1) - ot = dcommontype(lsym, t) - ot = objw.SymPtr(lsym, ot, s1, 0) - ot = dextratype(lsym, ot, t, 0) - - // ../../../../runtime/type.go:/structType - // for security, only the exported fields. case types.TSTRUCT: + // internal/abi.StructType fields := t.Fields() for _, t1 := range fields { writeType(t1.Type) @@ -1152,23 +1188,23 @@ func writeType(t *types.Type) *obj.LSym { } } - ot = dcommontype(lsym, t) - ot = dgopkgpath(lsym, ot, spkg) - ot = objw.SymPtr(lsym, ot, lsym, ot+3*types.PtrSize+uncommonSize(t)) - ot = objw.Uintptr(lsym, ot, uint64(len(fields))) - ot = objw.Uintptr(lsym, ot, uint64(len(fields))) + dgopkgpath(c.Field("PkgPath"), spkg) + c.Field("Fields").WriteSlice(lsym, C, int64(len(fields)), int64(len(fields))) - dataAdd := len(fields) * structfieldSize() - ot = dextratype(lsym, ot, t, dataAdd) - - for _, f := range fields { - // ../../../../runtime/type.go:/structField - ot = dnameField(lsym, ot, spkg, f) - ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0) - ot = objw.Uintptr(lsym, ot, uint64(f.Offset)) + array := rttype.NewArrayCursor(lsym, C, rttype.StructField, len(fields)) + for i, f := range fields { + e := array.Elem(i) + dnameField(e.Field("Name"), spkg, f) + e.Field("Typ").WritePtr(writeType(f.Type)) + e.Field("Offset").WriteUintptr(uint64(f.Offset)) } } + // Write the extra info, if any. + if extra { + dextratype(lsym, B, t, dataAdd) + } + // Note: DUPOK is required to ensure that we don't end up with more // than one type descriptor for a given type, if the type descriptor // can be defined in multiple packages, that is, unnamed types, @@ -1178,8 +1214,7 @@ func writeType(t *types.Type) *obj.LSym { dupok = obj.DUPOK } - ot = dextratypeData(lsym, ot, t) - objw.Global(lsym, int32(ot), int16(dupok|obj.RODATA)) + objw.Global(lsym, int32(E), int16(dupok|obj.RODATA)) // The linker will leave a table of all the typelinks for // types in the binary, so the runtime can find them. diff --git a/src/cmd/compile/internal/rttype/rttype.go b/src/cmd/compile/internal/rttype/rttype.go index 474203631de..1a614c86784 100644 --- a/src/cmd/compile/internal/rttype/rttype.go +++ b/src/cmd/compile/internal/rttype/rttype.go @@ -16,32 +16,46 @@ import ( "cmd/compile/internal/objw" "cmd/compile/internal/types" "cmd/internal/obj" - "fmt" "internal/abi" "reflect" ) -type RuntimeType struct { - // A *types.Type representing a type used at runtime. - t *types.Type - // components maps from component names to their location in the type. - components map[string]location -} +// The type structures shared with the runtime. +var Type *types.Type -type location struct { - offset int64 - kind types.Kind // Just used for bug detection -} +var ArrayType *types.Type +var ChanType *types.Type +var FuncType *types.Type +var InterfaceType *types.Type +var MapType *types.Type +var PtrType *types.Type +var SliceType *types.Type +var StructType *types.Type -// Types shared with the runtime via internal/abi. -// TODO: add more -var Type *RuntimeType +// Types that are parts of the types above. +var IMethod *types.Type +var Method *types.Type +var StructField *types.Type +var UncommonType *types.Type func Init() { // Note: this has to be called explicitly instead of being // an init function so it runs after the types package has // been properly initialized. Type = fromReflect(reflect.TypeOf(abi.Type{})) + ArrayType = fromReflect(reflect.TypeOf(abi.ArrayType{})) + ChanType = fromReflect(reflect.TypeOf(abi.ChanType{})) + FuncType = fromReflect(reflect.TypeOf(abi.FuncType{})) + InterfaceType = fromReflect(reflect.TypeOf(abi.InterfaceType{})) + MapType = fromReflect(reflect.TypeOf(abi.MapType{})) + PtrType = fromReflect(reflect.TypeOf(abi.PtrType{})) + SliceType = fromReflect(reflect.TypeOf(abi.SliceType{})) + StructType = fromReflect(reflect.TypeOf(abi.StructType{})) + + IMethod = fromReflect(reflect.TypeOf(abi.Imethod{})) + Method = fromReflect(reflect.TypeOf(abi.Method{})) + StructField = fromReflect(reflect.TypeOf(abi.StructField{})) + UncommonType = fromReflect(reflect.TypeOf(abi.UncommonType{})) // Make sure abi functions are correct. These functions are used // by the linker which doesn't have the ability to do type layout, @@ -50,17 +64,22 @@ func Init() { if got, want := int64(abi.CommonSize(ptrSize)), Type.Size(); got != want { base.Fatalf("abi.CommonSize() == %d, want %d", got, want) } - if got, want := int64(abi.TFlagOff(ptrSize)), Type.Offset("TFlag"); got != want { + if got, want := int64(abi.StructFieldSize(ptrSize)), StructField.Size(); got != want { + base.Fatalf("abi.StructFieldSize() == %d, want %d", got, want) + } + if got, want := int64(abi.UncommonSize()), UncommonType.Size(); got != want { + base.Fatalf("abi.UncommonSize() == %d, want %d", got, want) + } + if got, want := int64(abi.TFlagOff(ptrSize)), Type.OffsetOf("TFlag"); got != want { base.Fatalf("abi.TFlagOff() == %d, want %d", got, want) } } -// fromReflect translates from a host type to the equivalent -// target type. -func fromReflect(rt reflect.Type) *RuntimeType { +// fromReflect translates from a host type to the equivalent target type. +func fromReflect(rt reflect.Type) *types.Type { t := reflectToType(rt) types.CalcSize(t) - return &RuntimeType{t: t, components: unpack(t)} + return t } // reflectToType converts from a reflect.Type (which is a compiler @@ -68,8 +87,6 @@ func fromReflect(rt reflect.Type) *RuntimeType { // must be CalcSize'd before using. func reflectToType(rt reflect.Type) *types.Type { switch rt.Kind() { - case reflect.Bool: - return types.Types[types.TBOOL] case reflect.Int: return types.Types[types.TINT] case reflect.Int32: @@ -86,6 +103,8 @@ func reflectToType(rt reflect.Type) *types.Type { // TODO: there's no mechanism to distinguish different pointer types, // so we treat them all as unsafe.Pointer. return types.Types[types.TUNSAFEPTR] + case reflect.Slice: + return types.NewSlice(reflectToType(rt.Elem())) case reflect.Array: return types.NewArray(reflectToType(rt.Elem()), int64(rt.Len())) case reflect.Struct: @@ -102,91 +121,137 @@ func reflectToType(rt reflect.Type) *types.Type { } } -// Unpack generates a set of components of a *types.Type. -// The type must have already been CalcSize'd. -func unpack(t *types.Type) map[string]location { - components := map[string]location{} - switch t.Kind() { - default: - components[""] = location{0, t.Kind()} - case types.TARRAY: - // TODO: not used yet - elemSize := t.Elem().Size() - for name, loc := range unpack(t.Elem()) { - for i := int64(0); i < t.NumElem(); i++ { - components[fmt.Sprintf("[%d]%s", i, name)] = location{i*elemSize + loc.offset, loc.kind} - } - } - case types.TSTRUCT: - for _, f := range t.Fields() { - for name, loc := range unpack(f.Type) { - n := f.Sym.Name - if name != "" { - n += "." + name - } - components[n] = location{f.Offset + loc.offset, loc.kind} - } - } - } - return components +// A Cursor represents a typed location inside a static variable where we +// are going to write. +type Cursor struct { + lsym *obj.LSym + offset int64 + typ *types.Type } -func (r *RuntimeType) Size() int64 { - return r.t.Size() +// NewCursor returns a cursor starting at lsym+off and having type t. +func NewCursor(lsym *obj.LSym, off int64, t *types.Type) Cursor { + return Cursor{lsym: lsym, offset: off, typ: t} } -func (r *RuntimeType) Alignment() int64 { - return r.t.Alignment() -} - -func (r *RuntimeType) Offset(name string) int64 { - return r.components[name].offset -} - -// WritePtr writes a pointer "target" to the component named "name" in the -// static object "lsym". -func (r *RuntimeType) WritePtr(lsym *obj.LSym, name string, target *obj.LSym) { - loc := r.components[name] - if loc.kind != types.TUNSAFEPTR { - base.Fatalf("can't write ptr to field %s, it has kind %s", name, loc.kind) +// WritePtr writes a pointer "target" to the component at the location specified by c. +func (c Cursor) WritePtr(target *obj.LSym) { + if c.typ.Kind() != types.TUNSAFEPTR { + base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind()) } if target == nil { - objw.Uintptr(lsym, int(loc.offset), 0) + objw.Uintptr(c.lsym, int(c.offset), 0) } else { - objw.SymPtr(lsym, int(loc.offset), target, 0) + objw.SymPtr(c.lsym, int(c.offset), target, 0) } } -func (r *RuntimeType) WriteUintptr(lsym *obj.LSym, name string, val uint64) { - loc := r.components[name] - if loc.kind != types.TUINTPTR { - base.Fatalf("can't write uintptr to field %s, it has kind %s", name, loc.kind) +func (c Cursor) WriteUintptr(val uint64) { + if c.typ.Kind() != types.TUINTPTR { + base.Fatalf("can't write uintptr, it has kind %s", c.typ.Kind()) } - objw.Uintptr(lsym, int(loc.offset), val) + objw.Uintptr(c.lsym, int(c.offset), val) } -func (r *RuntimeType) WriteUint32(lsym *obj.LSym, name string, val uint32) { - loc := r.components[name] - if loc.kind != types.TUINT32 { - base.Fatalf("can't write uint32 to field %s, it has kind %s", name, loc.kind) +func (c Cursor) WriteUint32(val uint32) { + if c.typ.Kind() != types.TUINT32 { + base.Fatalf("can't write uint32, it has kind %s", c.typ.Kind()) } - objw.Uint32(lsym, int(loc.offset), val) + objw.Uint32(c.lsym, int(c.offset), val) } -func (r *RuntimeType) WriteUint8(lsym *obj.LSym, name string, val uint8) { - loc := r.components[name] - if loc.kind != types.TUINT8 { - base.Fatalf("can't write uint8 to field %s, it has kind %s", name, loc.kind) +func (c Cursor) WriteUint16(val uint16) { + if c.typ.Kind() != types.TUINT16 { + base.Fatalf("can't write uint16, it has kind %s", c.typ.Kind()) } - objw.Uint8(lsym, int(loc.offset), val) + objw.Uint16(c.lsym, int(c.offset), val) } -func (r *RuntimeType) WriteSymPtrOff(lsym *obj.LSym, name string, target *obj.LSym, weak bool) { - loc := r.components[name] - if loc.kind != types.TINT32 { - base.Fatalf("can't write SymPtr to field %s, it has kind %s", name, loc.kind) +func (c Cursor) WriteUint8(val uint8) { + if c.typ.Kind() != types.TUINT8 { + base.Fatalf("can't write uint8, it has kind %s", c.typ.Kind()) + } + objw.Uint8(c.lsym, int(c.offset), val) +} +func (c Cursor) WriteInt(val int64) { + if c.typ.Kind() != types.TINT { + base.Fatalf("can't write int, it has kind %s", c.typ.Kind()) + } + objw.Uintptr(c.lsym, int(c.offset), uint64(val)) +} +func (c Cursor) WriteInt32(val int32) { + if c.typ.Kind() != types.TINT32 { + base.Fatalf("can't write int32, it has kind %s", c.typ.Kind()) + } + objw.Uint32(c.lsym, int(c.offset), uint32(val)) +} + +// WriteSymPtrOff writes a "pointer" to the given symbol. The symbol +// is encoded as a uint32 offset from the start of the section. +func (c Cursor) WriteSymPtrOff(target *obj.LSym, weak bool) { + if c.typ.Kind() != types.TINT32 && c.typ.Kind() != types.TUINT32 { + base.Fatalf("can't write SymPtr, it has kind %s", c.typ.Kind()) } if target == nil { - objw.Uint32(lsym, int(loc.offset), 0) + objw.Uint32(c.lsym, int(c.offset), 0) } else if weak { - objw.SymPtrWeakOff(lsym, int(loc.offset), target) + objw.SymPtrWeakOff(c.lsym, int(c.offset), target) } else { - objw.SymPtrOff(lsym, int(loc.offset), target) + objw.SymPtrOff(c.lsym, int(c.offset), target) } } + +// WriteSlice writes a slice header to c. The pointer is target+off, the len and cap fields are given. +func (c Cursor) WriteSlice(target *obj.LSym, off, len, cap int64) { + if c.typ.Kind() != types.TSLICE { + base.Fatalf("can't write slice, it has kind %s", c.typ.Kind()) + } + objw.SymPtr(c.lsym, int(c.offset), target, int(off)) + objw.Uintptr(c.lsym, int(c.offset)+types.PtrSize, uint64(len)) + objw.Uintptr(c.lsym, int(c.offset)+2*types.PtrSize, uint64(cap)) + // TODO: ability to switch len&cap. Maybe not needed here, as every caller + // passes the same thing for both? + if len != cap { + base.Fatalf("len != cap (%d != %d)", len, cap) + } +} + +// Reloc adds a relocation from the current cursor position. +// Reloc fills in Off and Siz fields. Caller should fill in the rest (Type, others). +func (c Cursor) Reloc() *obj.Reloc { + r := obj.Addrel(c.lsym) + r.Off = int32(c.offset) + r.Siz = uint8(c.typ.Size()) + return r +} + +// Field selects the field with the given name from the struct pointed to by c. +func (c Cursor) Field(name string) Cursor { + if c.typ.Kind() != types.TSTRUCT { + base.Fatalf("can't call Field on non-struct %v", c.typ) + } + for _, f := range c.typ.Fields() { + if f.Sym.Name == name { + return Cursor{lsym: c.lsym, offset: c.offset + f.Offset, typ: f.Type} + } + } + base.Fatalf("couldn't find field %s in %v", name, c.typ) + return Cursor{} +} + +type ArrayCursor struct { + c Cursor // cursor pointing at first element + n int // number of elements +} + +// NewArrayCursor returns a cursor starting at lsym+off and having n copies of type t. +func NewArrayCursor(lsym *obj.LSym, off int64, t *types.Type, n int) ArrayCursor { + return ArrayCursor{ + c: NewCursor(lsym, off, t), + n: n, + } +} + +// Elem selects element i of the array pointed to by c. +func (a ArrayCursor) Elem(i int) Cursor { + if i < 0 || i >= a.n { + base.Fatalf("element index %d out of range [0:%d]", i, a.n) + } + return Cursor{lsym: a.c.lsym, offset: a.c.offset + int64(i)*a.c.typ.Size(), typ: a.c.typ} +} diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 3655c5db022..2777b4f0073 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -1471,6 +1471,21 @@ func (t *Type) FieldName(i int) string { return t.Field(i).Sym.Name } +// OffsetOf reports the offset of the field of a struct. +// The field is looked up by name. +func (t *Type) OffsetOf(name string) int64 { + if t.kind != TSTRUCT { + base.Fatalf("can't call OffsetOf on non-struct %v", t) + } + for _, f := range t.Fields() { + if f.Sym.Name == name { + return f.Offset + } + } + base.Fatalf("couldn't find field %s in %v", name, t) + return -1 +} + func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) return t.extra.(*Array).Bound diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go index 1aff97d7fd3..b406f502aa7 100644 --- a/src/cmd/compile/internal/walk/switch.go +++ b/src/cmd/compile/internal/walk/switch.go @@ -692,7 +692,7 @@ func typeHashFieldOf(pos src.XPos, itab *ir.UnaryExpr) *ir.SelectorExpr { if itab.X.Type().IsEmptyInterface() { // runtime._type's hash field if rtypeHashField == nil { - rtypeHashField = runtimeField("hash", rttype.Type.Offset("Hash"), types.Types[types.TUINT32]) + rtypeHashField = runtimeField("hash", rttype.Type.OffsetOf("Hash"), types.Types[types.TUINT32]) } hashField = rtypeHashField } else { diff --git a/src/internal/abi/compiletype.go b/src/internal/abi/compiletype.go index f2a3001d2ea..6f1a2d672c4 100644 --- a/src/internal/abi/compiletype.go +++ b/src/internal/abi/compiletype.go @@ -21,138 +21,5 @@ func StructFieldSize(ptrSize int) int { return 3 * ptrSize } // This exported function is in an internal package, so it may change to depend on ptrSize in the future. func UncommonSize() uint64 { return 4 + 2 + 2 + 4 + 4 } -// IMethodSize returns sizeof(IMethod) for a compilation target with a given ptrSize -func IMethodSize(ptrSize int) int { return 4 + 4 } - // TFlagOff returns the offset of Type.TFlag for a compilation target with a given ptrSize func TFlagOff(ptrSize int) int { return 2*ptrSize + 4 } - -// Offset is for computing offsets of type data structures at compile/link time; -// the target platform may not be the host platform. Its state includes the -// current offset, necessary alignment for the sequence of types, and the size -// of pointers and alignment of slices, interfaces, and strings (this is for tearing- -// resistant access to these types, if/when that is supported). -type Offset struct { - off uint64 // the current offset - align uint8 // the required alignmentof the container - ptrSize uint8 // the size of a pointer in bytes - sliceAlign uint8 // the alignment of slices (and interfaces and strings) -} - -// NewOffset returns a new Offset with offset 0 and alignment 1. -func NewOffset(ptrSize uint8, twoWordAlignSlices bool) Offset { - if twoWordAlignSlices { - return Offset{off: 0, align: 1, ptrSize: ptrSize, sliceAlign: 2 * ptrSize} - } - return Offset{off: 0, align: 1, ptrSize: ptrSize, sliceAlign: ptrSize} -} - -func assertIsAPowerOfTwo(x uint8) { - if x == 0 { - panic("Zero is not a power of two") - } - if x&-x == x { - return - } - panic("Not a power of two") -} - -// InitializedOffset returns a new Offset with specified offset, alignment, pointer size, and slice alignment. -func InitializedOffset(off int, align uint8, ptrSize uint8, twoWordAlignSlices bool) Offset { - assertIsAPowerOfTwo(align) - o0 := NewOffset(ptrSize, twoWordAlignSlices) - o0.off = uint64(off) - o0.align = align - return o0 -} - -func (o Offset) align_(a uint8) Offset { - o.off = (o.off + uint64(a) - 1) & ^(uint64(a) - 1) - if o.align < a { - o.align = a - } - return o -} - -// Align returns the offset obtained by aligning offset to a multiple of a. -// a must be a power of two. -func (o Offset) Align(a uint8) Offset { - assertIsAPowerOfTwo(a) - return o.align_(a) -} - -// plus returns the offset obtained by appending a power-of-2-sized-and-aligned object to o. -func (o Offset) plus(x uint64) Offset { - o = o.align_(uint8(x)) - o.off += x - return o -} - -// D8 returns the offset obtained by appending an 8-bit field to o. -func (o Offset) D8() Offset { - return o.plus(1) -} - -// D16 returns the offset obtained by appending a 16-bit field to o. -func (o Offset) D16() Offset { - return o.plus(2) -} - -// D32 returns the offset obtained by appending a 32-bit field to o. -func (o Offset) D32() Offset { - return o.plus(4) -} - -// D64 returns the offset obtained by appending a 64-bit field to o. -func (o Offset) D64() Offset { - return o.plus(8) -} - -// D64 returns the offset obtained by appending a pointer field to o. -func (o Offset) P() Offset { - if o.ptrSize == 0 { - panic("This offset has no defined pointer size") - } - return o.plus(uint64(o.ptrSize)) -} - -// Slice returns the offset obtained by appending a slice field to o. -func (o Offset) Slice() Offset { - o = o.align_(o.sliceAlign) - o.off += 3 * uint64(o.ptrSize) - // There's been discussion of whether slices should be 2-word aligned to allow - // use of aligned 2-word load/store to prevent tearing, this is future proofing. - // In general, for purposes of struct layout (and very likely default C layout - // compatibility) the "size" of a Go type is rounded up to its alignment. - return o.Align(o.sliceAlign) -} - -// String returns the offset obtained by appending a string field to o. -func (o Offset) String() Offset { - o = o.align_(o.sliceAlign) - o.off += 2 * uint64(o.ptrSize) - return o // We "know" it needs no further alignment -} - -// Interface returns the offset obtained by appending an interface field to o. -func (o Offset) Interface() Offset { - o = o.align_(o.sliceAlign) - o.off += 2 * uint64(o.ptrSize) - return o // We "know" it needs no further alignment -} - -// Offset returns the struct-aligned offset (size) of o. -// This is at least as large as the current internal offset; it may be larger. -func (o Offset) Offset() uint64 { - return o.Align(o.align).off -} - -func (o Offset) PlusUncommon() Offset { - o.off += UncommonSize() - return o -} - -// CommonOffset returns the Offset to the data after the common portion of type data structures. -func CommonOffset(ptrSize int, twoWordAlignSlices bool) Offset { - return InitializedOffset(CommonSize(ptrSize), uint8(ptrSize), uint8(ptrSize), twoWordAlignSlices) -}