mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
all: add the Boehm-Demers-Weiser GC on Linux
This adds support for the well-known Boehm GC. It's significantly faster than our own naive GC and could be used as an alternative on bigger systems. In the future, this GC might also be supported on WebAssembly with some extra work. Right now it's Linux only (though Windows/MacOS shouldn't be too difficult to add).
This commit is contained in:
committed by
Ron Evans
parent
a4cbe3326e
commit
3a7c25f987
@@ -22,9 +22,9 @@ jobs:
|
||||
run: sudo apt-get remove llvm-18
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Pull musl
|
||||
- name: Pull musl, bdwgc
|
||||
run: |
|
||||
git submodule update --init lib/musl
|
||||
git submodule update --init lib/musl lib/bdwgc
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
|
||||
@@ -39,3 +39,6 @@
|
||||
[submodule "lib/wasi-cli"]
|
||||
path = lib/wasi-cli
|
||||
url = https://github.com/WebAssembly/wasi-cli
|
||||
[submodule "lib/bdwgc"]
|
||||
path = lib/bdwgc
|
||||
url = https://github.com/ivmai/bdwgc.git
|
||||
|
||||
@@ -940,6 +940,7 @@ wasmtest:
|
||||
|
||||
build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/bdwgc
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||
@@ -961,6 +962,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
|
||||
ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
|
||||
endif
|
||||
@cp -rp lib/bdwgc/* build/release/tinygo/lib/bdwgc
|
||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||
@@ -974,6 +976,7 @@ endif
|
||||
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||
@cp -rp lib/musl/include build/release/tinygo/lib/musl
|
||||
@cp -rp lib/musl/src/ctype build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
|
||||
@@ -988,8 +991,10 @@ endif
|
||||
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/misc build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/sched build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/stdlib build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package builder
|
||||
|
||||
// The well-known conservative Boehm-Demers-Weiser GC.
|
||||
// This file provides a way to compile this GC for use with TinyGo.
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var BoehmGC = Library{
|
||||
name: "bdwgc",
|
||||
cflags: func(target, headerPath string) []string {
|
||||
libdir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
||||
return []string{
|
||||
// use a modern environment
|
||||
"-DUSE_MMAP", // mmap is available
|
||||
"-DUSE_MUNMAP", // return memory to the OS using munmap
|
||||
"-DGC_BUILTIN_ATOMIC", // use compiler intrinsics for atomic operations
|
||||
"-DNO_EXECUTE_PERMISSION", // don't make the heap executable
|
||||
|
||||
// specific flags for TinyGo
|
||||
"-DALL_INTERIOR_POINTERS", // scan interior pointers (needed for Go)
|
||||
"-DIGNORE_DYNAMIC_LOADING", // we don't support dynamic loading at the moment
|
||||
"-DNO_GETCONTEXT", // musl doesn't support getcontext()
|
||||
|
||||
// Special flag to work around the lack of __data_start in ld.lld.
|
||||
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
||||
// work around it anymore.
|
||||
"-DGC_DONT_REGISTER_MAIN_STATIC_DATA",
|
||||
|
||||
// Do not scan the stack. We have our own mechanism to do this.
|
||||
"-DSTACK_NOT_SCANNED",
|
||||
|
||||
// Assertions can be enabled while debugging GC issues.
|
||||
//"-DGC_ASSERTIONS",
|
||||
|
||||
// Threading is not yet supported, so these are disabled.
|
||||
//"-DGC_THREADS",
|
||||
//"-DTHREAD_LOCAL_ALLOC",
|
||||
|
||||
"-I" + libdir + "/include",
|
||||
}
|
||||
},
|
||||
sourceDir: func() string {
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
||||
},
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
return []string{
|
||||
"allchblk.c",
|
||||
"alloc.c",
|
||||
"blacklst.c",
|
||||
"dbg_mlc.c",
|
||||
"dyn_load.c",
|
||||
"finalize.c",
|
||||
"headers.c",
|
||||
"mach_dep.c",
|
||||
"malloc.c",
|
||||
"mark.c",
|
||||
"mark_rts.c",
|
||||
"misc.c",
|
||||
"new_hblk.c",
|
||||
"obj_map.c",
|
||||
"os_dep.c",
|
||||
"pthread_stop_world.c",
|
||||
"pthread_support.c",
|
||||
"reclaim.c",
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
+22
-7
@@ -147,20 +147,22 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
// the libc needs them.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
var libcDependencies []*compileJob
|
||||
var libcJob *compileJob
|
||||
switch config.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
job := makeDarwinLibSystemJob(config, tmpdir)
|
||||
libcDependencies = append(libcDependencies, job)
|
||||
case "musl":
|
||||
job, unlock, err := libMusl.load(config, tmpdir)
|
||||
var unlock func()
|
||||
libcJob, unlock, err = libMusl.load(config, tmpdir, nil)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
|
||||
libcDependencies = append(libcDependencies, job)
|
||||
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(libcJob.result), "crt1.o")))
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "picolibc":
|
||||
libcJob, unlock, err := libPicolibc.load(config, tmpdir)
|
||||
libcJob, unlock, err := libPicolibc.load(config, tmpdir, nil)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
@@ -173,14 +175,14 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
||||
case "wasmbuiltins":
|
||||
libcJob, unlock, err := libWasmBuiltins.load(config, tmpdir)
|
||||
libcJob, unlock, err := libWasmBuiltins.load(config, tmpdir, nil)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "mingw-w64":
|
||||
job, unlock, err := libMinGW.load(config, tmpdir)
|
||||
job, unlock, err := libMinGW.load(config, tmpdir, nil)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
@@ -701,7 +703,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||
// a cache.
|
||||
if config.Target.RTLib == "compiler-rt" {
|
||||
job, unlock, err := libCompilerRT.load(config, tmpdir)
|
||||
job, unlock, err := libCompilerRT.load(config, tmpdir, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@@ -709,6 +711,19 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
// The Boehm collector is stored in a separate C library.
|
||||
if config.GC() == "boehm" {
|
||||
if libcJob == nil {
|
||||
return BuildResult{}, fmt.Errorf("boehm GC isn't supported with libc %s", config.Target.Libc)
|
||||
}
|
||||
job, unlock, err := BoehmGC.load(config, tmpdir, libcJob)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
defer unlock()
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
// Add jobs to compile extra files. These files are in C or assembly and
|
||||
// contain things like the interrupt vector table and low level operations
|
||||
// such as stack switching.
|
||||
|
||||
+20
-5
@@ -43,7 +43,11 @@ type Library struct {
|
||||
// output archive file, it is expected to be removed after use.
|
||||
// As a side effect, this call creates the library header files if they didn't
|
||||
// exist yet.
|
||||
func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, abortLock func(), err error) {
|
||||
// The provided libc job (if not null) will cause this libc to be added as a
|
||||
// dependency for all C compiler jobs, and adds libc headers for the given
|
||||
// target config. In other words, pass this libc if the library needs a libc to
|
||||
// compile.
|
||||
func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJob) (job *compileJob, abortLock func(), err error) {
|
||||
outdir, precompiled := config.LibcPath(l.name)
|
||||
archiveFilePath := filepath.Join(outdir, "lib.a")
|
||||
if precompiled {
|
||||
@@ -181,6 +185,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
args = append(args, "-mfpu=vfpv2")
|
||||
}
|
||||
}
|
||||
if libc != nil {
|
||||
args = append(args, config.LibcCFlags()...)
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
@@ -233,7 +240,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
objpath := filepath.Join(dir, cleanpath+".o")
|
||||
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||
objs = append(objs, objpath)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
objfile := &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
var compileArgs []string
|
||||
@@ -248,7 +255,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
if libc != nil {
|
||||
objfile.dependencies = append(objfile.dependencies, libc)
|
||||
}
|
||||
job.dependencies = append(job.dependencies, objfile)
|
||||
}
|
||||
|
||||
// Create crt1.o job, if needed.
|
||||
@@ -257,7 +268,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// won't make much of a difference in speed).
|
||||
if l.crt1Source != "" {
|
||||
srcpath := filepath.Join(sourceDir, l.crt1Source)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
crt1Job := &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
var compileArgs []string
|
||||
@@ -277,7 +288,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
}
|
||||
return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o"))
|
||||
},
|
||||
})
|
||||
}
|
||||
if libc != nil {
|
||||
crt1Job.dependencies = append(crt1Job.dependencies, libc)
|
||||
}
|
||||
job.dependencies = append(job.dependencies, crt1Job)
|
||||
}
|
||||
|
||||
ok = true
|
||||
|
||||
@@ -113,26 +113,32 @@ var libMusl = Library{
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
globs := []string{
|
||||
"ctype/*.c",
|
||||
"env/*.c",
|
||||
"errno/*.c",
|
||||
"exit/*.c",
|
||||
"fcntl/*.c",
|
||||
"internal/defsysinfo.c",
|
||||
"internal/intscan.c",
|
||||
"internal/libc.c",
|
||||
"internal/shgetc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"locale/*.c",
|
||||
"linux/*.c",
|
||||
"locale/*.c",
|
||||
"malloc/*.c",
|
||||
"malloc/mallocng/*.c",
|
||||
"mman/*.c",
|
||||
"math/*.c",
|
||||
"misc/*.c",
|
||||
"multibyte/*.c",
|
||||
"sched/*.c",
|
||||
"signal/" + arch + "/*.s",
|
||||
"signal/*.c",
|
||||
"stdio/*.c",
|
||||
"stdlib/*.c",
|
||||
"string/*.c",
|
||||
"thread/" + arch + "/*.s",
|
||||
"thread/*.c",
|
||||
|
||||
+67
-53
@@ -261,6 +261,10 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
if c.Target.SoftFloat {
|
||||
archname += "-softfloat"
|
||||
}
|
||||
if name == "bdwgc" {
|
||||
// Boehm GC is compiled against a particular libc.
|
||||
archname += "-" + c.Target.Libc
|
||||
}
|
||||
|
||||
// Try to load a precompiled library.
|
||||
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
|
||||
@@ -315,59 +319,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
||||
"-resource-dir="+resourceDir,
|
||||
)
|
||||
}
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
|
||||
)
|
||||
case "picolibc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||
path, _ := c.LibcPath("picolibc")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||
)
|
||||
case "musl":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("musl")
|
||||
arch := MuslArchitecture(c.Triple())
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", "generic"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||
)
|
||||
case "wasi-libc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", root+"/lib/wasi-libc/sysroot/include")
|
||||
case "wasmbuiltins":
|
||||
// nothing to add (library is purely for builtins)
|
||||
case "mingw-w64":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("mingw-w64")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-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", "defaults", "include"),
|
||||
"-D_UCRT",
|
||||
)
|
||||
case "":
|
||||
// No libc specified, nothing to add.
|
||||
default:
|
||||
// Incorrect configuration. This could be handled in a better way, but
|
||||
// usually this will be found by developers (not by TinyGo users).
|
||||
panic("unknown libc: " + c.Target.Libc)
|
||||
}
|
||||
cflags = append(cflags, c.LibcCFlags()...)
|
||||
// Always emit debug information. It is optionally stripped at link time.
|
||||
cflags = append(cflags, "-gdwarf-4")
|
||||
// Use the same optimization level as TinyGo.
|
||||
@@ -394,6 +346,68 @@ func (c *Config) CFlags(libclang bool) []string {
|
||||
return cflags
|
||||
}
|
||||
|
||||
// LibcCFlags returns the C compiler flags for the configured libc.
|
||||
// It only uses flags that are part of the libc path (triple, cpu, abi, libc
|
||||
// name) so it can safely be used to compile another C library.
|
||||
func (c *Config) LibcCFlags() []string {
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
|
||||
}
|
||||
case "picolibc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||
path, _ := c.LibcPath("picolibc")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||
}
|
||||
case "musl":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("musl")
|
||||
arch := MuslArchitecture(c.Triple())
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", "generic"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||
}
|
||||
case "wasi-libc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", root + "/lib/wasi-libc/sysroot/include",
|
||||
}
|
||||
case "wasmbuiltins":
|
||||
// nothing to add (library is purely for builtins)
|
||||
return nil
|
||||
case "mingw-w64":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("mingw-w64")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-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", "defaults", "include"),
|
||||
"-D_UCRT",
|
||||
}
|
||||
case "":
|
||||
// No libc specified, nothing to add.
|
||||
return nil
|
||||
default:
|
||||
// Incorrect configuration. This could be handled in a better way, but
|
||||
// usually this will be found by developers (not by TinyGo users).
|
||||
panic("unknown libc: " + c.Target.Libc)
|
||||
}
|
||||
}
|
||||
|
||||
// LDFlags returns the flags to pass to the linker. A few more flags are needed
|
||||
// (like the one for the compiler runtime), but this represents the majority of
|
||||
// the flags.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
var (
|
||||
validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"}
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise", "boehm"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full", "html"}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestVerifyOptions(t *testing.T) {
|
||||
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise, boehm`)
|
||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`)
|
||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||
|
||||
@@ -247,7 +247,6 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
GOOS: options.GOOS,
|
||||
GOARCH: options.GOARCH,
|
||||
BuildTags: []string{options.GOOS, options.GOARCH},
|
||||
GC: "precise",
|
||||
Scheduler: "tasks",
|
||||
Linker: "cc",
|
||||
DefaultStackSize: 1024 * 64, // 64kB
|
||||
@@ -366,6 +365,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
llvmvendor := "unknown"
|
||||
switch options.GOOS {
|
||||
case "darwin":
|
||||
spec.GC = "precise"
|
||||
platformVersion := "10.12.0"
|
||||
if options.GOARCH == "arm64" {
|
||||
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
||||
@@ -388,6 +388,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
case "linux":
|
||||
spec.GC = "boehm"
|
||||
spec.Linker = "ld.lld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "musl"
|
||||
@@ -411,6 +412,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
case "windows":
|
||||
spec.GC = "precise"
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "mingw-w64"
|
||||
// Note: using a medium code model, low image base and no ASLR
|
||||
|
||||
Submodule
+1
Submodule lib/bdwgc added at d1ff06cc50
@@ -534,6 +534,12 @@ func markRoots(start, end uintptr) {
|
||||
}
|
||||
}
|
||||
|
||||
func markCurrentGoroutineStack(sp uintptr) {
|
||||
// This could be optimized by only marking the stack area that's currently
|
||||
// in use.
|
||||
markRoot(0, sp)
|
||||
}
|
||||
|
||||
// stackOverflow is a flag which is set when the GC scans too deep while marking.
|
||||
// After it is set, all marked allocations must be re-scanned.
|
||||
var stackOverflow bool
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
//go:build gc.boehm
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"internal/gclayout"
|
||||
"internal/reflectlite"
|
||||
"internal/task"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const needsStaticHeap = false
|
||||
|
||||
// zeroSizedAlloc is just a sentinel that gets returned when allocating 0 bytes.
|
||||
var zeroSizedAlloc uint8
|
||||
|
||||
var gcLock task.PMutex
|
||||
|
||||
func initHeap() {
|
||||
libgc_init()
|
||||
|
||||
libgc_set_push_other_roots(gcCallbackPtr)
|
||||
}
|
||||
|
||||
var gcCallbackPtr = reflectlite.ValueOf(gcCallback).UnsafePointer()
|
||||
|
||||
func gcCallback() {
|
||||
// Mark the system stack and (if we're on a goroutine stack) also the
|
||||
// current goroutine stack.
|
||||
markStack()
|
||||
|
||||
findGlobals(func(start, end uintptr) {
|
||||
libgc_push_all(start, end)
|
||||
})
|
||||
}
|
||||
|
||||
func markRoots(start, end uintptr) {
|
||||
libgc_push_all(start, end)
|
||||
}
|
||||
|
||||
func markCurrentGoroutineStack(sp uintptr) {
|
||||
// Only mark the area of the stack that is currently in use.
|
||||
// (This doesn't work for other goroutines, but at least it doesn't keep
|
||||
// more pointers alive than needed on the current stack).
|
||||
base := libgc_base(sp)
|
||||
if base == 0 { // && asserts
|
||||
runtimePanic("goroutine stack not in a heap allocation?")
|
||||
}
|
||||
stackBottom := base + libgc_size(base)
|
||||
libgc_push_all_stack(sp, stackBottom)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
if size == 0 {
|
||||
return unsafe.Pointer(&zeroSizedAlloc)
|
||||
}
|
||||
|
||||
gcLock.Lock()
|
||||
var ptr unsafe.Pointer
|
||||
if layout == gclayout.NoPtrs {
|
||||
// This object is entirely pointer free, for example make([]int, ...).
|
||||
// Make sure the GC knows this so it doesn't scan the object
|
||||
// unnecessarily to improve performance.
|
||||
ptr = libgc_malloc_atomic(size)
|
||||
// Memory returned from libgc_malloc_atomic has not been zeroed so we
|
||||
// have to do that manually.
|
||||
memzero(ptr, size)
|
||||
} else {
|
||||
// TODO: bdwgc supports typed allocations, which could be useful to
|
||||
// implement a mostly-precise GC.
|
||||
ptr = libgc_malloc(size)
|
||||
// Memory returned from libgc_malloc has already been zeroed, so nothing
|
||||
// to do here.
|
||||
}
|
||||
gcLock.Unlock()
|
||||
if ptr == nil {
|
||||
runtimePanic("gc: out of memory")
|
||||
}
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
func free(ptr unsafe.Pointer) {
|
||||
libgc_free(ptr)
|
||||
}
|
||||
|
||||
func GC() {
|
||||
libgc_gcollect()
|
||||
}
|
||||
|
||||
// This should be stack-allocated, but we don't currently have a good way of
|
||||
// ensuring that happens.
|
||||
var gcMemStats libgc_prof_stats
|
||||
|
||||
func ReadMemStats(m *MemStats) {
|
||||
gcLock.Lock()
|
||||
|
||||
libgc_get_prof_stats(&gcMemStats, unsafe.Sizeof(gcMemStats))
|
||||
|
||||
// Fill in MemStats as well as we can, given the information that bdwgc
|
||||
// provides to us.
|
||||
m.HeapIdle = uint64(gcMemStats.free_bytes_full - gcMemStats.unmapped_bytes)
|
||||
m.HeapInuse = uint64(gcMemStats.heapsize_full - gcMemStats.unmapped_bytes)
|
||||
m.HeapReleased = uint64(gcMemStats.unmapped_bytes)
|
||||
m.HeapSys = uint64(m.HeapInuse + m.HeapIdle)
|
||||
m.GCSys = 0 // not provided by bdwgc
|
||||
m.TotalAlloc = uint64(gcMemStats.allocd_bytes_before_gc + gcMemStats.bytes_allocd_since_gc)
|
||||
m.Mallocs = 0 // not provided by bdwgc
|
||||
m.Frees = 0 // not provided by bdwgc
|
||||
m.Sys = uint64(gcMemStats.obtained_from_os_bytes)
|
||||
|
||||
gcLock.Unlock()
|
||||
}
|
||||
|
||||
func setHeapEnd(newHeapEnd uintptr) {
|
||||
runtimePanic("gc: did not expect setHeapEnd call")
|
||||
}
|
||||
|
||||
func SetFinalizer(obj interface{}, finalizer interface{}) {
|
||||
// Unimplemented.
|
||||
// The GC *does* support finalization, so this could be added relatively
|
||||
// easily I think.
|
||||
}
|
||||
|
||||
//export GC_init
|
||||
func libgc_init()
|
||||
|
||||
//export GC_malloc
|
||||
func libgc_malloc(uintptr) unsafe.Pointer
|
||||
|
||||
//export GC_malloc_atomic
|
||||
func libgc_malloc_atomic(uintptr) unsafe.Pointer
|
||||
|
||||
//export GC_free
|
||||
func libgc_free(unsafe.Pointer)
|
||||
|
||||
//export GC_base
|
||||
func libgc_base(ptr uintptr) uintptr
|
||||
|
||||
//export GC_size
|
||||
func libgc_size(ptr uintptr) uintptr
|
||||
|
||||
//export GC_push_all
|
||||
func libgc_push_all(bottom, top uintptr)
|
||||
|
||||
//export GC_push_all_stack
|
||||
func libgc_push_all_stack(bottom, top uintptr)
|
||||
|
||||
//export GC_gcollect
|
||||
func libgc_gcollect()
|
||||
|
||||
//export GC_get_prof_stats
|
||||
func libgc_get_prof_stats(*libgc_prof_stats, uintptr) uintptr
|
||||
|
||||
//export GC_set_push_other_roots
|
||||
func libgc_set_push_other_roots(unsafe.Pointer)
|
||||
|
||||
type libgc_prof_stats struct {
|
||||
heapsize_full uintptr
|
||||
free_bytes_full uintptr
|
||||
unmapped_bytes uintptr
|
||||
bytes_allocd_since_gc uintptr
|
||||
allocd_bytes_before_gc uintptr
|
||||
non_gc_bytes uintptr
|
||||
gc_no uintptr
|
||||
markers_m1 uintptr
|
||||
bytes_reclaimed_since_gc uintptr
|
||||
reclaimed_bytes_before_gc uintptr
|
||||
expl_freed_bytes_since_gc uintptr
|
||||
obtained_from_os_bytes uintptr
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build (gc.conservative || gc.precise) && (baremetal || tinygo.wasm)
|
||||
//go:build baremetal || tinygo.wasm
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build (gc.conservative || gc.precise) && !tinygo.wasm
|
||||
//go:build (gc.conservative || gc.precise || gc.boehm) && !tinygo.wasm
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -33,7 +33,6 @@ func scanstack(sp uintptr) {
|
||||
markRoots(sp, stackTop)
|
||||
} else {
|
||||
// This is a goroutine stack.
|
||||
// It is an allocation, so scan it as if it were a value in a global.
|
||||
markRoot(0, sp)
|
||||
markCurrentGoroutineStack(sp)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user