summaryrefslogtreecommitdiffstats
path: root/src/internal/abi
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/abi')
-rw-r--r--src/internal/abi/abi.go126
-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/export_test.go14
-rw-r--r--src/internal/abi/testdata/x.go22
-rw-r--r--src/internal/abi/testdata/x.s6
11 files changed, 383 insertions, 0 deletions
diff --git a/src/internal/abi/abi.go b/src/internal/abi/abi.go
new file mode 100644
index 0000000..11acac3
--- /dev/null
+++ b/src/internal/abi/abi.go
@@ -0,0 +1,126 @@
+// 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
+}
+
+// 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 any) 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 any) uintptr
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..f0d8dce
--- /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"
+ "os/exec"
+ "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")
+
+ importcfgfile := filepath.Join(tmpdir, "hello.importcfg")
+ testenv.WriteImportcfg(t, importcfgfile, nil)
+
+ // parse assembly code for symabi.
+ cmd := exec.Command(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 = exec.Command(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/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/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