mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 12:03:41 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd7594a0ff | |||
| e95d56f749 |
@@ -101,7 +101,7 @@ jobs:
|
|||||||
- name: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-only-current-os"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
run: make release -j3
|
run: make release -j3
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ jobs:
|
|||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-only-current-os"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make build/release -j4
|
run: make build/release -j4
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
|
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
|
||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
|
{GOOS: "windows", GOARCH: "386"},
|
||||||
{GOOS: "windows", GOARCH: "amd64"},
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
{GOOS: "windows", GOARCH: "arm64"},
|
{GOOS: "windows", GOARCH: "arm64"},
|
||||||
} {
|
} {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
@@ -229,6 +230,10 @@ var libCompilerRT = Library{
|
|||||||
builtins = append(builtins, avrBuiltins...)
|
builtins = append(builtins, avrBuiltins...)
|
||||||
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
||||||
builtins = append(builtins, genericBuiltins128...)
|
builtins = append(builtins, genericBuiltins128...)
|
||||||
|
case "i386":
|
||||||
|
if strings.Split(target, "-")[2] == "windows" {
|
||||||
|
builtins = append(builtins, "i386/chkstk.S")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return builtins, nil
|
return builtins, nil
|
||||||
},
|
},
|
||||||
|
|||||||
+78
-31
@@ -32,23 +32,54 @@ var libMinGW = Library{
|
|||||||
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
|
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
|
||||||
return []string{
|
return []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
|
"-isystem", mingwDir + "/mingw-w64-crt/include",
|
||||||
"-isystem", mingwDir + "/mingw-w64-headers/crt",
|
"-isystem", mingwDir + "/mingw-w64-headers/crt",
|
||||||
|
"-isystem", mingwDir + "/mingw-w64-headers/include",
|
||||||
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
|
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
|
||||||
"-I" + headerPath,
|
"-I" + headerPath,
|
||||||
|
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
|
||||||
|
"-D_WIN32_WINNT=0x0501", // target Windows XP
|
||||||
|
"-D__LIBMSVCRT_OS__",
|
||||||
|
"-D_CRTBLD",
|
||||||
|
"-Wno-pragma-pack",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string) ([]string, error) {
|
||||||
// These files are needed so that printf and the like are supported.
|
// These files are needed so that printf and the like are supported.
|
||||||
sources := []string{
|
var sources []string
|
||||||
"mingw-w64-crt/stdio/ucrt_fprintf.c",
|
if strings.Split(target, "-")[0] == "i386" {
|
||||||
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
|
// Old 32-bit x86 systems use msvcrt.dll.
|
||||||
"mingw-w64-crt/stdio/ucrt_printf.c",
|
sources = []string{
|
||||||
"mingw-w64-crt/stdio/ucrt_snprintf.c",
|
"mingw-w64-crt/crt/pseudo-reloc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_sprintf.c",
|
"mingw-w64-crt/gdtoa/dmisc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
|
"mingw-w64-crt/gdtoa/gdtoa.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vprintf.c",
|
"mingw-w64-crt/gdtoa/gmisc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
|
"mingw-w64-crt/gdtoa/misc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
|
"mingw-w64-crt/misc/___mb_cur_max_func.c",
|
||||||
|
"mingw-w64-crt/misc/lc_locale_func.c",
|
||||||
|
"mingw-w64-crt/misc/mbrtowc.c",
|
||||||
|
"mingw-w64-crt/misc/strnlen.c",
|
||||||
|
"mingw-w64-crt/misc/wcrtomb.c",
|
||||||
|
"mingw-w64-crt/secapi/rand_s.c",
|
||||||
|
"mingw-w64-crt/stdio/acrt_iob_func.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_lock.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_pformat.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_vfprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_vsnprintf.c",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Anything somewhat modern (amd64, arm64) uses UCRT.
|
||||||
|
sources = []string{
|
||||||
|
"mingw-w64-crt/stdio/ucrt_fprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_printf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_snprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_sprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sources, nil
|
return sources, nil
|
||||||
},
|
},
|
||||||
@@ -63,27 +94,40 @@ var libMinGW = Library{
|
|||||||
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
||||||
var jobs []*compileJob
|
var jobs []*compileJob
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
var libs []string
|
||||||
// .lib file. But to simplify things, we're going to leave them as separate
|
if goarch == "386" {
|
||||||
// files.
|
libs = []string{
|
||||||
for _, name := range []string{
|
// x86 uses msvcrt.dll instead of UCRT for compatibility with old
|
||||||
"kernel32.def.in",
|
// Windows versions.
|
||||||
"api-ms-win-crt-conio-l1-1-0.def",
|
"advapi32.def.in",
|
||||||
"api-ms-win-crt-convert-l1-1-0.def.in",
|
"kernel32.def.in",
|
||||||
"api-ms-win-crt-environment-l1-1-0.def",
|
"msvcrt.def.in",
|
||||||
"api-ms-win-crt-filesystem-l1-1-0.def",
|
}
|
||||||
"api-ms-win-crt-heap-l1-1-0.def",
|
} else {
|
||||||
"api-ms-win-crt-locale-l1-1-0.def",
|
// Use the modernized UCRT on new systems.
|
||||||
"api-ms-win-crt-math-l1-1-0.def.in",
|
// Normally all the api-ms-win-crt-*.def files are all compiled to a
|
||||||
"api-ms-win-crt-multibyte-l1-1-0.def",
|
// single .lib file. But to simplify things, we're going to leave them
|
||||||
"api-ms-win-crt-private-l1-1-0.def.in",
|
// as separate files.
|
||||||
"api-ms-win-crt-process-l1-1-0.def",
|
libs = []string{
|
||||||
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
"kernel32.def.in",
|
||||||
"api-ms-win-crt-stdio-l1-1-0.def",
|
"api-ms-win-crt-conio-l1-1-0.def",
|
||||||
"api-ms-win-crt-string-l1-1-0.def",
|
"api-ms-win-crt-convert-l1-1-0.def.in",
|
||||||
"api-ms-win-crt-time-l1-1-0.def",
|
"api-ms-win-crt-environment-l1-1-0.def",
|
||||||
"api-ms-win-crt-utility-l1-1-0.def",
|
"api-ms-win-crt-filesystem-l1-1-0.def",
|
||||||
} {
|
"api-ms-win-crt-heap-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-locale-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-math-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-multibyte-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-private-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-process-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-stdio-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-string-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-time-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-utility-l1-1-0.def",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, name := range libs {
|
||||||
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
|
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
|
||||||
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
|
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -93,6 +137,9 @@ func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
|||||||
defpath := inpath
|
defpath := inpath
|
||||||
var archDef, emulation string
|
var archDef, emulation string
|
||||||
switch goarch {
|
switch goarch {
|
||||||
|
case "386":
|
||||||
|
archDef = "-DDEF_I386"
|
||||||
|
emulation = "i386pe"
|
||||||
case "amd64":
|
case "amd64":
|
||||||
archDef = "-DDEF_X64"
|
archDef = "-DDEF_X64"
|
||||||
emulation = "i386pep"
|
emulation = "i386pep"
|
||||||
|
|||||||
+13
-3
@@ -391,15 +391,25 @@ func (c *Config) LibcCFlags() []string {
|
|||||||
case "mingw-w64":
|
case "mingw-w64":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
path, _ := c.LibcPath("mingw-w64")
|
path, _ := c.LibcPath("mingw-w64")
|
||||||
return []string{
|
cflags := []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||||
"-D_UCRT",
|
|
||||||
"-D_WIN32_WINNT=0x0a00", // target Windows 10
|
|
||||||
}
|
}
|
||||||
|
if c.GOARCH() == "386" {
|
||||||
|
cflags = append(cflags,
|
||||||
|
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
|
||||||
|
"-D_WIN32_WINNT=0x0501", // target Windows XP
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cflags = append(cflags,
|
||||||
|
"-D_UCRT",
|
||||||
|
"-D_WIN32_WINNT=0x0a00", // target Windows 10
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return cflags
|
||||||
case "":
|
case "":
|
||||||
// No libc specified, nothing to add.
|
// No libc specified, nothing to add.
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+12
-6
@@ -430,14 +430,20 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
spec.GC = "boehm"
|
spec.GC = "boehm"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.Libc = "mingw-w64"
|
spec.Libc = "mingw-w64"
|
||||||
// Note: using a medium code model, low image base and no ASLR
|
|
||||||
// because Go doesn't really need those features. ASLR patches
|
|
||||||
// around issues for unsafe languages like C/C++ that are not
|
|
||||||
// normally present in Go (without explicitly opting in).
|
|
||||||
// For more discussion:
|
|
||||||
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
|
||||||
switch options.GOARCH {
|
switch options.GOARCH {
|
||||||
|
case "386":
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"-m", "i386pe",
|
||||||
|
)
|
||||||
|
// __udivdi3 is not present in ucrt it seems.
|
||||||
|
spec.RTLib = "compiler-rt"
|
||||||
case "amd64":
|
case "amd64":
|
||||||
|
// Note: using a medium code model, low image base and no ASLR
|
||||||
|
// because Go doesn't really need those features. ASLR patches
|
||||||
|
// around issues for unsafe languages like C/C++ that are not
|
||||||
|
// normally present in Go (without explicitly opting in).
|
||||||
|
// For more discussion:
|
||||||
|
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-m", "i386pep",
|
"-m", "i386pep",
|
||||||
"--image-base", "0x400000",
|
"--image-base", "0x400000",
|
||||||
|
|||||||
+9
-1
@@ -76,7 +76,15 @@ func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value,
|
|||||||
fragments := b.expandFormalParam(arg)
|
fragments := b.expandFormalParam(arg)
|
||||||
expanded = append(expanded, fragments...)
|
expanded = append(expanded, fragments...)
|
||||||
}
|
}
|
||||||
return b.CreateCall(fnType, fn, expanded, name)
|
call := b.CreateCall(fnType, fn, expanded, name)
|
||||||
|
if !fn.IsAFunction().IsNil() {
|
||||||
|
if cc := fn.FunctionCallConv(); cc != llvm.CCallConv {
|
||||||
|
// Set a different calling convention if needed.
|
||||||
|
// This is needed for GetModuleHandleExA on Windows, for example.
|
||||||
|
call.SetInstructionCallConv(cc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInvoke is like createCall but continues execution at the landing pad if
|
// createInvoke is like createCall but continues execution at the landing pad if
|
||||||
|
|||||||
@@ -452,6 +452,21 @@ func (b *builder) readStackPointer() llvm.Value {
|
|||||||
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeStackPointer emits a LLVM intrinsic call that updates the current stack
|
||||||
|
// pointer.
|
||||||
|
func (b *builder) writeStackPointer(sp llvm.Value) {
|
||||||
|
name := "llvm.stackrestore.p0"
|
||||||
|
if llvmutil.Version() < 18 {
|
||||||
|
name = "llvm.stackrestore" // backwards compatibility with LLVM 17 and below
|
||||||
|
}
|
||||||
|
stackrestore := b.mod.NamedFunction(name)
|
||||||
|
if stackrestore.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
|
||||||
|
stackrestore = llvm.AddFunction(b.mod, name, fnType)
|
||||||
|
}
|
||||||
|
b.CreateCall(stackrestore.GlobalValueType(), stackrestore, []llvm.Value{sp}, "")
|
||||||
|
}
|
||||||
|
|
||||||
// createZExtOrTrunc lets the input value fit in the output type bits, by zero
|
// createZExtOrTrunc lets the input value fit in the output type bits, by zero
|
||||||
// extending or truncating the integer.
|
// extending or truncating the integer.
|
||||||
func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value {
|
func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value {
|
||||||
|
|||||||
@@ -208,6 +208,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// > circumstances, and should not be exposed to source languages.
|
// > circumstances, and should not be exposed to source languages.
|
||||||
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
||||||
}
|
}
|
||||||
|
case "GetModuleHandleExA", "GetProcAddress", "GetSystemInfo", "GetSystemTimeAsFileTime", "LoadLibraryExW", "QueryUnbiasedInterruptTime", "SetEnvironmentVariableA", "Sleep", "VirtualAlloc":
|
||||||
|
// On Windows we need to use a special calling convention for some
|
||||||
|
// external calls.
|
||||||
|
if c.GOOS == "windows" && c.GOARCH == "386" {
|
||||||
|
llvmFn.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// External/exported functions may not retain pointer values.
|
// External/exported functions may not retain pointer values.
|
||||||
|
|||||||
+24
-1
@@ -268,6 +268,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// The signature looks like this:
|
// The signature looks like this:
|
||||||
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||||
|
|
||||||
|
isI386 := strings.HasPrefix(b.Triple, "i386-")
|
||||||
|
|
||||||
// Prepare input values.
|
// Prepare input values.
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
@@ -285,11 +287,17 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
if setLastError.IsNil() {
|
if setLastError.IsNil() {
|
||||||
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||||
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||||
|
if isI386 {
|
||||||
|
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getLastError := b.mod.NamedFunction("GetLastError")
|
getLastError := b.mod.NamedFunction("GetLastError")
|
||||||
if getLastError.IsNil() {
|
if getLastError.IsNil() {
|
||||||
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||||
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||||
|
if isI386 {
|
||||||
|
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now do the actual call. Pseudocode:
|
// Now do the actual call. Pseudocode:
|
||||||
@@ -300,9 +308,24 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// Note that SetLastError/GetLastError could be replaced with direct
|
// Note that SetLastError/GetLastError could be replaced with direct
|
||||||
// access to the thread control block, which is probably smaller and
|
// access to the thread control block, which is probably smaller and
|
||||||
// faster. The Go runtime does this in assembly.
|
// faster. The Go runtime does this in assembly.
|
||||||
b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
// On windows/386, we also need to save/restore the stack pointer. I'm
|
||||||
|
// not entirely sure why this is needed, but without it these calls
|
||||||
|
// change the stack pointer leading to a crash soon after.
|
||||||
|
setLastErrorCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||||
|
var sp llvm.Value
|
||||||
|
if isI386 {
|
||||||
|
setLastErrorCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
sp = b.readStackPointer()
|
||||||
|
}
|
||||||
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
|
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
|
||||||
|
if isI386 {
|
||||||
|
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
b.writeStackPointer(sp)
|
||||||
|
}
|
||||||
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||||
|
if isI386 {
|
||||||
|
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
if b.uintptrType != b.ctx.Int32Type() {
|
if b.uintptrType != b.ctx.Int32Type() {
|
||||||
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-24
@@ -35,6 +35,8 @@ const TESTDATA = "testdata"
|
|||||||
|
|
||||||
var testTarget = flag.String("target", "", "override test target")
|
var testTarget = flag.String("target", "", "override test target")
|
||||||
|
|
||||||
|
var testOnlyCurrentOS = flag.Bool("only-current-os", false, "")
|
||||||
|
|
||||||
var supportedLinuxArches = map[string]string{
|
var supportedLinuxArches = map[string]string{
|
||||||
"AMD64Linux": "linux/amd64",
|
"AMD64Linux": "linux/amd64",
|
||||||
"X86Linux": "linux/386",
|
"X86Linux": "linux/386",
|
||||||
@@ -158,20 +160,35 @@ func TestBuild(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
if !*testOnlyCurrentOS {
|
||||||
t.Parallel()
|
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromTarget("cortex-m-qemu", sema), tests, t)
|
t.Parallel()
|
||||||
})
|
runPlatTests(optionsFromTarget("cortex-m-qemu", sema), tests, t)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("EmulatedRISCV", func(t *testing.T) {
|
t.Run("EmulatedRISCV", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runPlatTests(optionsFromTarget("riscv-qemu", sema), tests, t)
|
runPlatTests(optionsFromTarget("riscv-qemu", sema), tests, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("AVR", func(t *testing.T) {
|
t.Run("AVR", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("WebAssembly", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runPlatTests(optionsFromTarget("wasm", sema), tests, t)
|
||||||
|
})
|
||||||
|
t.Run("WASI", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runPlatTests(optionsFromTarget("wasip1", sema), tests, t)
|
||||||
|
})
|
||||||
|
t.Run("WASIp2", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runPlatTests(optionsFromTarget("wasip2", sema), tests, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
for name, osArch := range supportedLinuxArches {
|
for name, osArch := range supportedLinuxArches {
|
||||||
@@ -191,18 +208,13 @@ func TestBuild(t *testing.T) {
|
|||||||
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
||||||
runTest("cgo/", options, t, nil, nil)
|
runTest("cgo/", options, t, nil, nil)
|
||||||
})
|
})
|
||||||
t.Run("WebAssembly", func(t *testing.T) {
|
} else if runtime.GOOS == "windows" {
|
||||||
t.Parallel()
|
if runtime.GOARCH != "386" {
|
||||||
runPlatTests(optionsFromTarget("wasm", sema), tests, t)
|
t.Run("Windows386", func(t *testing.T) {
|
||||||
})
|
t.Parallel()
|
||||||
t.Run("WASI", func(t *testing.T) {
|
runPlatTests(optionsFromOSARCH("windows/386", sema), tests, t)
|
||||||
t.Parallel()
|
})
|
||||||
runPlatTests(optionsFromTarget("wasip1", sema), tests, t)
|
}
|
||||||
})
|
|
||||||
t.Run("WASIp2", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
runPlatTests(optionsFromTarget("wasip2", sema), tests, t)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func (r *reader) Read(b []byte) (n int, err error) {
|
|||||||
// Call rand_s every four bytes because it's a C int (always 32-bit in
|
// Call rand_s every four bytes because it's a C int (always 32-bit in
|
||||||
// Windows).
|
// Windows).
|
||||||
if i%4 == 0 {
|
if i%4 == 0 {
|
||||||
|
// TODO: use RtlGenRandom on GOARCH=386.
|
||||||
errCode := libc_rand_s(&randomByte)
|
errCode := libc_rand_s(&randomByte)
|
||||||
if errCode != 0 {
|
if errCode != 0 {
|
||||||
// According to the documentation, it can return an error.
|
// According to the documentation, it can return an error.
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
.global _tinygo_startTask
|
||||||
|
_tinygo_startTask:
|
||||||
|
#else // Linux etc
|
||||||
.section .text.tinygo_startTask
|
.section .text.tinygo_startTask
|
||||||
.global tinygo_startTask
|
.global tinygo_startTask
|
||||||
.type tinygo_startTask, %function
|
.type tinygo_startTask, %function
|
||||||
tinygo_startTask:
|
tinygo_startTask:
|
||||||
|
#endif
|
||||||
.cfi_startproc
|
.cfi_startproc
|
||||||
// Small assembly stub for starting a goroutine. This is already run on the
|
// Small assembly stub for starting a goroutine. This is already run on the
|
||||||
// new stack, with the callee-saved registers already loaded.
|
// new stack, with the callee-saved registers already loaded.
|
||||||
@@ -24,12 +29,21 @@ tinygo_startTask:
|
|||||||
addl $4, %esp
|
addl $4, %esp
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
|
#ifdef _WIN32
|
||||||
|
jmp _tinygo_pause
|
||||||
|
#else
|
||||||
jmp tinygo_pause
|
jmp tinygo_pause
|
||||||
|
#endif
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
.global _tinygo_swapTask
|
||||||
|
_tinygo_swapTask:
|
||||||
|
#else
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
.type tinygo_swapTask, %function
|
.type tinygo_swapTask, %function
|
||||||
tinygo_swapTask:
|
tinygo_swapTask:
|
||||||
|
#endif
|
||||||
// This function gets the following parameters:
|
// This function gets the following parameters:
|
||||||
movl 4(%esp), %eax // newStack uintptr
|
movl 4(%esp), %eax // newStack uintptr
|
||||||
movl 8(%esp), %ecx // oldStack *uintptr
|
movl 8(%esp), %ecx // oldStack *uintptr
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
.global _tinygo_scanCurrentStack
|
||||||
|
_tinygo_scanCurrentStack:
|
||||||
|
#else
|
||||||
.section .text.tinygo_scanCurrentStack
|
.section .text.tinygo_scanCurrentStack
|
||||||
.global tinygo_scanCurrentStack
|
.global tinygo_scanCurrentStack
|
||||||
.type tinygo_scanCurrentStack, %function
|
.type tinygo_scanCurrentStack, %function
|
||||||
tinygo_scanCurrentStack:
|
tinygo_scanCurrentStack:
|
||||||
|
#endif
|
||||||
// Sources:
|
// Sources:
|
||||||
// * https://stackoverflow.com/questions/18024672/what-registers-are-preserved-through-a-linux-x86-64-function-call
|
// * https://stackoverflow.com/questions/18024672/what-registers-are-preserved-through-a-linux-x86-64-function-call
|
||||||
// * https://godbolt.org/z/q7e8dn
|
// * https://godbolt.org/z/q7e8dn
|
||||||
@@ -15,7 +20,11 @@ tinygo_scanCurrentStack:
|
|||||||
// Scan the stack.
|
// Scan the stack.
|
||||||
subl $8, %esp // adjust the stack before the call to maintain 16-byte alignment
|
subl $8, %esp // adjust the stack before the call to maintain 16-byte alignment
|
||||||
pushl %esp
|
pushl %esp
|
||||||
|
#ifdef _WIN32
|
||||||
|
calll _tinygo_scanstack
|
||||||
|
#else
|
||||||
calll tinygo_scanstack
|
calll tinygo_scanstack
|
||||||
|
#endif
|
||||||
|
|
||||||
// Restore the stack pointer. Registers do not need to be restored as they
|
// Restore the stack pointer. Registers do not need to be restored as they
|
||||||
// were only pushed to be discoverable by the GC.
|
// were only pushed to be discoverable by the GC.
|
||||||
@@ -23,9 +32,14 @@ tinygo_scanCurrentStack:
|
|||||||
retl
|
retl
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
.global _tinygo_longjmp
|
||||||
|
_tinygo_longjmp:
|
||||||
|
#else
|
||||||
.section .text.tinygo_longjmp
|
.section .text.tinygo_longjmp
|
||||||
.global tinygo_longjmp
|
.global tinygo_longjmp
|
||||||
tinygo_longjmp:
|
tinygo_longjmp:
|
||||||
|
#endif
|
||||||
// Note: the code we jump to assumes eax is set to a non-zero value if we
|
// Note: the code we jump to assumes eax is set to a non-zero value if we
|
||||||
// jump from here.
|
// jump from here.
|
||||||
movl 4(%esp), %eax
|
movl 4(%esp), %eax
|
||||||
|
|||||||
@@ -239,11 +239,17 @@ func procUnpin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hardwareRand() (n uint64, ok bool) {
|
func hardwareRand() (n uint64, ok bool) {
|
||||||
var n1, n2 uint32
|
if GOARCH == "386" {
|
||||||
errCode1 := libc_rand_s(&n1)
|
// Use the old RtlGenRandom, introduced in Windows XP.
|
||||||
errCode2 := libc_rand_s(&n2)
|
ok = RtlGenRandom(unsafe.Pointer(&n), 8)
|
||||||
n = uint64(n1)<<32 | uint64(n2)
|
} else {
|
||||||
ok = errCode1 == 0 && errCode2 == 0
|
// Use the newer secure rand_s function that's part of UCRT.
|
||||||
|
var n1, n2 uint32
|
||||||
|
errCode1 := libc_rand_s(&n1)
|
||||||
|
errCode2 := libc_rand_s(&n2)
|
||||||
|
n = uint64(n1)<<32 | uint64(n2)
|
||||||
|
ok = errCode1 == 0 && errCode2 == 0
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,3 +259,10 @@ func hardwareRand() (n uint64, ok bool) {
|
|||||||
//
|
//
|
||||||
//export rand_s
|
//export rand_s
|
||||||
func libc_rand_s(randomValue *uint32) int32
|
func libc_rand_s(randomValue *uint32) int32
|
||||||
|
|
||||||
|
// This function is part of advapi32.dll, and is called SystemFunction036 for
|
||||||
|
// some reason. It's available on Windows XP and newer.
|
||||||
|
// See: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
|
||||||
|
//
|
||||||
|
//export SystemFunction036
|
||||||
|
func RtlGenRandom(buf unsafe.Pointer, len int) bool
|
||||||
|
|||||||
Reference in New Issue
Block a user