1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package rttype allows the compiler to share type information with
6// the runtime. The shared type information is stored in
7// internal/abi. This package translates those types from the host
8// machine on which the compiler runs to the target machine on which
9// the compiled program will run. In particular, this package handles
10// layout differences between e.g. a 64 bit compiler and 32 bit
11// target.
12package rttype
13
14import (
15	"cmd/compile/internal/base"
16	"cmd/compile/internal/objw"
17	"cmd/compile/internal/types"
18	"cmd/internal/obj"
19	"internal/abi"
20	"reflect"
21)
22
23// The type structures shared with the runtime.
24var Type *types.Type
25
26var ArrayType *types.Type
27var ChanType *types.Type
28var FuncType *types.Type
29var InterfaceType *types.Type
30var MapType *types.Type
31var PtrType *types.Type
32var SliceType *types.Type
33var StructType *types.Type
34
35// Types that are parts of the types above.
36var IMethod *types.Type
37var Method *types.Type
38var StructField *types.Type
39var UncommonType *types.Type
40
41// Type switches and asserts
42var InterfaceSwitch *types.Type
43var TypeAssert *types.Type
44
45// Interface tables (itabs)
46var ITab *types.Type
47
48func Init() {
49	// Note: this has to be called explicitly instead of being
50	// an init function so it runs after the types package has
51	// been properly initialized.
52	Type = fromReflect(reflect.TypeOf(abi.Type{}))
53	ArrayType = fromReflect(reflect.TypeOf(abi.ArrayType{}))
54	ChanType = fromReflect(reflect.TypeOf(abi.ChanType{}))
55	FuncType = fromReflect(reflect.TypeOf(abi.FuncType{}))
56	InterfaceType = fromReflect(reflect.TypeOf(abi.InterfaceType{}))
57	MapType = fromReflect(reflect.TypeOf(abi.MapType{}))
58	PtrType = fromReflect(reflect.TypeOf(abi.PtrType{}))
59	SliceType = fromReflect(reflect.TypeOf(abi.SliceType{}))
60	StructType = fromReflect(reflect.TypeOf(abi.StructType{}))
61
62	IMethod = fromReflect(reflect.TypeOf(abi.Imethod{}))
63	Method = fromReflect(reflect.TypeOf(abi.Method{}))
64	StructField = fromReflect(reflect.TypeOf(abi.StructField{}))
65	UncommonType = fromReflect(reflect.TypeOf(abi.UncommonType{}))
66
67	InterfaceSwitch = fromReflect(reflect.TypeOf(abi.InterfaceSwitch{}))
68	TypeAssert = fromReflect(reflect.TypeOf(abi.TypeAssert{}))
69
70	ITab = fromReflect(reflect.TypeOf(abi.ITab{}))
71
72	// Make sure abi functions are correct. These functions are used
73	// by the linker which doesn't have the ability to do type layout,
74	// so we check the functions it uses here.
75	ptrSize := types.PtrSize
76	if got, want := int64(abi.CommonSize(ptrSize)), Type.Size(); got != want {
77		base.Fatalf("abi.CommonSize() == %d, want %d", got, want)
78	}
79	if got, want := int64(abi.StructFieldSize(ptrSize)), StructField.Size(); got != want {
80		base.Fatalf("abi.StructFieldSize() == %d, want %d", got, want)
81	}
82	if got, want := int64(abi.UncommonSize()), UncommonType.Size(); got != want {
83		base.Fatalf("abi.UncommonSize() == %d, want %d", got, want)
84	}
85	if got, want := int64(abi.TFlagOff(ptrSize)), Type.OffsetOf("TFlag"); got != want {
86		base.Fatalf("abi.TFlagOff() == %d, want %d", got, want)
87	}
88	if got, want := int64(abi.ITabTypeOff(ptrSize)), ITab.OffsetOf("Type"); got != want {
89		base.Fatalf("abi.ITabTypeOff() == %d, want %d", got, want)
90	}
91}
92
93// fromReflect translates from a host type to the equivalent target type.
94func fromReflect(rt reflect.Type) *types.Type {
95	t := reflectToType(rt)
96	types.CalcSize(t)
97	return t
98}
99
100// reflectToType converts from a reflect.Type (which is a compiler
101// host type) to a *types.Type, which is a target type.  The result
102// must be CalcSize'd before using.
103func reflectToType(rt reflect.Type) *types.Type {
104	switch rt.Kind() {
105	case reflect.Bool:
106		return types.Types[types.TBOOL]
107	case reflect.Int:
108		return types.Types[types.TINT]
109	case reflect.Int32:
110		return types.Types[types.TINT32]
111	case reflect.Uint8:
112		return types.Types[types.TUINT8]
113	case reflect.Uint16:
114		return types.Types[types.TUINT16]
115	case reflect.Uint32:
116		return types.Types[types.TUINT32]
117	case reflect.Uintptr:
118		return types.Types[types.TUINTPTR]
119	case reflect.Ptr, reflect.Func, reflect.UnsafePointer:
120		// TODO: there's no mechanism to distinguish different pointer types,
121		// so we treat them all as unsafe.Pointer.
122		return types.Types[types.TUNSAFEPTR]
123	case reflect.Slice:
124		return types.NewSlice(reflectToType(rt.Elem()))
125	case reflect.Array:
126		return types.NewArray(reflectToType(rt.Elem()), int64(rt.Len()))
127	case reflect.Struct:
128		fields := make([]*types.Field, rt.NumField())
129		for i := 0; i < rt.NumField(); i++ {
130			f := rt.Field(i)
131			ft := reflectToType(f.Type)
132			fields[i] = &types.Field{Sym: &types.Sym{Name: f.Name}, Type: ft}
133		}
134		return types.NewStruct(fields)
135	default:
136		base.Fatalf("unhandled kind %s", rt.Kind())
137		return nil
138	}
139}
140
141// A Cursor represents a typed location inside a static variable where we
142// are going to write.
143type Cursor struct {
144	lsym   *obj.LSym
145	offset int64
146	typ    *types.Type
147}
148
149// NewCursor returns a cursor starting at lsym+off and having type t.
150func NewCursor(lsym *obj.LSym, off int64, t *types.Type) Cursor {
151	return Cursor{lsym: lsym, offset: off, typ: t}
152}
153
154// WritePtr writes a pointer "target" to the component at the location specified by c.
155func (c Cursor) WritePtr(target *obj.LSym) {
156	if c.typ.Kind() != types.TUNSAFEPTR {
157		base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind())
158	}
159	if target == nil {
160		objw.Uintptr(c.lsym, int(c.offset), 0)
161	} else {
162		objw.SymPtr(c.lsym, int(c.offset), target, 0)
163	}
164}
165func (c Cursor) WritePtrWeak(target *obj.LSym) {
166	if c.typ.Kind() != types.TUINTPTR {
167		base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind())
168	}
169	objw.SymPtrWeak(c.lsym, int(c.offset), target, 0)
170}
171func (c Cursor) WriteUintptr(val uint64) {
172	if c.typ.Kind() != types.TUINTPTR {
173		base.Fatalf("can't write uintptr, it has kind %s", c.typ.Kind())
174	}
175	objw.Uintptr(c.lsym, int(c.offset), val)
176}
177func (c Cursor) WriteUint32(val uint32) {
178	if c.typ.Kind() != types.TUINT32 {
179		base.Fatalf("can't write uint32, it has kind %s", c.typ.Kind())
180	}
181	objw.Uint32(c.lsym, int(c.offset), val)
182}
183func (c Cursor) WriteUint16(val uint16) {
184	if c.typ.Kind() != types.TUINT16 {
185		base.Fatalf("can't write uint16, it has kind %s", c.typ.Kind())
186	}
187	objw.Uint16(c.lsym, int(c.offset), val)
188}
189func (c Cursor) WriteUint8(val uint8) {
190	if c.typ.Kind() != types.TUINT8 {
191		base.Fatalf("can't write uint8, it has kind %s", c.typ.Kind())
192	}
193	objw.Uint8(c.lsym, int(c.offset), val)
194}
195func (c Cursor) WriteInt(val int64) {
196	if c.typ.Kind() != types.TINT {
197		base.Fatalf("can't write int, it has kind %s", c.typ.Kind())
198	}
199	objw.Uintptr(c.lsym, int(c.offset), uint64(val))
200}
201func (c Cursor) WriteInt32(val int32) {
202	if c.typ.Kind() != types.TINT32 {
203		base.Fatalf("can't write int32, it has kind %s", c.typ.Kind())
204	}
205	objw.Uint32(c.lsym, int(c.offset), uint32(val))
206}
207func (c Cursor) WriteBool(val bool) {
208	if c.typ.Kind() != types.TBOOL {
209		base.Fatalf("can't write bool, it has kind %s", c.typ.Kind())
210	}
211	objw.Bool(c.lsym, int(c.offset), val)
212}
213
214// WriteSymPtrOff writes a "pointer" to the given symbol. The symbol
215// is encoded as a uint32 offset from the start of the section.
216func (c Cursor) WriteSymPtrOff(target *obj.LSym, weak bool) {
217	if c.typ.Kind() != types.TINT32 && c.typ.Kind() != types.TUINT32 {
218		base.Fatalf("can't write SymPtr, it has kind %s", c.typ.Kind())
219	}
220	if target == nil {
221		objw.Uint32(c.lsym, int(c.offset), 0)
222	} else if weak {
223		objw.SymPtrWeakOff(c.lsym, int(c.offset), target)
224	} else {
225		objw.SymPtrOff(c.lsym, int(c.offset), target)
226	}
227}
228
229// WriteSlice writes a slice header to c. The pointer is target+off, the len and cap fields are given.
230func (c Cursor) WriteSlice(target *obj.LSym, off, len, cap int64) {
231	if c.typ.Kind() != types.TSLICE {
232		base.Fatalf("can't write slice, it has kind %s", c.typ.Kind())
233	}
234	objw.SymPtr(c.lsym, int(c.offset), target, int(off))
235	objw.Uintptr(c.lsym, int(c.offset)+types.PtrSize, uint64(len))
236	objw.Uintptr(c.lsym, int(c.offset)+2*types.PtrSize, uint64(cap))
237	// TODO: ability to switch len&cap. Maybe not needed here, as every caller
238	// passes the same thing for both?
239	if len != cap {
240		base.Fatalf("len != cap (%d != %d)", len, cap)
241	}
242}
243
244// Reloc adds a relocation from the current cursor position.
245// Reloc fills in Off and Siz fields. Caller should fill in the rest (Type, others).
246func (c Cursor) Reloc() *obj.Reloc {
247	r := obj.Addrel(c.lsym)
248	r.Off = int32(c.offset)
249	r.Siz = uint8(c.typ.Size())
250	return r
251}
252
253// Field selects the field with the given name from the struct pointed to by c.
254func (c Cursor) Field(name string) Cursor {
255	if c.typ.Kind() != types.TSTRUCT {
256		base.Fatalf("can't call Field on non-struct %v", c.typ)
257	}
258	for _, f := range c.typ.Fields() {
259		if f.Sym.Name == name {
260			return Cursor{lsym: c.lsym, offset: c.offset + f.Offset, typ: f.Type}
261		}
262	}
263	base.Fatalf("couldn't find field %s in %v", name, c.typ)
264	return Cursor{}
265}
266
267func (c Cursor) Elem(i int64) Cursor {
268	if c.typ.Kind() != types.TARRAY {
269		base.Fatalf("can't call Elem on non-array %v", c.typ)
270	}
271	if i < 0 || i >= c.typ.NumElem() {
272		base.Fatalf("element access out of bounds [%d] in [0:%d]", i, c.typ.NumElem())
273	}
274	elem := c.typ.Elem()
275	return Cursor{lsym: c.lsym, offset: c.offset + i*elem.Size(), typ: elem}
276}
277
278type ArrayCursor struct {
279	c Cursor // cursor pointing at first element
280	n int    // number of elements
281}
282
283// NewArrayCursor returns a cursor starting at lsym+off and having n copies of type t.
284func NewArrayCursor(lsym *obj.LSym, off int64, t *types.Type, n int) ArrayCursor {
285	return ArrayCursor{
286		c: NewCursor(lsym, off, t),
287		n: n,
288	}
289}
290
291// Elem selects element i of the array pointed to by c.
292func (a ArrayCursor) Elem(i int) Cursor {
293	if i < 0 || i >= a.n {
294		base.Fatalf("element index %d out of range [0:%d]", i, a.n)
295	}
296	return Cursor{lsym: a.c.lsym, offset: a.c.offset + int64(i)*a.c.typ.Size(), typ: a.c.typ}
297}
298
299// ModifyArray converts a cursor pointing at a type [k]T to a cursor pointing
300// at a type [n]T.
301// Also returns the size delta, aka (n-k)*sizeof(T).
302func (c Cursor) ModifyArray(n int) (ArrayCursor, int64) {
303	if c.typ.Kind() != types.TARRAY {
304		base.Fatalf("can't call ModifyArray on non-array %v", c.typ)
305	}
306	k := c.typ.NumElem()
307	return ArrayCursor{c: Cursor{lsym: c.lsym, offset: c.offset, typ: c.typ.Elem()}, n: n}, (int64(n) - k) * c.typ.Elem().Size()
308}
309