Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion builder/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {

// Version range supported by TinyGo.
const minorMin = 19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't minorMin also match the go.mod version?

const minorMax = 25
const minorMax = 26

// Check that we support this Go toolchain version.
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
Expand Down
1 change: 1 addition & 0 deletions loader/goroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"internal/cm/": false,
"internal/futex/": false,
"internal/fuzz/": false,
"internal/itoa": false,
"internal/reflectlite/": false,
"internal/gclayout": false,
"internal/task/": false,
Expand Down
32 changes: 32 additions & 0 deletions src/internal/abi/escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,35 @@ func Escape[T any](x T) T {
// as-is.
panic("internal/abi.Escape: unreachable (implemented in the compiler)")
}

// EscapeNonString forces v to be on the heap, if v contains a
// non-string pointer.
//
// This is used in hash/maphash.Comparable. We cannot hash pointers
// to local variables on stack, as their addresses might change on
// stack growth. Strings are okay as the hash depends on only the
// content, not the pointer.
//
// This is essentially
//
// if hasNonStringPointers(T) { Escape(v) }
//
// Implemented as a compiler intrinsic.
func EscapeNonString[T any](v T) { panic("intrinsic") }

// EscapeToResultNonString models a data flow edge from v to the result,
// if v contains a non-string pointer. If v contains only string pointers,
// it returns a copy of v, but is not modeled as a data flow edge
// from the escape analysis's perspective.
//
// This is used in unique.clone, to model the data flow edge on the
// value with strings excluded, because strings are cloned (by
// content).
//
// TODO: probably we should define this as a intrinsic and EscapeNonString
// could just be "heap = EscapeToResultNonString(v)". This way we can model
// an edge to the result but not necessarily heap.
func EscapeToResultNonString[T any](v T) T {
EscapeNonString(v)
return *(*T)(NoEscape(unsafe.Pointer(&v)))
}
57 changes: 57 additions & 0 deletions src/internal/itoa/itoa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.

// Simple conversions to avoid depending on strconv.

package itoa

// Itoa converts val to a decimal string.
func Itoa(val int) string {
if val < 0 {
return "-" + Uitoa(uint(-val))
}
return Uitoa(uint(val))
}

// Uitoa converts val to a decimal string.
func Uitoa(val uint) string {
if val == 0 { // avoid string allocation
return "0"
}
var buf [20]byte // big enough for 64bit value base 10
i := len(buf) - 1
for val >= 10 {
q := val / 10
buf[i] = byte('0' + val - q*10)
i--
val = q
}
// val < 10
buf[i] = byte('0' + val)
return string(buf[i:])
}

const hex = "0123456789abcdef"

// Uitox converts val (a uint) to a hexadecimal string.
func Uitox(val uint) string {
if val == 0 { // avoid string allocation
return "0x0"
}
var buf [20]byte // big enough for 64bit value base 16 + 0x
i := len(buf) - 1
for val >= 16 {
q := val / 16
buf[i] = hex[val%16]
i--
val = q
}
// val < 16
buf[i] = hex[val%16]
i--
buf[i] = 'x'
i--
buf[i] = '0'
return string(buf[i:])
}
51 changes: 51 additions & 0 deletions src/internal/itoa/itoa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 itoa_test

import (
"fmt"
"internal/itoa"
"math"
"testing"
)

var (
minInt64 int64 = math.MinInt64
maxInt64 int64 = math.MaxInt64
maxUint64 uint64 = math.MaxUint64
)

func TestItoa(t *testing.T) {
tests := []int{int(minInt64), math.MinInt32, -999, -100, -1, 0, 1, 100, 999, math.MaxInt32, int(maxInt64)}
for _, tt := range tests {
got := itoa.Itoa(tt)
want := fmt.Sprint(tt)
if want != got {
t.Fatalf("Itoa(%d) = %s, want %s", tt, got, want)
}
}
}

func TestUitoa(t *testing.T) {
tests := []uint{0, 1, 100, 999, math.MaxUint32, uint(maxUint64)}
for _, tt := range tests {
got := itoa.Uitoa(tt)
want := fmt.Sprint(tt)
if want != got {
t.Fatalf("Uitoa(%d) = %s, want %s", tt, got, want)
}
}
}

func TestUitox(t *testing.T) {
tests := []uint{0, 1, 15, 100, 999, math.MaxUint32, uint(maxUint64)}
for _, tt := range tests {
got := itoa.Uitox(tt)
want := fmt.Sprintf("%#x", tt)
if want != got {
t.Fatalf("Uitox(%x) = %s, want %s", tt, got, want)
}
}
}
67 changes: 0 additions & 67 deletions src/runtime/os_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,73 +130,6 @@ func syscall_Getpagesize() int {
return int(libc_getpagesize())
}

// Call "system calls" (actually: libc functions) in a special way.
// - Most calls calls return a C int (which is 32-bits), and -1 on failure.
// - syscallX* is for functions that return a 64-bit integer (and also return
// -1 on failure).
// - syscallPtr is for functions that return a pointer on success or NULL on
// failure.
// - rawSyscall seems to avoid some stack modifications, which isn't relevant
// to TinyGo.

//go:linkname syscall_syscall syscall.syscall
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
// For TinyGo we don't need to do anything special to call C functions.
return syscall_rawSyscall(fn, a1, a2, a3)
}

