Skip to content

Commit 659c167

Browse files
committed
Add support for using wizer to handle running initAll()
1 parent c83f712 commit 659c167

File tree

11 files changed

+143
-10
lines changed

11 files changed

+143
-10
lines changed

builder/build.go

+32-4
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,33 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
834834
}
835835
}
836836

837+
if config.Options.WizerInit {
838+
var args []string
839+
840+
resultWizer := result.Executable + "-wizer"
841+
842+
args = append(args,
843+
"--allow-wasi",
844+
"--wasm-bulk-memory=true",
845+
"-f", "runtime.wizerInit",
846+
result.Executable,
847+
"-o", resultWizer,
848+
)
849+
850+
cmd := exec.Command(goenv.Get("WIZER"), args...)
851+
cmd.Stdout = os.Stdout
852+
cmd.Stderr = os.Stderr
853+
854+
err := cmd.Run()
855+
if err != nil {
856+
return fmt.Errorf("wizer failed: %w", err)
857+
}
858+
859+
if err := os.Rename(resultWizer, result.Executable); err != nil {
860+
return fmt.Errorf("rename failed: %w", err)
861+
}
862+
}
863+
837864
// Print code size if requested.
838865
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
839866
packagePathMap := make(map[string]string, len(lprogram.Packages))
@@ -1031,9 +1058,10 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
10311058
// needed to convert a program to its final form. Some transformations are not
10321059
// optional and must be run as the compiler expects them to run.
10331060
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
1034-
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
1035-
if err != nil {
1036-
return err
1061+
if !config.Options.WizerInit {
1062+
if err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA()); err != nil {
1063+
return err
1064+
}
10371065
}
10381066
if config.VerifyIR() {
10391067
// Only verify if we really need it.
@@ -1049,7 +1077,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
10491077
}
10501078

10511079
// Insert values from -ldflags="-X ..." into the IR.
1052-
err = setGlobalValues(mod, config.Options.GlobalValues)
1080+
err := setGlobalValues(mod, config.Options.GlobalValues)
10531081
if err != nil {
10541082
return err
10551083
}

compileopts/options.go

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type Options struct {
5353
Monitor bool
5454
BaudRate int
5555
Timeout time.Duration
56+
WizerInit bool
5657
}
5758

5859
// Verify performs a validation on the given options, raising an error if options are not valid.

goenv/goenv.go

+29
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,35 @@ func Get(name string) string {
151151
}
152152

153153
return findWasmOpt()
154+
155+
case "WIZER":
156+
if path := os.Getenv("WIZER"); path != "" {
157+
wizerBin := "wizer"
158+
if runtime.GOOS == "windows" {
159+
wizerBin += ".exe"
160+
}
161+
162+
_, err := exec.LookPath(wizerBin)
163+
if err != nil {
164+
fmt.Fprintf(os.Stderr, "cannot use %q as wasm-opt (from WASMOPT environment variable): %s", path, err.Error())
165+
os.Exit(1)
166+
}
167+
168+
return path
169+
}
170+
171+
wizerBin := "wizer"
172+
if runtime.GOOS == "windows" {
173+
wizerBin += ".exe"
174+
}
175+
176+
path, err := exec.LookPath(wizerBin)
177+
if err != nil {
178+
fmt.Fprintf(os.Stderr, "cannot use %q as wizer: %s", path, err.Error())
179+
os.Exit(1)
180+
}
181+
return path
182+
154183
default:
155184
return ""
156185
}

main.go

