| // Copyright 2023 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. |
| |
| // Package rttype allows the compiler to share type information with |
| // the runtime. The shared type information is stored in |
| // internal/abi. This package translates those types from the host |
| // machine on which the compiler runs to the target machine on which |
| // the compiled program will run. In particular, this package handles |
| // layout differences between e.g. a 64 bit compiler and 32 bit |
| // target. |
| package rttype |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/objw" |
| "cmd/compile/internal/types" |
| "cmd/internal/obj" |
| "internal/abi" |
| "reflect" |
| ) |
| |
| // The type structures shared with the runtime. |
| var Type *types.Type |
| |
| 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 that are parts of the types above. |
| var IMethod *types.Type |
| var Method *types.Type |
| var StructField *types.Type |
| var UncommonType *types.Type |
| |
| // Type switches and asserts |
| var InterfaceSwitch *types.Type |
| var TypeAssert *types.Type |
| |
| // Interface tables (itabs) |
| var ITab *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{})) |
| |
| InterfaceSwitch = fromReflect(reflect.TypeOf(abi.InterfaceSwitch{})) |
| TypeAssert = fromReflect(reflect.TypeOf(abi.TypeAssert{})) |
| |
| ITab = fromReflect(reflect.TypeOf(abi.ITab{})) |
| |
| // Make sure abi functions are correct. These functions are used |
| // by the linker which doesn't have the ability to do type layout, |
| // so we check the functions it uses here. |
| ptrSize := types.PtrSize |
| 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.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) |
| } |
| if got, want := int64(abi.ITabTypeOff(ptrSize)), ITab.OffsetOf("Type"); got != want { |
| base.Fatalf("abi.ITabTypeOff() == %d, want %d", got, want) |
| } |
| } |
| |
| // 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 t |
| } |
| |
| // reflectToType converts from a reflect.Type (which is a compiler |
| // host type) to a *types.Type, which is a target type. The result |
| // 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: |
| return types.Types[types.TINT32] |
| case reflect.Uint8: |
| return types.Types[types.TUINT8] |
| case reflect.Uint16: |
| return types.Types[types.TUINT16] |
| case reflect.Uint32: |
| return types.Types[types.TUINT32] |
| case reflect.Uintptr: |
| return types.Types[types.TUINTPTR] |
| case reflect.Ptr, reflect.Func, reflect.UnsafePointer: |
| // 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: |
| fields := make([]*types.Field, rt.NumField()) |
| for i := 0; i < rt.NumField(); i++ { |
| f := rt.Field(i) |
| ft := reflectToType(f.Type) |
| fields[i] = &types.Field{Sym: &types.Sym{Name: f.Name}, Type: ft} |
| } |
| return types.NewStruct(fields) |
| default: |
| base.Fatalf("unhandled kind %s", rt.Kind()) |
| return nil |
| } |
| } |
| |
| // 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 |
| } |
| |
| // 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} |
| } |
| |
| // 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(c.lsym, int(c.offset), 0) |
| } else { |
| objw.SymPtr(c.lsym, int(c.offset), target, 0) |
| } |
| } |
| func (c Cursor) WritePtrWeak(target *obj.LSym) { |
| if c.typ.Kind() != types.TUINTPTR { |
| base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind()) |
| } |
| objw.SymPtrWeak(c.lsym, int(c.offset), target, 0) |
| } |
| 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(c.lsym, int(c.offset), val) |
| } |
| 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(c.lsym, int(c.offset), val) |
| } |
| 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.Uint16(c.lsym, int(c.offset), val) |
| } |
| 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)) |
| } |
| func (c Cursor) WriteBool(val bool) { |
| if c.typ.Kind() != types.TBOOL { |
| base.Fatalf("can't write bool, it has kind %s", c.typ.Kind()) |
| } |
| objw.Bool(c.lsym, int(c.offset), 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(c.lsym, int(c.offset), 0) |
| } else if weak { |
| objw.SymPtrWeakOff(c.lsym, int(c.offset), target) |
| } else { |
| 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{} |
| } |
| |
| func (c Cursor) Elem(i int64) Cursor { |
| if c.typ.Kind() != types.TARRAY { |
| base.Fatalf("can't call Elem on non-array %v", c.typ) |
| } |
| if i < 0 || i >= c.typ.NumElem() { |
| base.Fatalf("element access out of bounds [%d] in [0:%d]", i, c.typ.NumElem()) |
| } |
| elem := c.typ.Elem() |
| return Cursor{lsym: c.lsym, offset: c.offset + i*elem.Size(), typ: elem} |
| } |
| |
| 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} |
| } |
| |
| // ModifyArray converts a cursor pointing at a type [k]T to a cursor pointing |
| // at a type [n]T. |
| // Also returns the size delta, aka (n-k)*sizeof(T). |
| func (c Cursor) ModifyArray(n int) (ArrayCursor, int64) { |
| if c.typ.Kind() != types.TARRAY { |
| base.Fatalf("can't call ModifyArray on non-array %v", c.typ) |
| } |
| k := c.typ.NumElem() |
| return ArrayCursor{c: Cursor{lsym: c.lsym, offset: c.offset, typ: c.typ.Elem()}, n: n}, (int64(n) - k) * c.typ.Elem().Size() |
| } |