//go:linkname syscall_rawSyscall syscall.rawSyscall
func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
result := call_syscall(fn, a1, a2, a3)
r1 = uintptr(result)
if result == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscallX syscall.syscallX
func syscall_syscallX(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
r1 = call_syscallX(fn, a1, a2, a3)
if int64(r1) == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscallPtr syscall.syscallPtr
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
r1 = call_syscallX(fn, a1, a2, a3)
if r1 == 0 {
// Syscall returns a pointer on success, or NULL on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscall6 syscall.syscall6
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
result := call_syscall6(fn, a1, a2, a3, a4, a5, a6)
r1 = uintptr(result)
if result == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscall6X syscall.syscall6X
func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
r1 = call_syscall6X(fn, a1, a2, a3, a4, a5, a6)
if int64(r1) == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

// uint32_t arc4random(void);
//
//export arc4random
Expand Down
70 changes: 70 additions & 0 deletions src/runtime/os_darwin_go125.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//go:build darwin && !go1.26

package runtime

// Call "system calls" (actually: libc functions) in a special way.
// - Most calls calls return a C int (which is 32-bits), and -1 on failure.
// - syscallX* is for functions that return a 64-bit integer (and also return
// -1 on failure).
// - syscallPtr is for functions that return a pointer on success or NULL on
// failure.
// - rawSyscall seems to avoid some stack modifications, which isn't relevant
// to TinyGo.

//go:linkname syscall_syscall syscall.syscall
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
// For TinyGo we don't need to do anything special to call C functions.
return syscall_rawSyscall(fn, a1, a2, a3)
}

//go:linkname syscall_rawSyscall syscall.rawSyscall
func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
result := call_syscall(fn, a1, a2, a3)
r1 = uintptr(result)
if result == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscallX syscall.syscallX
func syscall_syscallX(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
r1 = call_syscallX(fn, a1, a2, a3)
if int64(r1) == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscallPtr syscall.syscallPtr
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
r1 = call_syscallX(fn, a1, a2, a3)
if r1 == 0 {
// Syscall returns a pointer on success, or NULL on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscall6 syscall.syscall6
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
result := call_syscall6(fn, a1, a2, a3, a4, a5, a6)
r1 = uintptr(result)
if result == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

//go:linkname syscall_syscall6X syscall.syscall6X
func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
r1 = call_syscall6X(fn, a1, a2, a3, a4, a5, a6)
if int64(r1) == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}
69 changes: 69 additions & 0 deletions src/runtime/os_darwin_go126.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//go:build darwin && go1.26

package runtime

// syscall_syscalln is a wrapper around the libc call with variable arguments.
//
//go:nosplit
//go:linkname syscall_syscalln syscall.syscalln
func syscall_syscalln(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
r1, r2, err = syscall_rawsyscalln(fn, args...)
return r1, r2, err
}

// syscall_rawsyscalln is a wrapper around the libc call with variable arguments.
//
//go:linkname syscall_rawsyscalln syscall.rawsyscalln
//go:nosplit
func syscall_rawsyscalln(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {

var a1, a2, a3, a4, a5, a6 uintptr

switch len(args) {
case 3:
a3 = args[2]
fallthrough
case 2:
a2 = args[1]
fallthrough
case 1:
a1 = args[0]
fallthrough
case 0:
return runtime_syscall(fn, a1, a2, a3)

case 6:
a6 = args[5]
fallthrough
case 5:
a5 = args[4]
fallthrough
case 4:
a3 = args[3]

a1, a2, a3 = args[0], args[1], args[2]
return runtime_syscall6(fn, a1, a2, a3, a4, a5, a6)
}

panic("syscall args not handled")
}

func runtime_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
result := call_syscall(fn, a1, a2, a3)
r1 = uintptr(result)
if result == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}

func runtime_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
result := call_syscall6(fn, a1, a2, a3, a4, a5, a6)
r1 = uintptr(result)
if result == -1 {
// Syscall returns -1 on failure.
err = uintptr(*libc_errno_location())
}
return
}
Loading