Skip to content
Merged
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
6 changes: 6 additions & 0 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (*BuildR
if err != nil {
return nil, err
}
if _, ok := globalValues["runtime"]["godebugDefault"]; !ok {
globalValues["runtime"]["godebugDefault"] = lprogram.MainPkg().DefaultGODEBUG
}
result := &BuildResult{
ModuleRoot: lprogram.MainPkg().Module.Dir,
MainDir: lprogram.MainPkg().Dir,
Expand Down Expand Up @@ -1389,6 +1392,9 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
// needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run.
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
if err := transform.PrepareRuntimeStartup(mod); err != nil {
return err
}
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions builder/testdata/binary-size.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ pico2 ./testdata/size-timer z 13791
pico2 ./testdata/size-timer 2 33399 2809 136 5672
cortex-m-qemu ./testdata/size-corpus z 8775 1297 80 4188
cortex-m-qemu ./testdata/size-corpus 2 17034 862 72 4188
riscv-qemu ./testdata/size-corpus z 16052 1028 88 16660
riscv-qemu ./testdata/size-corpus 2 24808 744 80 16644
riscv-qemu ./testdata/size-corpus z 16100 1028 88 16660
riscv-qemu ./testdata/size-corpus 2 22873 935 80 16644
cortex-m-qemu ./testdata/size-init-recover z 2559 761 4 4168
cortex-m-qemu ./testdata/size-init-recover 2 6499 309 4 4168
pico2 ./testdata/size-init-regexp z 16047 11809 940 5736
Expand Down
2 changes: 2 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2355,6 +2355,8 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
//
// This is also where compiler intrinsics are implemented.
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
b.markRuntimeFeatureUse(instr)

// See if this is an intrinsic function that is handled specially.
if fn := instr.StaticCallee(); fn != nil {
// Direct function call, either to a named or anonymous (directly
Expand Down
2 changes: 2 additions & 0 deletions compiler/defer.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@ func (b *builder) loadDeferredCallParams(structType llvm.Type, ptr llvm.Value) [
// createDefer emits a single defer instruction, to be run when this function
// returns.
func (b *builder) createDefer(instr *ssa.Defer) {
b.markRuntimeFeatureUse(&instr.Call)

// The pointer to the previous defer struct, which we will replace to
// make a linked list.
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
Expand Down
2 changes: 2 additions & 0 deletions compiler/goroutine.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (

// createGo emits code to start a new goroutine.
func (b *builder) createGo(instr *ssa.Go) {
b.markRuntimeFeatureUse(&instr.Call)

if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
// We cheat. None of the builtins do any long or blocking operation, so
// we might as well run these builtins right away without the program
Expand Down
34 changes: 34 additions & 0 deletions compiler/runtimefeature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package compiler

import (
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)

func (b *builder) markRuntimeFeatureUse(call *ssa.CallCommon) {
callee := call.StaticCallee()
if callee == nil {
return
}

var markerName string
switch b.getFunctionInfo(callee).linkName {
case "runtime.GOROOT":
markerName = "tinygo.runtime.feature.goroot"
case "(*internal/godebug.Setting).Value":
markerName = "tinygo.runtime.feature.godebug"
case "internal/godebug.setUpdate":
if b.fn.Pkg != nil && b.fn.Pkg.Pkg.Path() == "internal/godebug" {
return
}
markerName = "tinygo.runtime.feature.godebug"
default:
return
}

marker := b.mod.NamedFunction(markerName)
if marker.IsNil() {
marker = llvm.AddFunction(b.mod, markerName, llvm.FunctionType(b.ctx.VoidType(), nil, false))
}
b.CreateCall(marker.GlobalValueType(), marker, nil, "")
}
17 changes: 9 additions & 8 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ type Program struct {

// PackageJSON is a subset of the JSON struct returned from `go list`.
type PackageJSON struct {
Dir string
ImportPath string
Name string
ForTest string
Root string
Standard bool
DepOnly bool
Module struct {
Dir string
ImportPath string
Name string
ForTest string
Root string
Standard bool
DepOnly bool
DefaultGODEBUG string
Module struct {
Path string
Main bool
Dir string
Expand Down
69 changes: 69 additions & 0 deletions runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/goenv"
)

// TestTimerStopResetRace checks that stopping or resetting a timer while its
Expand Down Expand Up @@ -166,3 +167,71 @@ func TestStdin(t *testing.T) {
}
checkOutput(t, TESTDATA+"/stdin.txt", output.Bytes())
}

func TestRuntimeGOROOT(t *testing.T) {
t.Parallel()

for _, test := range []struct {
name string
environment []string
want string
}{
{"startup environment", []string{"GOROOT=/startup"}, "/startup"},
{"empty startup environment", []string{"GOROOT="}, goenv.Get("GOROOT")},
} {
t.Run(test.name, func(t *testing.T) {
options := optionsFromTarget(*testTarget, sema)
config, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}

output := &bytes.Buffer{}
_, err = buildAndRun("testdata/runtimegoroot.go", config, output, nil, test.environment, time.Minute, func(cmd *exec.Cmd, _ *builder.BuildResult) error {
return cmd.Run()
})
if err != nil {
t.Fatal(err)
}
want := test.want + "\n" + test.want + "\n"
if output.String() != want {
t.Fatalf("unexpected output:\n%s\nwant:\n%s", output.String(), want)
}
})
}
}

func TestRuntimeGODEBUG(t *testing.T) {
t.Parallel()

for _, test := range []struct {
name string
program string
environment []string
want string
}{
{"compiled default", "testdata/runtimegodebug.go", nil, "true \"\"\ntrue \"\"\ntrue \"\"\ntrue \"tarinsecurepath=1\"\ntrue \"\"\ntrue \"\"\n"},
{"environment override", "testdata/runtimegodebug.go", []string{"GODEBUG=tarinsecurepath=1"}, "true \"tarinsecurepath=1\"\ntrue \"\"\ntrue \"\"\ntrue \"tarinsecurepath=1\"\ntrue \"\"\ntrue \"\"\n"},
{"setting compiled default", "testdata/runtimegodebugsetting.go", nil, "true\nfalse\ntrue\ntrue\n"},
{"setting environment override", "testdata/runtimegodebugsetting.go", []string{"GODEBUG=tarinsecurepath=1"}, "false\nfalse\ntrue\ntrue\n"},
} {
t.Run(test.name, func(t *testing.T) {
options := optionsFromTarget(*testTarget, sema)
config, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}

output := &bytes.Buffer{}
_, err = buildAndRun(test.program, config, output, nil, test.environment, time.Minute, func(cmd *exec.Cmd, _ *builder.BuildResult) error {
return cmd.Run()
})
if err != nil {
t.Fatal(err)
}
if output.String() != test.want {
t.Fatalf("unexpected output:\n%s\nwant:\n%s", output.String(), test.want)
}
})
}
}
7 changes: 5 additions & 2 deletions src/runtime/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ func syscallSetenv(key, value string) {
keydata := cstring(key)
valdata := cstring(value)
setenv(&keydata[0], &valdata[0])
if key == "GODEBUG" && godebugUpdate != nil {
if key == "GODEBUG" && godebugEnvEnabled() {
// Starting with Go 1.20, we need to call a callback (set by
// internal/godebug) to notify the GODEBUG environment variable has
// changed. This is necessary to get archive/zip to pass tests.
godebugUpdate(key, value)
godebugSetEnv(value)
}
}

Expand All @@ -25,6 +25,9 @@ func syscallSetenv(key, value string) {
func syscallUnsetenv(key string) {
keydata := cstring(key)
unsetenv(&keydata[0])
if key == "GODEBUG" && godebugEnvEnabled() {
godebugUnsetEnv()
}
}

// Clear the environment.
Expand Down
15 changes: 9 additions & 6 deletions src/runtime/env_nonhosted.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ func syscallSetenv(key, value string) {
for i, e := range env {
if envKey(e) == key {
env[i] = entry
if key == "GODEBUG" && godebugUpdate != nil {
godebugUpdate(key, value)
if key == "GODEBUG" && godebugEnvEnabled() {
godebugSetEnv(value)
}
return
}
}
env = append(env, entry)
if key == "GODEBUG" && godebugUpdate != nil {
godebugUpdate(key, value)
if key == "GODEBUG" && godebugEnvEnabled() {
godebugSetEnv(value)
}
}

Expand All @@ -25,12 +25,15 @@ func syscallUnsetenv(key string) {
for i, e := range env {
if envKey(e) == key {
env = append(env[:i], env[i+1:]...)
if key == "GODEBUG" && godebugUpdate != nil {
godebugUpdate(key, "")
if key == "GODEBUG" && godebugEnvEnabled() {
godebugUnsetEnv()
}
return
}
}
if key == "GODEBUG" {
godebugUnsetEnv()
}
}

func envKey(entry string) string {
Expand Down
27 changes: 27 additions & 0 deletions src/runtime/env_startup_libc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build darwin || windows || wasip1 || (linux && !baremetal && !wasm_unknown && !wasip2 && !nintendoswitch)

package runtime

import "unsafe"

//export getenv
func libc_getenv(name *byte) *byte

func startupEnv(key string) string {
name := make([]byte, len(key)+1)
copy(name, key)
value := libc_getenv(&name[0])
if value == nil {
return ""
}
length := strlen(unsafe.Pointer(value))
return string(unsafe.Slice(value, length))
}

func init() {
if gorootEnvEnabled() {
if value := startupEnv("GOROOT"); value != "" {
goroot = value
}
}
}
8 changes: 4 additions & 4 deletions src/runtime/env_wasip2.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ package runtime

//go:linkname syscallSetenv syscall.runtimeSetenv
func syscallSetenv(key, value string) {
if key == "GODEBUG" && godebugUpdate != nil {
godebugUpdate(key, value)
if key == "GODEBUG" && godebugEnvEnabled() {
godebugSetEnv(value)
}
}

//go:linkname syscallUnsetenv syscall.runtimeUnsetenv
func syscallUnsetenv(key string) {
if key == "GODEBUG" && godebugUpdate != nil {
godebugUpdate(key, "")
if key == "GODEBUG" && godebugEnvEnabled() {
godebugUnsetEnv()
}
}
9 changes: 9 additions & 0 deletions src/runtime/godebug_startup_libc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build (linux && !baremetal && !wasm_unknown && !wasip2 && !nintendoswitch) || darwin || windows

package runtime

func init() {
if godebugEnvEnabled() {
godebugStartup = startupEnv("GODEBUG")
}
}
20 changes: 20 additions & 0 deletions src/runtime/nonhosted.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,23 @@ func init() {
env = append(env, s[start:])
}
}

func init() {
if gorootEnvEnabled() {
if value := startupEnv("GOROOT"); value != "" {
goroot = value
Comment thread
davecheney marked this conversation as resolved.
Resolved
}
}
if godebugEnvEnabled() {
godebugStartup = startupEnv("GODEBUG")
}
}

func startupEnv(key string) string {
for _, entry := range env {
if len(entry) > len(key) && entry[:len(key)] == key && entry[len(key)] == '=' {
return entry[len(key)+1:]
}
}
return ""
}
Loading
Loading