summaryrefslogtreecommitdiffstats
path: root/src/internal/abi
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/abi')
-rw-r--r--src/internal/abi/abi.go102
-rw-r--r--src/internal/abi/abi_amd64.go18
-rw-r--r--src/internal/abi/abi_arm64.go17
-rw-r--r--src/internal/abi/abi_generic.go38
-rw-r--r--src/internal/abi/abi_ppc64x.go19
-rw-r--r--src/internal/abi/abi_riscv64.go17
-rw-r--r--src/internal/abi/abi_test.go79
-rw-r--r--src/internal/abi/abi_test.s27
-rw-r--r--src/internal/abi/compiletype.go167
-rw-r--r--src/internal/abi/export_test.go14
-rw-r--r--src/internal/abi/funcpc.go31
-rw-r--r--src/internal/abi/funcpc_gccgo.go21
-rw-r--r--src/internal/abi/map.go14
-rw-r--r--src/internal/abi/stack.go33
-rw-r--r--src/internal/abi/stub.s7
-rw-r--r--src/internal/abi/symtab.go106
-rw-r--r--src/internal/abi/testdata/x.go22
-rw-r--r--src/internal/abi/testdata/x.s6
-rw-r--r--src/internal/abi/type.go712
-rw-r--r--src/internal/abi/unsafestring_go119.go32
-rw-r--r--src/internal/abi/unsafestring_go120.go18
21 files changed, 1500 insertions, 0 deletions
diff --git a/src/internal/abi/abi.go b/src/internal/abi/abi.go
new file mode 100644
index 0000000..e1c8adc
--- /dev/null
+++ b/src/internal/abi/abi.go
@@ -0,0 +1,102 @@
+// Copyright 2020 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 abi
+
+import (
+ "internal/goarch"
+ "unsafe"
+)
+
+// RegArgs is a struct that has space for each argument
+// and return value register on the current architecture.
+//
+// Assembly code knows the layout of the first two fields
+// of RegArgs.
+//
+// RegArgs also contains additional space to hold pointers
+// when it may not be safe to keep them only in the integer
+// register space otherwise.
+type RegArgs struct {
+ // Values in these slots should be precisely the bit-by-bit
+ // representation of how they would appear in a register.
+ //
+ // This means that on big endian arches, integer values should
+ // be in the top bits of the slot. Floats are usually just
+ // directly represented, but some architectures treat narrow
+ // width floating point values specially (e.g. they're promoted
+ // first, or they need to be NaN-boxed).
+ Ints [IntArgRegs]uintptr // untyped integer registers
+ Floats [FloatArgRegs]uint64 // untyped float registers
+
+ // Fields above this point are known to assembly.
+
+ // Ptrs is a space that duplicates Ints but with pointer type,
+ // used to make pointers passed or returned in registers
+ // visible to the GC by making the type unsafe.Pointer.
+ Ptrs [IntArgRegs]unsafe.Pointer
+
+ // ReturnIsPtr is a bitmap that indicates which registers
+ // contain or will contain pointers on the return path from
+ // a reflectcall. The i'th bit indicates whether the i'th
+ // register contains or will contain a valid Go pointer.
+ ReturnIsPtr IntArgRegBitmap
+}
+
+func (r *RegArgs) Dump() {
+ print("Ints:")
+ for _, x := range r.Ints {
+ print(" ", x)
+ }
+ println()
+ print("Floats:")
+ for _, x := range r.Floats {
+ print(" ", x)
+ }
+ println()
+ print("Ptrs:")
+ for _, x := range r.Ptrs {
+ print(" ", x)
+ }
+ println()
+}
+
+// IntRegArgAddr returns a pointer inside of r.Ints[reg] that is appropriately
+// offset for an argument of size argSize.
+//
+// argSize must be non-zero, fit in a register, and a power-of-two.
+//
+// This method is a helper for dealing with the endianness of different CPU
+// architectures, since sub-word-sized arguments in big endian architectures
+// need to be "aligned" to the upper edge of the register to be interpreted
+// by the CPU correctly.
+func (r *RegArgs) IntRegArgAddr(reg int, argSize uintptr) unsafe.Pointer {
+ if argSize > goarch.PtrSize || argSize == 0 || argSize&(argSize-1) != 0 {
+ panic("invalid argSize")
+ }
+ offset := uintptr(0)
+ if goarch.BigEndian {
+ offset = goarch.PtrSize - argSize
+ }
+ return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset)
+}
+
+// IntArgRegBitmap is a bitmap large enough to hold one bit per
+// integer argument/return register.
+type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8
+
+// Set sets the i'th bit of the bitmap to 1.
+func (b *IntArgRegBitmap) Set(i int) {
+ b[i/8] |= uint8(1) << (i % 8)
+}
+
+// Get returns whether the i'th bit of the bitmap is set.
+//
+// nosplit because it's called in extremely sensitive contexts, like
+// on the reflectcall return path.
+//
+//go:nosplit
+func (b *IntArgRegBitmap) Get(i int) bool {
+ return b[i/8]&(uint8(1)<<(i%8)) != 0
+}
diff --git a/src/internal/abi/abi_amd64.go b/src/internal/abi/abi_amd64.go
new file mode 100644
index 0000000..d3c5678
--- /dev/null
+++ b/src/internal/abi/abi_amd64.go
@@ -0,0 +1,18 @@
+// Copyright 2020 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 abi
+
+const (
+ // See abi_generic.go.
+
+ // RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11.
+ IntArgRegs = 9
+
+ // X0 -> X14.
+ FloatArgRegs = 15
+
+ // We use SSE2 registers which support 64-bit float operations.
+ EffectiveFloatRegSize = 8
+)
diff --git a/src/internal/abi/abi_arm64.go b/src/internal/abi/abi_arm64.go
new file mode 100644
index 0000000..4dc5143
--- /dev/null
+++ b/src/internal/abi/abi_arm64.go
@@ -0,0 +1,17 @@
+// Copyright 2021 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 abi
+
+const (
+ // See abi_generic.go.
+
+ // R0 - R15.
+ IntArgRegs = 16
+
+ // F0 - F15.
+ FloatArgRegs = 16
+
+ EffectiveFloatRegSize = 8
+)
diff --git a/src/internal/abi/abi_generic.go b/src/internal/abi/abi_generic.go
new file mode 100644
index 0000000..76ef2e2
--- /dev/null
+++ b/src/internal/abi/abi_generic.go
@@ -0,0 +1,38 @@
+// Copyright 2020 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.
+
+//go:build !goexperiment.regabiargs && !amd64 && !arm64 && !ppc64 && !ppc64le && !riscv64
+
+package abi
+
+const (
+ // ABI-related constants.
+ //
+ // In the generic case, these are all zero
+ // which lets them gracefully degrade to ABI0.
+
+ // IntArgRegs is the number of registers dedicated
+ // to passing integer argument values. Result registers are identical
+ // to argument registers, so this number is used for those too.
+ IntArgRegs = 0
+
+ // FloatArgRegs is the number of registers dedicated
+ // to passing floating-point argument values. Result registers are
+ // identical to argument registers, so this number is used for
+ // those too.
+ FloatArgRegs = 0
+
+ // EffectiveFloatRegSize describes the width of floating point
+ // registers on the current platform from the ABI's perspective.
+ //
+ // Since Go only supports 32-bit and 64-bit floating point primitives,
+ // this number should be either 0, 4, or 8. 0 indicates no floating
+ // point registers for the ABI or that floating point values will be
+ // passed via the softfloat ABI.
+ //
+ // For platforms that support larger floating point register widths,
+ // such as x87's 80-bit "registers" (not that we support x87 currently),
+ // use 8.
+ EffectiveFloatRegSize = 0
+)
diff --git a/src/internal/abi/abi_ppc64x.go b/src/internal/abi/abi_ppc64x.go
new file mode 100644
index 0000000..73416d7
--- /dev/null
+++ b/src/internal/abi/abi_ppc64x.go
@@ -0,0 +1,19 @@
+// Copyright 2021 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.
+
+//go:build ppc64 || ppc64le
+
+package abi
+
+const (
+ // See abi_generic.go.
+
+ // R3 - R10, R14 - R17.
+ IntArgRegs = 12
+
+ // F1 - F12.
+ FloatArgRegs = 12
+
+ EffectiveFloatRegSize = 8
+)
diff --git a/src/internal/abi/abi_riscv64.go b/src/internal/abi/abi_riscv64.go
new file mode 100644
index 0000000..2bcd9d6
--- /dev/null
+++ b/src/internal/abi/abi_riscv64.go
@@ -0,0 +1,17 @@
+// Copyright 2022 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 abi
+
+const (
+ // See abi_generic.go.
+
+ // X8 - X23
+ IntArgRegs = 16
+
+ // F8 - F23.
+ FloatArgRegs = 16
+
+ EffectiveFloatRegSize = 8
+)
diff --git a/src/internal/abi/abi_test.go b/src/internal/abi/abi_test.go
new file mode 100644
index 0000000..44b9e78
--- /dev/null
+++ b/src/internal/abi/abi_test.go
@@ -0,0 +1,79 @@
+// Copyright 2021 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 abi_test
+
+import (
+ "internal/abi"
+ "internal/testenv"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestFuncPC(t *testing.T) {
+ // Test that FuncPC* can get correct function PC.
+ pcFromAsm := abi.FuncPCTestFnAddr
+
+ // Test FuncPC for locally defined function
+ pcFromGo := abi.FuncPCTest()
+ if pcFromGo != pcFromAsm {
+ t.Errorf("FuncPC returns wrong PC, want %x, got %x", pcFromAsm, pcFromGo)
+ }
+
+ // Test FuncPC for imported function
+ pcFromGo = abi.FuncPCABI0(abi.FuncPCTestFn)
+ if pcFromGo != pcFromAsm {
+ t.Errorf("FuncPC returns wrong PC, want %x, got %x", pcFromAsm, pcFromGo)
+ }
+}
+
+func TestFuncPCCompileError(t *testing.T) {
+ // Test that FuncPC* on a function of a mismatched ABI is rejected.
+ testenv.MustHaveGoBuild(t)
+
+ // We want to test internal package, which we cannot normally import.
+ // Run the assembler and compiler manually.
+ tmpdir := t.TempDir()
+ asmSrc := filepath.Join("testdata", "x.s")
+ goSrc := filepath.Join("testdata", "x.go")
+ symabi := filepath.Join(tmpdir, "symabi")
+ obj := filepath.Join(tmpdir, "x.o")
+
+ // Write an importcfg file for the dependencies of the package.
+ importcfgfile := filepath.Join(tmpdir, "hello.importcfg")
+ testenv.WriteImportcfg(t, importcfgfile, nil, "internal/abi")
+
+ // parse assembly code for symabi.
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-gensymabis", "-o", symabi, asmSrc)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go tool asm -gensymabis failed: %v\n%s", err, out)
+ }
+
+ // compile go code.
+ cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-symabis", symabi, "-o", obj, goSrc)
+ out, err = cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("go tool compile did not fail")
+ }
+
+ // Expect errors in line 17, 18, 20, no errors on other lines.
+ want := []string{"x.go:17", "x.go:18", "x.go:20"}
+ got := strings.Split(string(out), "\n")
+ if got[len(got)-1] == "" {
+ got = got[:len(got)-1] // remove last empty line
+ }
+ for i, s := range got {
+ if !strings.Contains(s, want[i]) {
+ t.Errorf("did not error on line %s", want[i])
+ }
+ }
+ if len(got) != len(want) {
+ t.Errorf("unexpected number of errors, want %d, got %d", len(want), len(got))
+ }
+ if t.Failed() {
+ t.Logf("output:\n%s", string(out))
+ }
+}
diff --git a/src/internal/abi/abi_test.s b/src/internal/abi/abi_test.s
new file mode 100644
index 0000000..93ace3e
--- /dev/null
+++ b/src/internal/abi/abi_test.s
@@ -0,0 +1,27 @@
+// Copyright 2021 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.
+
+#include "textflag.h"
+
+#ifdef GOARCH_386
+#define PTRSIZE 4
+#endif
+#ifdef GOARCH_arm
+#define PTRSIZE 4
+#endif
+#ifdef GOARCH_mips
+#define PTRSIZE 4
+#endif
+#ifdef GOARCH_mipsle
+#define PTRSIZE 4
+#endif
+#ifndef PTRSIZE
+#define PTRSIZE 8
+#endif
+
+TEXT internal∕abi·FuncPCTestFn(SB),NOSPLIT,$0-0
+ RET
+
+GLOBL internal∕abi·FuncPCTestFnAddr(SB), NOPTR, $PTRSIZE
+DATA internal∕abi·FuncPCTestFnAddr(SB)/PTRSIZE, $internal∕abi·FuncPCTestFn(SB)
diff --git a/src/internal/abi/compiletype.go b/src/internal/abi/compiletype.go
new file mode 100644
index 0000000..d92adde
--- /dev/null
+++ b/src/internal/abi/compiletype.go
@@ -0,0 +1,167 @@
+// 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 abi
+
+// These functions are the build-time version of the Go type data structures.
+
+// Their contents must be kept in sync with their definitions.
+// Because the host and target type sizes can differ, the compiler and
+// linker cannot use the host information that they might get from
+// either unsafe.Sizeof and Alignof, nor runtime, reflect, or reflectlite.
+
+// CommonSize returns sizeof(Type) for a compilation target with a given ptrSize
+func CommonSize(ptrSize int) int { return 4*ptrSize + 8 + 8 }
+
+// StructFieldSize returns sizeof(StructField) for a compilation target with a given ptrSize
+func StructFieldSize(ptrSize int) int { return 3 * ptrSize }
+
+// UncommonSize returns sizeof(UncommonType). This currently does not depend on 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 }
+
+// KindOff returns the offset of Type.Kind_ for a compilation target with a given ptrSize
+func KindOff(ptrSize int) int { return 2*ptrSize + 7 }
+
+// SizeOff returns the offset of Type.Size_ for a compilation target with a given ptrSize
+func SizeOff(ptrSize int) int { return 0 }
+
+// PtrBytes returns the offset of Type.PtrBytes for a compilation target with a given ptrSize
+func PtrBytesOff(ptrSize int) int { return ptrSize }
+
+// 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)
+}
diff --git a/src/internal/abi/export_test.go b/src/internal/abi/export_test.go
new file mode 100644
index 0000000..2a87e9d
--- /dev/null
+++ b/src/internal/abi/export_test.go
@@ -0,0 +1,14 @@
+// Copyright 2021 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 abi
+
+func FuncPCTestFn()
+
+var FuncPCTestFnAddr uintptr // address of FuncPCTestFn, directly retrieved from assembly
+
+//go:noinline
+func FuncPCTest() uintptr {
+ return FuncPCABI0(FuncPCTestFn)
+}
diff --git a/src/internal/abi/funcpc.go b/src/internal/abi/funcpc.go
new file mode 100644
index 0000000..e038d36
--- /dev/null
+++ b/src/internal/abi/funcpc.go
@@ -0,0 +1,31 @@
+// 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.
+
+//go:build !gccgo
+
+package abi
+
+// FuncPC* intrinsics.
+//
+// CAREFUL: In programs with plugins, FuncPC* can return different values
+// for the same function (because there are actually multiple copies of
+// the same function in the address space). To be safe, don't use the
+// results of this function in any == expression. It is only safe to
+// use the result as an address at which to start executing code.
+
+// FuncPCABI0 returns the entry PC of the function f, which must be a
+// direct reference of a function defined as ABI0. Otherwise it is a
+// compile-time error.
+//
+// Implemented as a compile intrinsic.
+func FuncPCABI0(f interface{}) uintptr
+
+// FuncPCABIInternal returns the entry PC of the function f. If f is a
+// direct reference of a function, it must be defined as ABIInternal.
+// Otherwise it is a compile-time error. If f is not a direct reference
+// of a defined function, it assumes that f is a func value. Otherwise
+// the behavior is undefined.
+//
+// Implemented as a compile intrinsic.
+func FuncPCABIInternal(f interface{}) uintptr
diff --git a/src/internal/abi/funcpc_gccgo.go b/src/internal/abi/funcpc_gccgo.go
new file mode 100644
index 0000000..ad5fa52
--- /dev/null
+++ b/src/internal/abi/funcpc_gccgo.go
@@ -0,0 +1,21 @@
+// 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.
+
+// For bootstrapping with gccgo.
+
+//go:build gccgo
+
+package abi
+
+import "unsafe"
+
+func FuncPCABI0(f interface{}) uintptr {
+ words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f))
+ return *(*uintptr)(unsafe.Pointer(words[1]))
+}
+
+func FuncPCABIInternal(f interface{}) uintptr {
+ words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f))
+ return *(*uintptr)(unsafe.Pointer(words[1]))
+}
diff --git a/src/internal/abi/map.go b/src/internal/abi/map.go
new file mode 100644
index 0000000..e5b0a0b
--- /dev/null
+++ b/src/internal/abi/map.go
@@ -0,0 +1,14 @@
+// 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 abi
+
+// Map constants common to several packages
+// runtime/runtime-gdb.py:MapTypePrinter contains its own copy
+const (
+ MapBucketCountBits = 3 // log2 of number of elements in a bucket.
+ MapBucketCount = 1 << MapBucketCountBits
+ MapMaxKeyBytes = 128 // Must fit in a uint8.
+ MapMaxElemBytes = 128 // Must fit in a uint8.
+)
diff --git a/src/internal/abi/stack.go b/src/internal/abi/stack.go
new file mode 100644
index 0000000..8e3327e
--- /dev/null
+++ b/src/internal/abi/stack.go
@@ -0,0 +1,33 @@
+// 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 abi
+
+const (
+ // StackNosplitBase is the base maximum number of bytes that a chain of
+ // NOSPLIT functions can use.
+ //
+ // This value must be multiplied by the stack guard multiplier, so do not
+ // use it directly. See runtime/stack.go:stackNosplit and
+ // cmd/internal/objabi/stack.go:StackNosplit.
+ StackNosplitBase = 800
+
+ // We have three different sequences for stack bounds checks, depending on
+ // whether the stack frame of a function is small, big, or huge.
+
+ // After a stack split check the SP is allowed to be StackSmall bytes below
+ // the stack guard.
+ //
+ // Functions that need frames <= StackSmall can perform the stack check
+ // using a single comparison directly between the stack guard and the SP
+ // because we ensure that StackSmall bytes of stack space are available
+ // beyond the stack guard.
+ StackSmall = 128
+
+ // Functions that need frames <= StackBig can assume that neither
+ // SP-framesize nor stackGuard-StackSmall will underflow, and thus use a
+ // more efficient check. In order to ensure this, StackBig must be <= the
+ // size of the unmapped space at zero.
+ StackBig = 4096
+)
diff --git a/src/internal/abi/stub.s b/src/internal/abi/stub.s
new file mode 100644
index 0000000..5bad98d
--- /dev/null
+++ b/src/internal/abi/stub.s
@@ -0,0 +1,7 @@
+// 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.
+
+// This file silences errors about body-less functions
+// that are provided by intrinsics in the latest version of the compiler,
+// but may not be known to the bootstrap compiler.
diff --git a/src/internal/abi/symtab.go b/src/internal/abi/symtab.go
new file mode 100644
index 0000000..bf6ea82
--- /dev/null
+++ b/src/internal/abi/symtab.go
@@ -0,0 +1,106 @@
+// 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 abi
+
+// A FuncFlag records bits about a function, passed to the runtime.
+type FuncFlag uint8
+
+const (
+ // FuncFlagTopFrame indicates a function that appears at the top of its stack.
+ // The traceback routine stop at such a function and consider that a
+ // successful, complete traversal of the stack.
+ // Examples of TopFrame functions include goexit, which appears
+ // at the top of a user goroutine stack, and mstart, which appears
+ // at the top of a system goroutine stack.
+ FuncFlagTopFrame FuncFlag = 1 << iota
+
+ // FuncFlagSPWrite indicates a function that writes an arbitrary value to SP
+ // (any write other than adding or subtracting a constant amount).
+ // The traceback routines cannot encode such changes into the
+ // pcsp tables, so the function traceback cannot safely unwind past
+ // SPWrite functions. Stopping at an SPWrite function is considered
+ // to be an incomplete unwinding of the stack. In certain contexts
+ // (in particular garbage collector stack scans) that is a fatal error.
+ FuncFlagSPWrite
+
+ // FuncFlagAsm indicates that a function was implemented in assembly.
+ FuncFlagAsm
+)
+
+// A FuncID identifies particular functions that need to be treated
+// specially by the runtime.
+// Note that in some situations involving plugins, there may be multiple
+// copies of a particular special runtime function.
+type FuncID uint8
+
+const (
+ // If you add a FuncID, you probably also want to add an entry to the map in
+ // ../../cmd/internal/objabi/funcid.go
+
+ FuncIDNormal FuncID = iota // not a special function
+ FuncID_abort
+ FuncID_asmcgocall
+ FuncID_asyncPreempt
+ FuncID_cgocallback
+ FuncID_debugCallV2
+ FuncID_gcBgMarkWorker
+ FuncID_goexit
+ FuncID_gogo
+ FuncID_gopanic
+ FuncID_handleAsyncEvent
+ FuncID_mcall
+ FuncID_morestack
+ FuncID_mstart
+ FuncID_panicwrap
+ FuncID_rt0_go
+ FuncID_runfinq
+ FuncID_runtime_main
+ FuncID_sigpanic
+ FuncID_systemstack
+ FuncID_systemstack_switch
+ FuncIDWrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
+)
+
+// ArgsSizeUnknown is set in Func.argsize to mark all functions
+// whose argument size is unknown (C vararg functions, and
+// assembly code without an explicit specification).
+// This value is generated by the compiler, assembler, or linker.
+const ArgsSizeUnknown = -0x80000000
+
+// IDs for PCDATA and FUNCDATA tables in Go binaries.
+//
+// These must agree with ../../../runtime/funcdata.h.
+const (
+ PCDATA_UnsafePoint = 0
+ PCDATA_StackMapIndex = 1
+ PCDATA_InlTreeIndex = 2
+ PCDATA_ArgLiveIndex = 3
+
+ FUNCDATA_ArgsPointerMaps = 0
+ FUNCDATA_LocalsPointerMaps = 1
+ FUNCDATA_StackObjects = 2
+ FUNCDATA_InlTree = 3
+ FUNCDATA_OpenCodedDeferInfo = 4
+ FUNCDATA_ArgInfo = 5
+ FUNCDATA_ArgLiveInfo = 6
+ FUNCDATA_WrapInfo = 7
+)
+
+// Special values for the PCDATA_UnsafePoint table.
+const (
+ UnsafePointSafe = -1 // Safe for async preemption
+ UnsafePointUnsafe = -2 // Unsafe for async preemption
+
+ // UnsafePointRestart1(2) apply on a sequence of instructions, within
+ // which if an async preemption happens, we should back off the PC
+ // to the start of the sequence when resuming.
+ // We need two so we can distinguish the start/end of the sequence
+ // in case that two sequences are next to each other.
+ UnsafePointRestart1 = -3
+ UnsafePointRestart2 = -4
+
+ // Like UnsafePointRestart1, but back to function entry if async preempted.
+ UnsafePointRestartAtEntry = -5
+)
diff --git a/src/internal/abi/testdata/x.go b/src/internal/abi/testdata/x.go
new file mode 100644
index 0000000..cae103d
--- /dev/null
+++ b/src/internal/abi/testdata/x.go
@@ -0,0 +1,22 @@
+// Copyright 2021 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 x
+
+import "internal/abi"
+
+func Fn0() // defined in assembly
+
+func Fn1() {}
+
+var FnExpr func()
+
+func test() {
+ _ = abi.FuncPCABI0(Fn0) // line 16, no error
+ _ = abi.FuncPCABIInternal(Fn0) // line 17, error
+ _ = abi.FuncPCABI0(Fn1) // line 18, error
+ _ = abi.FuncPCABIInternal(Fn1) // line 19, no error
+ _ = abi.FuncPCABI0(FnExpr) // line 20, error
+ _ = abi.FuncPCABIInternal(FnExpr) // line 21, no error
+}
diff --git a/src/internal/abi/testdata/x.s b/src/internal/abi/testdata/x.s
new file mode 100644
index 0000000..63c1385
--- /dev/null
+++ b/src/internal/abi/testdata/x.s
@@ -0,0 +1,6 @@
+// Copyright 2021 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.
+
+TEXT ·Fn0(SB), 0, $0-0
+ RET
diff --git a/src/internal/abi/type.go b/src/internal/abi/type.go
new file mode 100644
index 0000000..4794f5a
--- /dev/null
+++ b/src/internal/abi/type.go
@@ -0,0 +1,712 @@
+// 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 abi
+
+import (
+ "unsafe"
+)
+
+// Type is the runtime representation of a Go type.
+//
+// Type is also referenced implicitly
+// (in the form of expressions involving constants and arch.PtrSize)
+// in cmd/compile/internal/reflectdata/reflect.go
+// and cmd/link/internal/ld/decodesym.go
+// (e.g. data[2*arch.PtrSize+4] references the TFlag field)
+// unsafe.OffsetOf(Type{}.TFlag) cannot be used directly in those
+// places because it varies with cross compilation and experiments.
+type Type struct {
+ Size_ uintptr
+ PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers
+ Hash uint32 // hash of type; avoids computation in hash tables
+ TFlag TFlag // extra type information flags
+ Align_ uint8 // alignment of variable with this type
+ FieldAlign_ uint8 // alignment of struct field with this type
+ Kind_ uint8 // enumeration for C
+ // function for comparing objects of this type
+ // (ptr to object A, ptr to object B) -> ==?
+ Equal func(unsafe.Pointer, unsafe.Pointer) bool
+ // GCData stores the GC type data for the garbage collector.
+ // If the KindGCProg bit is set in kind, GCData is a GC program.
+ // Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
+ GCData *byte
+ Str NameOff // string form
+ PtrToThis TypeOff // type for pointer to this type, may be zero
+}
+
+// A Kind represents the specific kind of type that a Type represents.
+// The zero Kind is not a valid kind.
+type Kind uint
+
+const (
+ Invalid Kind = iota
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ Array
+ Chan
+ Func
+ Interface
+ Map
+ Pointer
+ Slice
+ String
+ Struct
+ UnsafePointer
+)
+
+const (
+ // TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible.
+ KindDirectIface = 1 << 5
+ KindGCProg = 1 << 6 // Type.gc points to GC program
+ KindMask = (1 << 5) - 1
+)
+
+// TFlag is used by a Type to signal what extra type information is
+// available in the memory directly following the Type value.
+type TFlag uint8
+
+const (
+ // TFlagUncommon means that there is a data with a type, UncommonType,
+ // just beyond the shared-per-type common data. That is, the data
+ // for struct types will store their UncommonType at one offset, the
+ // data for interface types will store their UncommonType at a different
+ // offset. UncommonType is always accessed via a pointer that is computed
+ // using trust-us-we-are-the-implementors pointer arithmetic.
+ //
+ // For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0,
+ // then t has UncommonType data and it can be accessed as:
+ //
+ // type structTypeUncommon struct {
+ // structType
+ // u UncommonType
+ // }
+ // u := &(*structTypeUncommon)(unsafe.Pointer(t)).u
+ TFlagUncommon TFlag = 1 << 0
+
+ // TFlagExtraStar means the name in the str field has an
+ // extraneous '*' prefix. This is because for most types T in
+ // a program, the type *T also exists and reusing the str data
+ // saves binary size.
+ TFlagExtraStar TFlag = 1 << 1
+
+ // TFlagNamed means the type has a name.
+ TFlagNamed TFlag = 1 << 2
+
+ // TFlagRegularMemory means that equal and hash functions can treat
+ // this type as a single region of t.size bytes.
+ TFlagRegularMemory TFlag = 1 << 3
+)
+
+// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime.
+type NameOff int32
+
+// TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime.
+type TypeOff int32
+
+// TextOff is an offset from the top of a text section. See (rtype).textOff in runtime.
+type TextOff int32
+
+// String returns the name of k.
+func (k Kind) String() string {
+ if int(k) < len(kindNames) {
+ return kindNames[k]
+ }
+ return kindNames[0]
+}
+
+var kindNames = []string{
+ Invalid: "invalid",
+ Bool: "bool",
+ Int: "int",
+ Int8: "int8",
+ Int16: "int16",
+ Int32: "int32",
+ Int64: "int64",
+ Uint: "uint",
+ Uint8: "uint8",
+ Uint16: "uint16",
+ Uint32: "uint32",
+ Uint64: "uint64",
+ Uintptr: "uintptr",
+ Float32: "float32",
+ Float64: "float64",
+ Complex64: "complex64",
+ Complex128: "complex128",
+ Array: "array",
+ Chan: "chan",
+ Func: "func",
+ Interface: "interface",
+ Map: "map",
+ Pointer: "ptr",
+ Slice: "slice",
+ String: "string",
+ Struct: "struct",
+ UnsafePointer: "unsafe.Pointer",
+}
+
+func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) }
+
+func (t *Type) HasName() bool {
+ return t.TFlag&TFlagNamed != 0
+}
+
+func (t *Type) Pointers() bool { return t.PtrBytes != 0 }
+
+// IfaceIndir reports whether t is stored indirectly in an interface value.
+func (t *Type) IfaceIndir() bool {
+ return t.Kind_&KindDirectIface == 0
+}
+
+// isDirectIface reports whether t is stored directly in an interface value.
+func (t *Type) IsDirectIface() bool {
+ return t.Kind_&KindDirectIface != 0
+}
+
+func (t *Type) GcSlice(begin, end uintptr) []byte {
+ return unsafeSliceFor(t.GCData, int(end))[begin:]
+}
+
+// Method on non-interface type
+type Method struct {
+ Name NameOff // name of method
+ Mtyp TypeOff // method type (without receiver)
+ Ifn TextOff // fn used in interface call (one-word receiver)
+ Tfn TextOff // fn used for normal method call
+}
+
+// UncommonType is present only for defined types or types with methods
+// (if T is a defined type, the uncommonTypes for T and *T have methods).
+// Using a pointer to this struct reduces the overall size required
+// to describe a non-defined type with no methods.
+type UncommonType struct {
+ PkgPath NameOff // import path; empty for built-in types like int, string
+ Mcount uint16 // number of methods
+ Xcount uint16 // number of exported methods
+ Moff uint32 // offset from this uncommontype to [mcount]Method
+ _ uint32 // unused
+}
+
+func (t *UncommonType) Methods() []Method {
+ if t.Mcount == 0 {
+ return nil
+ }
+ return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount]
+}
+
+func (t *UncommonType) ExportedMethods() []Method {
+ if t.Xcount == 0 {
+ return nil
+ }
+ return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount]
+}
+
+// addChecked returns p+x.
+//
+// The whySafe string is ignored, so that the function still inlines
+// as efficiently as p+x, but all call sites should use the string to
+// record why the addition is safe, which is to say why the addition
+// does not cause x to advance to the very end of p's allocation
+// and therefore point incorrectly at the next block in memory.
+func addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
+ return unsafe.Pointer(uintptr(p) + x)
+}
+
+// Imethod represents a method on an interface type
+type Imethod struct {
+ Name NameOff // name of method
+ Typ TypeOff // .(*FuncType) underneath
+}
+
+// ArrayType represents a fixed array type.
+type ArrayType struct {
+ Type
+ Elem *Type // array element type
+ Slice *Type // slice type
+ Len uintptr
+}
+
+// Len returns the length of t if t is an array type, otherwise 0
+func (t *Type) Len() int {
+ if t.Kind() == Array {
+ return int((*ArrayType)(unsafe.Pointer(t)).Len)
+ }
+ return 0
+}
+
+func (t *Type) Common() *Type {
+ return t
+}
+
+type ChanDir int
+
+const (
+ RecvDir ChanDir = 1 << iota // <-chan
+ SendDir // chan<-
+ BothDir = RecvDir | SendDir // chan
+ InvalidDir ChanDir = 0
+)
+
+// ChanType represents a channel type
+type ChanType struct {
+ Type
+ Elem *Type
+ Dir ChanDir
+}
+
+type structTypeUncommon struct {
+ StructType
+ u UncommonType
+}
+
+// ChanDir returns the direction of t if t is a channel type, otherwise InvalidDir (0).
+func (t *Type) ChanDir() ChanDir {
+ if t.Kind() == Chan {
+ ch := (*ChanType)(unsafe.Pointer(t))
+ return ch.Dir
+ }
+ return InvalidDir
+}
+
+// Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil
+func (t *Type) Uncommon() *UncommonType {
+ if t.TFlag&TFlagUncommon == 0 {
+ return nil
+ }
+ switch t.Kind() {
+ case Struct:
+ return &(*structTypeUncommon)(unsafe.Pointer(t)).u
+ case Pointer:
+ type u struct {
+ PtrType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ case Func:
+ type u struct {
+ FuncType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ case Slice:
+ type u struct {
+ SliceType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ case Array:
+ type u struct {
+ ArrayType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ case Chan:
+ type u struct {
+ ChanType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ case Map:
+ type u struct {
+ MapType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ case Interface:
+ type u struct {
+ InterfaceType
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ default:
+ type u struct {
+ Type
+ u UncommonType
+ }
+ return &(*u)(unsafe.Pointer(t)).u
+ }
+}
+
+// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil.
+func (t *Type) Elem() *Type {
+ switch t.Kind() {
+ case Array:
+ tt := (*ArrayType)(unsafe.Pointer(t))
+ return tt.Elem
+ case Chan:
+ tt := (*ChanType)(unsafe.Pointer(t))
+ return tt.Elem
+ case Map:
+ tt := (*MapType)(unsafe.Pointer(t))
+ return tt.Elem
+ case Pointer:
+ tt := (*PtrType)(unsafe.Pointer(t))
+ return tt.Elem
+ case Slice:
+ tt := (*SliceType)(unsafe.Pointer(t))
+ return tt.Elem
+ }
+ return nil
+}
+
+// StructType returns t cast to a *StructType, or nil if its tag does not match.
+func (t *Type) StructType() *StructType {
+ if t.Kind() != Struct {
+ return nil
+ }
+ return (*StructType)(unsafe.Pointer(t))
+}
+
+// MapType returns t cast to a *MapType, or nil if its tag does not match.
+func (t *Type) MapType() *MapType {
+ if t.Kind() != Map {
+ return nil
+ }
+ return (*MapType)(unsafe.Pointer(t))
+}
+
+// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match.
+func (t *Type) ArrayType() *ArrayType {
+ if t.Kind() != Array {
+ return nil
+ }
+ return (*ArrayType)(unsafe.Pointer(t))
+}
+
+// FuncType returns t cast to a *FuncType, or nil if its tag does not match.
+func (t *Type) FuncType() *FuncType {
+ if t.Kind() != Func {
+ return nil
+ }
+ return (*FuncType)(unsafe.Pointer(t))
+}
+
+// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match.
+func (t *Type) InterfaceType() *InterfaceType {
+ if t.Kind() != Interface {
+ return nil
+ }
+ return (*InterfaceType)(unsafe.Pointer(t))
+}
+
+// Size returns the size of data with type t.
+func (t *Type) Size() uintptr { return t.Size_ }
+
+// Align returns the alignment of data with type t.
+func (t *Type) Align() int { return int(t.Align_) }
+
+func (t *Type) FieldAlign() int { return int(t.FieldAlign_) }
+
+type InterfaceType struct {
+ Type
+ PkgPath Name // import path
+ Methods []Imethod // sorted by hash
+}
+
+func (t *Type) ExportedMethods() []Method {
+ ut := t.Uncommon()
+ if ut == nil {
+ return nil
+ }
+ return ut.ExportedMethods()
+}
+
+func (t *Type) NumMethod() int {
+ if t.Kind() == Interface {
+ tt := (*InterfaceType)(unsafe.Pointer(t))
+ return tt.NumMethod()
+ }
+ return len(t.ExportedMethods())
+}
+
+// NumMethod returns the number of interface methods in the type's method set.
+func (t *InterfaceType) NumMethod() int { return len(t.Methods) }
+
+type MapType struct {
+ Type
+ Key *Type
+ Elem *Type
+ Bucket *Type // internal type representing a hash bucket
+ // function for hashing keys (ptr to key, seed) -> hash
+ Hasher func(unsafe.Pointer, uintptr) uintptr
+ KeySize uint8 // size of key slot
+ ValueSize uint8 // size of elem slot
+ BucketSize uint16 // size of bucket
+ Flags uint32
+}
+
+// Note: flag values must match those used in the TMAP case
+// in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
+func (mt *MapType) IndirectKey() bool { // store ptr to key instead of key itself
+ return mt.Flags&1 != 0
+}
+func (mt *MapType) IndirectElem() bool { // store ptr to elem instead of elem itself
+ return mt.Flags&2 != 0
+}
+func (mt *MapType) ReflexiveKey() bool { // true if k==k for all keys
+ return mt.Flags&4 != 0
+}
+func (mt *MapType) NeedKeyUpdate() bool { // true if we need to update key on an overwrite
+ return mt.Flags&8 != 0
+}
+func (mt *MapType) HashMightPanic() bool { // true if hash function might panic
+ return mt.Flags&16 != 0
+}
+
+func (t *Type) Key() *Type {
+ if t.Kind() == Map {
+ return (*MapType)(unsafe.Pointer(t)).Key
+ }
+ return nil
+}
+
+type SliceType struct {
+ Type
+ Elem *Type // slice element type
+}
+
+// funcType represents a function type.
+//
+// A *Type for each in and out parameter is stored in an array that
+// directly follows the funcType (and possibly its uncommonType). So
+// a function type with one method, one input, and one output is:
+//
+// struct {
+// funcType
+// uncommonType
+// [2]*rtype // [0] is in, [1] is out
+// }
+type FuncType struct {
+ Type
+ InCount uint16
+ OutCount uint16 // top bit is set if last input parameter is ...
+}
+
+func (t *FuncType) In(i int) *Type {
+ return t.InSlice()[i]
+}
+
+func (t *FuncType) NumIn() int {
+ return int(t.InCount)
+}
+
+func (t *FuncType) NumOut() int {
+ return int(t.OutCount & (1<<15 - 1))
+}
+
+func (t *FuncType) Out(i int) *Type {
+ return (t.OutSlice()[i])
+}
+
+func (t *FuncType) InSlice() []*Type {
+ uadd := unsafe.Sizeof(*t)
+ if t.TFlag&TFlagUncommon != 0 {
+ uadd += unsafe.Sizeof(UncommonType{})
+ }
+ if t.InCount == 0 {
+ return nil
+ }
+ return (*[1 << 16]*Type)(addChecked(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.InCount:t.InCount]
+}
+func (t *FuncType) OutSlice() []*Type {
+ outCount := uint16(t.NumOut())
+ if outCount == 0 {
+ return nil
+ }
+ uadd := unsafe.Sizeof(*t)
+ if t.TFlag&TFlagUncommon != 0 {
+ uadd += unsafe.Sizeof(UncommonType{})
+ }
+ return (*[1 << 17]*Type)(addChecked(unsafe.Pointer(t), uadd, "outCount > 0"))[t.InCount : t.InCount+outCount : t.InCount+outCount]
+}
+
+func (t *FuncType) IsVariadic() bool {
+ return t.OutCount&(1<<15) != 0
+}
+
+type PtrType struct {
+ Type
+ Elem *Type // pointer element (pointed at) type
+}
+
+type StructField struct {
+ Name Name // name is always non-empty
+ Typ *Type // type of field
+ Offset uintptr // byte offset of field
+}
+
+func (f *StructField) Embedded() bool {
+ return f.Name.IsEmbedded()
+}
+
+type StructType struct {
+ Type
+ PkgPath Name
+ Fields []StructField
+}
+
+// Name is an encoded type Name with optional extra data.
+//
+// The first byte is a bit field containing:
+//
+// 1<<0 the name is exported
+// 1<<1 tag data follows the name
+// 1<<2 pkgPath nameOff follows the name and tag
+// 1<<3 the name is of an embedded (a.k.a. anonymous) field
+//
+// Following that, there is a varint-encoded length of the name,
+// followed by the name itself.
+//
+// If tag data is present, it also has a varint-encoded length
+// followed by the tag itself.
+//
+// If the import path follows, then 4 bytes at the end of
+// the data form a nameOff. The import path is only set for concrete
+// methods that are defined in a different package than their type.
+//
+// If a name starts with "*", then the exported bit represents
+// whether the pointed to type is exported.
+//
+// Note: this encoding must match here and in:
+// cmd/compile/internal/reflectdata/reflect.go
+// cmd/link/internal/ld/decodesym.go
+
+type Name struct {
+ Bytes *byte
+}
+
+// DataChecked does pointer arithmetic on n's Bytes, and that arithmetic is asserted to
+// be safe for the reason in whySafe (which can appear in a backtrace, etc.)
+func (n Name) DataChecked(off int, whySafe string) *byte {
+ return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), whySafe))
+}
+
+// Data does pointer arithmetic on n's Bytes, and that arithmetic is asserted to
+// be safe because the runtime made the call (other packages use DataChecked)
+func (n Name) Data(off int) *byte {
+ return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), "the runtime doesn't need to give you a reason"))
+}
+
+// IsExported returns "is n exported?"
+func (n Name) IsExported() bool {
+ return (*n.Bytes)&(1<<0) != 0
+}
+
+// HasTag returns true iff there is tag data following this name
+func (n Name) HasTag() bool {
+ return (*n.Bytes)&(1<<1) != 0
+}
+
+// IsEmbedded returns true iff n is embedded (an anonymous field).
+func (n Name) IsEmbedded() bool {
+ return (*n.Bytes)&(1<<3) != 0
+}
+
+// ReadVarint parses a varint as encoded by encoding/binary.
+// It returns the number of encoded bytes and the encoded value.
+func (n Name) ReadVarint(off int) (int, int) {
+ v := 0
+ for i := 0; ; i++ {
+ x := *n.DataChecked(off+i, "read varint")
+ v += int(x&0x7f) << (7 * i)
+ if x&0x80 == 0 {
+ return i + 1, v
+ }
+ }
+}
+
+// IsBlank indicates whether n is "_".
+func (n Name) IsBlank() bool {
+ if n.Bytes == nil {
+ return false
+ }
+ _, l := n.ReadVarint(1)
+ return l == 1 && *n.Data(2) == '_'
+}
+
+// writeVarint writes n to buf in varint form. Returns the
+// number of bytes written. n must be nonnegative.
+// Writes at most 10 bytes.
+func writeVarint(buf []byte, n int) int {
+ for i := 0; ; i++ {
+ b := byte(n & 0x7f)
+ n >>= 7
+ if n == 0 {
+ buf[i] = b
+ return i + 1
+ }
+ buf[i] = b | 0x80
+ }
+}
+
+// Name returns the tag string for n, or empty if there is none.
+func (n Name) Name() string {
+ if n.Bytes == nil {
+ return ""
+ }
+ i, l := n.ReadVarint(1)
+ return unsafeStringFor(n.DataChecked(1+i, "non-empty string"), l)
+}
+
+// Tag returns the tag string for n, or empty if there is none.
+func (n Name) Tag() string {
+ if !n.HasTag() {
+ return ""
+ }
+ i, l := n.ReadVarint(1)
+ i2, l2 := n.ReadVarint(1 + i + l)
+ return unsafeStringFor(n.DataChecked(1+i+l+i2, "non-empty string"), l2)
+}
+
+func NewName(n, tag string, exported, embedded bool) Name {
+ if len(n) >= 1<<29 {
+ panic("abi.NewName: name too long: " + n[:1024] + "...")
+ }
+ if len(tag) >= 1<<29 {
+ panic("abi.NewName: tag too long: " + tag[:1024] + "...")
+ }
+ var nameLen [10]byte
+ var tagLen [10]byte
+ nameLenLen := writeVarint(nameLen[:], len(n))
+ tagLenLen := writeVarint(tagLen[:], len(tag))
+
+ var bits byte
+ l := 1 + nameLenLen + len(n)
+ if exported {
+ bits |= 1 << 0
+ }
+ if len(tag) > 0 {
+ l += tagLenLen + len(tag)
+ bits |= 1 << 1
+ }
+ if embedded {
+ bits |= 1 << 3
+ }
+
+ b := make([]byte, l)
+ b[0] = bits
+ copy(b[1:], nameLen[:nameLenLen])
+ copy(b[1+nameLenLen:], n)
+ if len(tag) > 0 {
+ tb := b[1+nameLenLen+len(n):]
+ copy(tb, tagLen[:tagLenLen])
+ copy(tb[tagLenLen:], tag)
+ }
+
+ return Name{Bytes: &b[0]}
+}
diff --git a/src/internal/abi/unsafestring_go119.go b/src/internal/abi/unsafestring_go119.go
new file mode 100644
index 0000000..a710384
--- /dev/null
+++ b/src/internal/abi/unsafestring_go119.go
@@ -0,0 +1,32 @@
+// 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.
+
+//go:build !go1.20
+// +build !go1.20
+
+package abi
+
+import "unsafe"
+
+type (
+ stringHeader struct {
+ Data *byte
+ Len int
+ }
+ sliceHeader struct {
+ Data *byte
+ Len int
+ Cap int
+ }
+)
+
+func unsafeStringFor(b *byte, l int) string {
+ h := stringHeader{Data: b, Len: l}
+ return *(*string)(unsafe.Pointer(&h))
+}
+
+func unsafeSliceFor(b *byte, l int) []byte {
+ h := sliceHeader{Data: b, Len: l, Cap: l}
+ return *(*[]byte)(unsafe.Pointer(&h))
+}
diff --git a/src/internal/abi/unsafestring_go120.go b/src/internal/abi/unsafestring_go120.go
new file mode 100644
index 0000000..93ff8ea
--- /dev/null
+++ b/src/internal/abi/unsafestring_go120.go
@@ -0,0 +1,18 @@
+// 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.
+
+//go:build go1.20
+// +build go1.20
+
+package abi
+
+import "unsafe"
+
+func unsafeStringFor(b *byte, l int) string {
+ return unsafe.String(b, l)
+}
+
+func unsafeSliceFor(b *byte, l int) []byte {
+ return unsafe.Slice(b, l)
+}