Skip to content

Commit dfa3a21

Browse files
committed
wasm: add Boehm GC support
This adds support for `-gc=boehm` on `-target=wasip1` and `-target=wasm` (in a browser or NodeJS). Notably it does *not* add Boehm GC support for `-target=wasip2`, since that target doesn't have a real libc.
1 parent 9ca6f72 commit dfa3a21

File tree

21 files changed

+121
-60
lines changed

21 files changed

+121
-60
lines changed

builder/bdwgc.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var BoehmGC = Library{
1414
name: "bdwgc",
1515
cflags: func(target, headerPath string) []string {
1616
libdir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
17-
return []string{
17+
flags := []string{
1818
// use a modern environment
1919
"-DUSE_MMAP", // mmap is available
2020
"-DUSE_MUNMAP", // return memory to the OS using munmap
@@ -30,6 +30,7 @@ var BoehmGC = Library{
3030
// Use a minimal environment.
3131
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
3232
"-DDONT_USE_ATEXIT",
33+
"-DNO_GETENV",
3334

3435
// Special flag to work around the lack of __data_start in ld.lld.
3536
// TODO: try to fix this in LLVM/lld directly so we don't have to
@@ -49,17 +50,22 @@ var BoehmGC = Library{
4950

5051
"-I" + libdir + "/include",
5152
}
53+
if strings.HasPrefix(target, "wasm") {
54+
// Save binary size on WebAssembly by disabling snprintf.
55+
// This reduces the binary by around 18kB.
56+
flags = append(flags, "-DGC_DISABLE_SNPRINTF=1")
57+
}
58+
return flags
5259
},
5360
needsLibc: true,
5461
sourceDir: func() string {
5562
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
5663
},
57-
librarySources: func(target string) ([]string, error) {
64+
librarySources: func(target string, _ bool) ([]string, error) {
5865
sources := []string{
5966
"allchblk.c",
6067
"alloc.c",
6168
"blacklst.c",
62-
"dbg_mlc.c",
6369
"dyn_load.c",
6470
"finalize.c",
6571
"headers.c",

builder/builtins.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ var libCompilerRT = Library{
226226
// Development build.
227227
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
228228
},
229-
librarySources: func(target string) ([]string, error) {
229+
librarySources: func(target string, _ bool) ([]string, error) {
230230
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
231231
switch compileopts.CanonicalArchName(target) {
232232
case "arm":

builder/library.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Library struct {
3838
sourceDir func() string
3939

4040
// The source files, relative to sourceDir.
41-
librarySources func(target string) ([]string, error)
41+
librarySources func(target string, libcNeedsMalloc bool) ([]string, error)
4242

4343
// The source code for the crt1.o file, relative to sourceDir.
4444
crt1Source string
@@ -226,7 +226,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
226226

227227
// Create jobs to compile all sources. These jobs are depended upon by the
228228
// archive job above, so must be run first.
229-
paths, err := l.librarySources(target)
229+
paths, err := l.librarySources(target, config.LibcNeedsMalloc())
230230
if err != nil {
231231
return nil, nil, err
232232
}

builder/mingw-w64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ var libMinGW = Library{
4848
}
4949
return flags
5050
},
51-
librarySources: func(target string) ([]string, error) {
51+
librarySources: func(target string, _ bool) ([]string, error) {
5252
// These files are needed so that printf and the like are supported.
5353
var sources []string
5454
if strings.Split(target, "-")[0] == "i386" {

builder/musl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ var libMusl = Library{
121121
return cflags
122122
},
123123
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
124-
librarySources: func(target string) ([]string, error) {
124+
librarySources: func(target string, _ bool) ([]string, error) {
125125
arch := compileopts.MuslArchitecture(target)
126126
globs := []string{
127127
"conf/*.c",

builder/picolibc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var libPicolibc = Library{
4343
}
4444
},
4545
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
46-
librarySources: func(target string) ([]string, error) {
46+
librarySources: func(target string, _ bool) ([]string, error) {
4747
sources := append([]string(nil), picolibcSources...)
4848
if !strings.HasPrefix(target, "avr") {
4949
// Small chips without long jumps can't compile many files (printf,

builder/wasilibc.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ var libWasiLibc = Library{
120120
return nil
121121
},
122122
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
123-
librarySources: func(target string) ([]string, error) {
123+
librarySources: func(target string, libcNeedsMalloc bool) ([]string, error) {
124124
type filePattern struct {
125125
glob string
126126
exclude []string
@@ -169,6 +169,11 @@ var libWasiLibc = Library{
169169
{glob: "libc-bottom-half/sources/*.c"},
170170
}
171171

172+
// We're using the Boehm GC, so we need a heap implementation in the libc.
173+
if libcNeedsMalloc {
174+
globs = append(globs, filePattern{glob: "dlmalloc/src/dlmalloc.c"})
175+
}
176+
172177
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
173178
sources := []string{
174179
"libc-top-half/musl/src/misc/a64l.c",

builder/wasmbuiltins.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var libWasmBuiltins = Library{
4141
}
4242
},
4343
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
44-
librarySources: func(target string) ([]string, error) {
44+
librarySources: func(target string, _ bool) ([]string, error) {
4545
return []string{
4646
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
4747
// llvm.memset.* LLVM intrinsics.

compileopts/config.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (c *Config) GC() string {
133133
// that can be traced by the garbage collector.
134134
func (c *Config) NeedsStackObjects() bool {
135135
switch c.GC() {
136-
case "conservative", "custom", "precise":
136+
case "conservative", "custom", "precise", "boehm":
137137
for _, tag := range c.BuildTags() {
138138
if tag == "tinygo.wasm" {
139139
return true
@@ -258,6 +258,15 @@ func MuslArchitecture(triple string) string {
258258
return CanonicalArchName(triple)
259259
}
260260

261+
// Returns true if the libc needs to include malloc, for the libcs where this
262+
// matters.
263+
func (c *Config) LibcNeedsMalloc() bool {
264+
if c.GC() == "boehm" && c.Target.Libc == "wasi-libc" {
265+
return true
266+
}
267+
return false
268+
}
269+
261270
// LibraryPath returns the path to the library build directory. The path will be
262271
// a library path in the cache directory (which might not yet be built).
263272
func (c *Config) LibraryPath(name string) string {
@@ -281,9 +290,14 @@ func (c *Config) LibraryPath(name string) string {
281290
archname += "-v" + strconv.Itoa(v)
282291
}
283292

293+
options := ""
294+
if c.LibcNeedsMalloc() {
295+
options += "+malloc"
296+
}
297+
284298
// No precompiled library found. Determine the path name that will be used
285299
// in the build cache.
286-
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname)
300+
return filepath.Join(goenv.Get("GOCACHE"), name+options+"-"+archname)
287301
}
288302

289303
// DefaultBinaryExtension returns the default extension for binaries, such as

compileopts/target.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
266266
DefaultStackSize: 1024 * 64, // 64kB
267267
GDB: []string{"gdb"},
268268
PortReset: "false",
269+
ExtraFiles: []string{
270+
"src/runtime/gc_boehm.c",
271+
},
269272
}
270273

271274
// Configure target based on GOARCH.

0 commit comments

Comments
 (0)