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
4 changes: 4 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,10 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
// with a LLVM intrinsic.
continue
}
if ok := b.defineCryptoIntrinsic(); ok {
// Body of this function was replaced
continue
}
if member.Blocks == nil {
// Try to define this as an intrinsic function.
b.defineIntrinsicFunction()
Expand Down
17 changes: 17 additions & 0 deletions compiler/intrinsics.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,23 @@ func (b *builder) defineMathOp() {
b.CreateRet(result)
}

func (b *builder) defineCryptoIntrinsic() bool {
if b.fn.Pkg.Pkg.Path() != "crypto/internal/constanttime" {
return false
}

switch b.fn.Name() {
case "boolToUint8":
b.createFunctionStart(true)
param := b.getValue(b.fn.Params[0], b.fn.Pos())
result := b.CreateZExt(param, b.ctx.Int8Type(), "")
b.CreateRet(result)
return true
}

return false
}

// Implement most math/bits functions.
//
// This implements all the functions that operate on bits. It does not yet
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)
}
}
}
3 changes: 2 additions & 1 deletion src/internal/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ type Task struct {
state state

// This is needed for some crypto packages.
FipsIndicator uint8
FipsIndicator uint8
FipsOnlyBypass bool

// State of the goroutine: running, paused, or must-resume-next-pause.
// This extra field doesn't increase memory usage on 32-bit CPUs and above,
Expand Down
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
}
Loading
Loading