+8
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,7 @@ func main() {
14451445
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
14461446
monitor := flag.Bool("monitor", false, "enable serial monitor")
14471447
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
1448+
wizerInit := flag.Bool("wizer-init", false, "use wizer for package initialization")
14481449

14491450
// Internal flags, that are only intended for TinyGo development.
14501451
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
@@ -1517,6 +1518,11 @@ func main() {
15171518
ocdCommands = strings.Split(*ocdCommandsString, ",")
15181519
}
15191520

1521+
if *wizerInit && *target != "wasi" {
1522+
fmt.Fprintf(os.Stderr, "-wizer-init only makes sense with -target=wasi, got -target=%q\n", *target)
1523+
os.Exit(1)
1524+
}
1525+
15201526
options := &compileopts.Options{
15211527
GOOS: goenv.Get("GOOS"),
15221528
GOARCH: goenv.Get("GOARCH"),
@@ -1549,7 +1555,9 @@ func main() {
15491555
Monitor: *monitor,
15501556
BaudRate: *baudrate,
15511557
Timeout: *timeout,
1558+
WizerInit: *wizerInit,
15521559
}
1560+
15531561
if *printCommands {
15541562
options.PrintCommands = printCommand
15551563
}

src/runtime/runtime.go

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ const Compiler = "tinygo"
1212
// package.
1313
func initAll()
1414

15+
var runtimeInitialized = false
16+
17+
func runtime_is_initialized() bool {
18+
return runtimeInitialized
19+
}
20+
1521
//go:linkname callMain main.main
1622
func callMain()
1723

src/runtime/runtime_wasm_wasi.go

+35-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,29 @@ func __wasm_call_ctors()
1515

1616
//export _start
1717
func _start() {
18+
runtimeInitialize()
19+
run()
20+
}
21+
22+
//export runtime.wizerInit
23+
func runtimeInitialize() {
24+
if runtimeInitialized {
25+
// Second time initialization is happening. Refresh environment
26+
// to whatever our current host gives us instead of whatever
27+
// libc cached.
28+
reset_libc_environment()
29+
return
30+
}
1831
// These need to be initialized early so that the heap can be initialized.
1932
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
2033
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
21-
run()
34+
initHeap()
35+
if hasScheduler {
36+
go initAll()
37+
} else {
38+
initAll()
39+
}
40+
runtimeInitialized = true
2241
}
2342

2443
// Read the command line arguments from WASI.
@@ -29,6 +48,17 @@ func init() {
2948
__wasm_call_ctors()
3049
}
3150

51+
//export __wasilibc_deinitialize_environ
52+
func __wasilibc_deinitialize_environ()
53+
54+
//export __wasilibc_initialize_environ
55+
func __wasilibc_initialize_environ()
56+
57+
func reset_libc_environment() {
58+
__wasilibc_deinitialize_environ()
59+
__wasilibc_initialize_environ()
60+
}
61+
3262
var args []string
3363

3464
//go:linkname os_runtime_args os.runtime_args
@@ -39,7 +69,10 @@ func os_runtime_args() []string {
3969
var argc, argv_buf_size uint32
4070
args_sizes_get(&argc, &argv_buf_size)
4171
if argc == 0 {
42-
return nil
72+
// Most things expect os.Args to have at least the
73+
// program name. We don't have one, but also don't
74+
// return just a nil slice.
75+
return []string{""}
4376
}
4477

4578
// Obtain the command line arguments

src/runtime/scheduler_any.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ func sleep(duration int64) {
1919
// run is called by the program entry point to execute the go program.
2020
// With a scheduler, init and the main function are invoked in a goroutine before starting the scheduler.
2121
func run() {
22-
initHeap()
22+
if !runtimeInitialized {
23+
initHeap()
24+
}
2325
go func() {
24-
initAll()
26+
if !runtimeInitialized {
27+
initAll()
28+
runtimeInitialized = true
29+
}
2530
callMain()
2631
schedulerDone = true
2732
}()

src/runtime/scheduler_none.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ func getSystemStackPointer() uintptr {
2020
// run is called by the program entry point to execute the go program.
2121
// With the "none" scheduler, init and the main function are invoked directly.
2222
func run() {
23-
initHeap()
24-
initAll()
23+
if !runtimeInitialized {
24+
initHeap()
25+
initAll()
26+
runtimeInitialized = true
27+
}
2528
callMain()
2629
}
2730

src/syscall/build_asserts.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build syscall_asserts
2+
3+
package syscall
4+
5+
// enable assertions for checking Environ during runtime preinitialization.
6+
// This is to catch people using os.Environ() during package inits with wizer.
7+
const panicOnEnvironDuringInitAll = true

src/syscall/build_noasserts.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build !syscall_asserts
2+
3+
package syscall
4+
5+
// enable assertions for checking Environ during runtime preinitialization.
6+
// This is to catch people using os.Environ() during package init with wizer.
7+
const panicOnEnvironDuringInitAll = false

src/syscall/syscall_libc.go

+6
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,13 @@ func Mprotect(b []byte, prot int) (err error) {
250250
return
251251
}
252252

253+
//go:linkname runtime_is_initialized runtime.runtime_is_initialized
254+
func runtime_is_initialized() bool
255+
253256
func Environ() []string {
257+
if panicOnEnvironDuringInitAll && !runtime_is_initialized() {
258+
panic("syscall.Environ() called during runtime preinitialization")
259+
}
254260

255261
// This function combines all the environment into a single allocation.
256262
// While this optimizes for memory usage and garbage collector

0 commit comments

Comments
 (0)