blob: b90e23dc5b4cfd6fae41d420de3e958a77db3a2c [file] [log] [blame]
// 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()
}