From 8dba49137e1c9d2eaab1c31fd7a85005a996410d Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 15 Jan 2020 15:34:43 +0100 Subject: [PATCH 1/2] builder: refactor compiler-rt library This refactor makes adding a new library (such as a libc) much easier in the future as it avoids a lot of duplicate code. Additionally, CI should become a little bit faster (~15s) as build-builtins now uses the build cache. --- builder/build.go | 15 +++--- builder/builtins.go | 120 ++++++-------------------------------------- builder/library.go | 99 ++++++++++++++++++++++++++++++++++++ main.go | 16 ++++-- 4 files changed, 133 insertions(+), 117 deletions(-) create mode 100644 builder/library.go diff --git a/builder/build.go b/builder/build.go index 3ce7f644ee..d3e9fcc7fc 100644 --- a/builder/build.go +++ b/builder/build.go @@ -130,21 +130,18 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri return err } + // Prepare link command. + executable := filepath.Join(dir, "main") + tmppath := executable // final file + ldflags := append(config.LDFlags(), "-o", executable, objfile) + // Load builtins library from the cache, possibly compiling it on the // fly. - var librt string if config.Target.RTLib == "compiler-rt" { - librt, err = loadBuiltins(config.Triple()) + librt, err := CompilerRT.Load(config.Triple()) if err != nil { return err } - } - - // Prepare link command. - executable := filepath.Join(dir, "main") - tmppath := executable // final file - ldflags := append(config.LDFlags(), "-o", executable, objfile) - if config.Target.RTLib == "compiler-rt" { ldflags = append(ldflags, librt) } diff --git a/builder/builtins.go b/builder/builtins.go index 9a1eb14803..a64a1d1464 100644 --- a/builder/builtins.go +++ b/builder/builtins.go @@ -1,12 +1,7 @@ package builder import ( - "io/ioutil" - "os" - "path/filepath" "strings" - - "github.com/tinygo-org/tinygo/goenv" ) // These are the GENERIC_SOURCES according to CMakeList.txt. @@ -156,105 +151,20 @@ var aeabiBuiltins = []string{ "arm/aeabi_uldivmod.S", } -func builtinFiles(target string) []string { - builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins - if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { - builtins = append(builtins, aeabiBuiltins...) - } - return builtins -} - -// builtinsDir returns the directory where the sources for compiler-rt are kept. -func builtinsDir() string { - return filepath.Join(goenv.Get("TINYGOROOT"), "lib", "compiler-rt", "lib", "builtins") -} - -// Get the builtins archive, possibly generating it as needed. -func loadBuiltins(target string) (path string, err error) { - // Try to load a precompiled compiler-rt library. - precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, "compiler-rt.a") - if _, err := os.Stat(precompiledPath); err == nil { - // Found a precompiled compiler-rt for this OS/architecture. Return the - // path directly. - return precompiledPath, nil - } - - outfile := "librt-" + target + ".a" - builtinsDir := builtinsDir() - - builtins := builtinFiles(target) - srcs := make([]string, len(builtins)) - for i, name := range builtins { - srcs[i] = filepath.Join(builtinsDir, name) - } - - if path, err := cacheLoad(outfile, commands["clang"][0], srcs); path != "" || err != nil { - return path, err - } - - var cachepath string - err = CompileBuiltins(target, func(path string) error { - path, err := cacheStore(path, outfile, commands["clang"][0], srcs) - cachepath = path - return err - }) - return cachepath, err -} - -// CompileBuiltins compiles builtins from compiler-rt into a static library. -// When it succeeds, it will call the callback with the resulting path. The path -// will be removed after callback returns. If callback returns an error, this is -// passed through to the return value of this function. -func CompileBuiltins(target string, callback func(path string) error) error { - builtinsDir := builtinsDir() - - builtins := builtinFiles(target) - srcs := make([]string, len(builtins)) - for i, name := range builtins { - srcs[i] = filepath.Join(builtinsDir, name) - } - - dirPrefix := "tinygo-builtins" - remapDir := filepath.Join(os.TempDir(), dirPrefix) - dir, err := ioutil.TempDir(os.TempDir(), dirPrefix) - if err != nil { - return err - } - defer os.RemoveAll(dir) - - // Compile all builtins. - // TODO: use builtins optimized for a given target if available. - objs := make([]string, 0, len(builtins)) - for _, name := range builtins { - objname := name - if strings.LastIndexByte(objname, '/') >= 0 { - objname = objname[strings.LastIndexByte(objname, '/'):] +// CompilerRT is a library with symbols required by programs compiled with LLVM. +// These symbols are for operations that cannot be emitted with a single +// instruction or a short sequence of instructions for that target. +// +// For more information, see: https://compiler-rt.llvm.org/ +var CompilerRT = Library{ + name: "compiler-rt", + cflags: func() []string { return []string{"-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc"} }, + sourceDir: "lib/compiler-rt/lib/builtins", + sources: func(target string) []string { + builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins + if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { + builtins = append(builtins, aeabiBuiltins...) } - objpath := filepath.Join(dir, objname+".o") - objs = append(objs, objpath) - srcpath := filepath.Join(builtinsDir, name) - // Note: -fdebug-prefix-map is necessary to make the output archive - // reproducible. Otherwise the temporary directory is stored in the - // archive itself, which varies each run. - args := []string{"-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target=" + target, "-fdebug-prefix-map=" + dir + "=" + remapDir} - if strings.HasPrefix(target, "riscv32-") { - args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128") - } - err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...) - if err != nil { - return &commandError{"failed to build", srcpath, err} - } - } - - // Put all the object files in a single archive. This archive file will be - // used to statically link compiler-rt. - arpath := filepath.Join(dir, "librt.a") - err = makeArchive(arpath, objs) - if err != nil { - return err - } - - // Give the caller the resulting file. The callback must copy the file, - // because after it returns the temporary directory will be removed. - return callback(arpath) + return builtins + }, } diff --git a/builder/library.go b/builder/library.go new file mode 100644 index 0000000000..0df1f1467e --- /dev/null +++ b/builder/library.go @@ -0,0 +1,99 @@ +package builder + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/tinygo-org/tinygo/goenv" +) + +// Library is a container for information about a single C library, such as a +// compiler runtime or libc. +type Library struct { + // The library name, such as compiler-rt or picolibc. + name string + + cflags func() []string + + // The source directory, relative to TINYGOROOT. + sourceDir string + + // The source files, relative to sourceDir. + sources func(target string) []string +} + +// fullPath returns the full path to the source directory. +func (l *Library) fullPath() string { + return filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir) +} + +// sourcePaths returns a slice with the full paths to the source files. +func (l *Library) sourcePaths(target string) []string { + sources := l.sources(target) + paths := make([]string, len(sources)) + for i, name := range sources { + paths[i] = filepath.Join(l.fullPath(), name) + } + return paths +} + +// Load the library archive, possibly generating and caching it if needed. +func (l *Library) Load(target string) (path string, err error) { + // Try to load a precompiled library. + precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a") + if _, err := os.Stat(precompiledPath); err == nil { + // Found a precompiled library for this OS/architecture. Return the path + // directly. + return precompiledPath, nil + } + + outfile := l.name + "-" + target + ".a" + + // Try to fetch this library from the cache. + if path, err := cacheLoad(outfile, commands["clang"][0], l.sourcePaths(target)); path != "" || err != nil { + // Cache hit. + return path, err + } + // Cache miss, build it now. + + dirPrefix := "tinygo-" + l.name + remapDir := filepath.Join(os.TempDir(), dirPrefix) + dir, err := ioutil.TempDir(os.TempDir(), dirPrefix) + if err != nil { + return "", err + } + defer os.RemoveAll(dir) + + // Precalculate the flags to the compiler invocation. + args := append(l.cflags(), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir) + if strings.HasPrefix(target, "riscv32-") { + args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128") + } + + // Compile all sources. + var objs []string + for _, srcpath := range l.sourcePaths(target) { + objpath := filepath.Join(dir, filepath.Base(srcpath)+".o") + objs = append(objs, objpath) + // Note: -fdebug-prefix-map is necessary to make the output archive + // reproducible. Otherwise the temporary directory is stored in the + // archive itself, which varies each run. + err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...) + if err != nil { + return "", &commandError{"failed to build", srcpath, err} + } + } + + // Put all the object files in a single archive. This archive file will be + // used to statically link this library. + arpath := filepath.Join(dir, l.name+".a") + err = makeArchive(arpath, objs) + if err != nil { + return "", err + } + + // Store this archive in the cache. + return cacheStore(arpath, outfile, commands["clang"][0], l.sourcePaths(target)) +} diff --git a/main.go b/main.go index 19c83353ef..c21b1377bf 100644 --- a/main.go +++ b/main.go @@ -50,6 +50,17 @@ func moveFile(src, dst string) error { } // Failed to move, probably a different filesystem. // Do a copy + remove. + err = copyFile(src, dst) + if err != nil { + return err + } + return os.Remove(src) +} + +// copyFile copies the given file from src to dst. It copies first to a .tmp +// file which is then moved over a possibly already existing file at the +// destination. +func copyFile(src, dst string) error { inf, err := os.Open(src) if err != nil { return err @@ -785,10 +796,9 @@ func main() { if *target == "" { fmt.Fprintln(os.Stderr, "No target (-target).") } - err := builder.CompileBuiltins(*target, func(path string) error { - return moveFile(path, *outpath) - }) + path, err := builder.CompilerRT.Load(*target) handleCompilerError(err) + copyFile(path, *outpath) case "flash", "gdb": if *outpath != "" { fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.") From 6d2fdbb64fdc9542555bc20900c621b671ecb0b2 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 15 Jan 2020 15:59:51 +0100 Subject: [PATCH 2/2] all: include picolibc for bare metal targets This is necessary for better CGo support on bare metal. Existing libraries expect to be able to include parts of libc and expect to be able to link to those symbols. Because with this all targets have a working libc, it is now possible to add tests to check that a libc in fact works basically. Not all parts of picolibc are included, such as the math or stdio parts. These should be added later, when needed. This commit also avoids the need for the custom memcpy/memset/memcmp symbols that are sometimes emitted by LLVM. The C library will take care of that. --- .gitmodules | 3 + Makefile | 16 +++- builder/build.go | 9 ++ builder/picolibc.go | 127 +++++++++++++++++++++++++++++ cgo/cgo.go | 5 ++ compileopts/config.go | 6 ++ compileopts/target.go | 4 + lib/picolibc | 1 + lib/picolibc-include/picolibc.h | 0 main.go | 19 ++++- src/runtime/runtime_arm7tdmi.go | 14 ---- src/runtime/runtime_cortexm.go | 20 ----- src/runtime/runtime_tinygoriscv.go | 19 ----- targets/cortex-m.json | 3 +- targets/gameboy-advance.json | 2 +- targets/riscv.json | 2 +- testdata/cgo/main.go | 7 ++ testdata/cgo/out.txt | 1 + 18 files changed, 196 insertions(+), 62 deletions(-) create mode 100644 builder/picolibc.go create mode 160000 lib/picolibc create mode 100644 lib/picolibc-include/picolibc.h delete mode 100644 src/runtime/runtime_tinygoriscv.go diff --git a/.gitmodules b/.gitmodules index 7e86215efa..af6e1809aa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "lib/wasi-libc"] path = lib/wasi-libc url = https://github.com/CraneStation/wasi-libc +[submodule "lib/picolibc"] + path = lib/picolibc + url = https://github.com/keith-packard/picolibc.git diff --git a/Makefile b/Makefile index 6a18278c6c..e16f225a8c 100644 --- a/Makefile +++ b/Makefile @@ -312,6 +312,7 @@ release: tinygo gen-device wasi-libc @mkdir -p build/release/tinygo/lib/CMSIS/CMSIS @mkdir -p build/release/tinygo/lib/compiler-rt/lib @mkdir -p build/release/tinygo/lib/nrfx + @mkdir -p build/release/tinygo/lib/picolibc/newlib/libc @mkdir -p build/release/tinygo/lib/wasi-libc @mkdir -p build/release/tinygo/pkg/armv6m-none-eabi @mkdir -p build/release/tinygo/pkg/armv7m-none-eabi @@ -325,10 +326,19 @@ release: tinygo gen-device wasi-libc @cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt @cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt @cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx + @cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc + @cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc + @cp -rp lib/picolibc/newlib/libc/locale build/release/tinygo/lib/picolibc/newlib/libc + @cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc + @cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc + @cp -rp lib/picolibc-include build/release/tinygo/lib @cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot @cp -rp src build/release/tinygo/src @cp -rp targets build/release/tinygo/targets - ./build/tinygo build-builtins -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a - ./build/tinygo build-builtins -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/compiler-rt.a - ./build/tinygo build-builtins -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/compiler-rt.a + ./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a compiler-rt + ./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/compiler-rt.a compiler-rt + ./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/compiler-rt.a compiler-rt + ./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/picolibc.a picolibc + ./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/picolibc.a picolibc + ./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/picolibc.a picolibc tar -czf build/release.tar.gz -C build/release tinygo diff --git a/builder/build.go b/builder/build.go index d3e9fcc7fc..85a4dd515f 100644 --- a/builder/build.go +++ b/builder/build.go @@ -145,6 +145,15 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri ldflags = append(ldflags, librt) } + // Add libc. + if config.Target.Libc == "picolibc" { + libc, err := Picolibc.Load(config.Triple()) + if err != nil { + return err + } + ldflags = append(ldflags, libc) + } + // Compile extra files. root := goenv.Get("TINYGOROOT") for i, path := range config.ExtraFiles() { diff --git a/builder/picolibc.go b/builder/picolibc.go new file mode 100644 index 0000000000..31e2f05308 --- /dev/null +++ b/builder/picolibc.go @@ -0,0 +1,127 @@ +package builder + +import ( + "path/filepath" + + "github.com/tinygo-org/tinygo/goenv" +) + +// Picolibc is a C library for bare metal embedded devices. It was originally +// based on newlib. +var Picolibc = Library{ + name: "picolibc", + cflags: func() []string { + picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") + return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "-fshort-enums", "--sysroot=" + picolibcDir, "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"} + }, + sourceDir: "lib/picolibc/newlib/libc", + sources: func(target string) []string { + return picolibcSources + }, +} + +var picolibcSources = []string{ + "string/bcmp.c", + "string/bcopy.c", + "string/bzero.c", + "string/explicit_bzero.c", + "string/ffsl.c", + "string/ffsll.c", + "string/fls.c", + "string/flsl.c", + "string/flsll.c", + "string/gnu_basename.c", + "string/index.c", + "string/memccpy.c", + "string/memchr.c", + "string/memcmp.c", + "string/memcpy.c", + "string/memmem.c", + "string/memmove.c", + "string/mempcpy.c", + "string/memrchr.c", + "string/memset.c", + "string/rawmemchr.c", + "string/rindex.c", + "string/stpcpy.c", + "string/stpncpy.c", + "string/strcasecmp.c", + "string/strcasecmp_l.c", + "string/strcasestr.c", + "string/strcat.c", + "string/strchr.c", + "string/strchrnul.c", + "string/strcmp.c", + "string/strcoll.c", + "string/strcoll_l.c", + "string/strcpy.c", + "string/strcspn.c", + "string/strdup.c", + "string/strerror.c", + "string/strerror_r.c", + "string/strlcat.c", + "string/strlcpy.c", + "string/strlen.c", + "string/strlwr.c", + "string/strncasecmp.c", + "string/strncasecmp_l.c", + "string/strncat.c", + "string/strncmp.c", + "string/strncpy.c", + "string/strndup.c", + "string/strnlen.c", + "string/strnstr.c", + "string/strpbrk.c", + "string/strrchr.c", + "string/strsep.c", + "string/strsignal.c", + "string/strspn.c", + "string/strstr.c", + "string/strtok.c", + "string/strtok_r.c", + "string/strupr.c", + "string/strverscmp.c", + "string/strxfrm.c", + "string/strxfrm_l.c", + "string/swab.c", + "string/timingsafe_bcmp.c", + "string/timingsafe_memcmp.c", + "string/u_strerr.c", + "string/wcpcpy.c", + "string/wcpncpy.c", + "string/wcscasecmp.c", + "string/wcscasecmp_l.c", + "string/wcscat.c", + "string/wcschr.c", + "string/wcscmp.c", + "string/wcscoll.c", + "string/wcscoll_l.c", + "string/wcscpy.c", + "string/wcscspn.c", + "string/wcsdup.c", + "string/wcslcat.c", + "string/wcslcpy.c", + "string/wcslen.c", + "string/wcsncasecmp.c", + "string/wcsncasecmp_l.c", + "string/wcsncat.c", + "string/wcsncmp.c", + "string/wcsncpy.c", + "string/wcsnlen.c", + "string/wcspbrk.c", + "string/wcsrchr.c", + "string/wcsspn.c", + "string/wcsstr.c", + "string/wcstok.c", + "string/wcswidth.c", + "string/wcsxfrm.c", + "string/wcsxfrm_l.c", + "string/wcwidth.c", + "string/wmemchr.c", + "string/wmemcmp.c", + "string/wmemcpy.c", + "string/wmemmove.c", + "string/wmempcpy.c", + "string/wmemset.c", + "string/xpg_strerror_r.c", +} diff --git a/cgo/cgo.go b/cgo/cgo.go index 58aaed81ca..d6470a5229 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -170,6 +170,11 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string enums: map[string]enumInfo{}, } + // Disable _FORTIFY_SOURCE as it causes problems on macOS. + // Note that it is only disabled for memcpy (etc) calls made from Go, which + // have better alternatives anyway. + cflags = append(cflags, "-D_FORTIFY_SOURCE=0") + // Add a new location for the following file. generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0) generatedTokenPos.SetLines([]int{0}) diff --git a/compileopts/config.go b/compileopts/config.go index d3f4bd1d00..e306790800 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -5,6 +5,7 @@ package compileopts import ( "errors" "fmt" + "path/filepath" "regexp" "strconv" "strings" @@ -163,6 +164,11 @@ func (c *Config) CFlags() []string { for _, flag := range c.Target.CFlags { cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1)) } + if c.Target.Libc == "picolibc" { + root := goenv.Get("TINYGOROOT") + cflags = append(cflags, "--sysroot="+filepath.Join(root, "lib", "picolibc", "newlib", "libc")) + cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include")) + } return cflags } diff --git a/compileopts/target.go b/compileopts/target.go index 637712c830..2071c73a53 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -32,6 +32,7 @@ type TargetSpec struct { Compiler string `json:"compiler"` Linker string `json:"linker"` RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt) + Libc string `json:"libc"` CFlags []string `json:"cflags"` LDFlags []string `json:"ldflags"` LinkerScript string `json:"linkerscript"` @@ -84,6 +85,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) { if spec2.RTLib != "" { spec.RTLib = spec2.RTLib } + if spec2.Libc != "" { + spec.Libc = spec2.Libc + } spec.CFlags = append(spec.CFlags, spec2.CFlags...) spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...) if spec2.LinkerScript != "" { diff --git a/lib/picolibc b/lib/picolibc new file mode 160000 index 0000000000..80528c684b --- /dev/null +++ b/lib/picolibc @@ -0,0 +1 @@ +Subproject commit 80528c684b10aaee977397e7eb40c4784e6dc433 diff --git a/lib/picolibc-include/picolibc.h b/lib/picolibc-include/picolibc.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/main.go b/main.go index c21b1377bf..d4361d13b6 100644 --- a/main.go +++ b/main.go @@ -786,7 +786,7 @@ func main() { } err := Build(pkgName, *outpath, options) handleCompilerError(err) - case "build-builtins": + case "build-library": // Note: this command is only meant to be used while making a release! if *outpath == "" { fmt.Fprintln(os.Stderr, "No output filename supplied (-o).") @@ -796,7 +796,22 @@ func main() { if *target == "" { fmt.Fprintln(os.Stderr, "No target (-target).") } - path, err := builder.CompilerRT.Load(*target) + if flag.NArg() != 1 { + fmt.Fprintf(os.Stderr, "Build-library only accepts exactly one library name as argument, %d given\n", flag.NArg()) + usage() + os.Exit(1) + } + var lib *builder.Library + switch name := flag.Arg(0); name { + case "compiler-rt": + lib = &builder.CompilerRT + case "picolibc": + lib = &builder.Picolibc + default: + fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name) + os.Exit(1) + } + path, err := lib.Load(*target) handleCompilerError(err) copyFile(path, *outpath) case "flash", "gdb": diff --git a/src/runtime/runtime_arm7tdmi.go b/src/runtime/runtime_arm7tdmi.go index d7629ad6da..b292f64684 100644 --- a/src/runtime/runtime_arm7tdmi.go +++ b/src/runtime/runtime_arm7tdmi.go @@ -76,17 +76,3 @@ func abort() { for { } } - -// Implement memset for LLVM and compiler-rt. -//go:export memset -func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) { - for i := uintptr(0); i < size; i++ { - *(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c - } -} - -// Implement memmove for LLVM and compiler-rt. -//go:export memmove -func libc_memmove(dst, src unsafe.Pointer, size uintptr) { - memmove(dst, src, size) -} diff --git a/src/runtime/runtime_cortexm.go b/src/runtime/runtime_cortexm.go index 04b34039ae..46f836770f 100644 --- a/src/runtime/runtime_cortexm.go +++ b/src/runtime/runtime_cortexm.go @@ -95,23 +95,3 @@ func handleHardFault(sp *interruptStack) { println() abort() } - -// Implement memset for LLVM and compiler-rt. -//go:export memset -func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) { - for i := uintptr(0); i < size; i++ { - *(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c - } -} - -// Implement memmove for LLVM and compiler-rt. -//go:export memmove -func libc_memmove(dst, src unsafe.Pointer, size uintptr) { - memmove(dst, src, size) -} - -// Implement memcpy for LLVM and compiler-rt. -//go:export memcpy -func libc_memcpy(dst, src unsafe.Pointer, size uintptr) { - memcpy(dst, src, size) -} diff --git a/src/runtime/runtime_tinygoriscv.go b/src/runtime/runtime_tinygoriscv.go deleted file mode 100644 index 691680b44e..0000000000 --- a/src/runtime/runtime_tinygoriscv.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build tinygo.riscv - -package runtime - -import "unsafe" - -// Implement memset for LLVM. -//go:export memset -func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) { - for i := uintptr(0); i < size; i++ { - *(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c - } -} - -// Implement memmove for LLVM. -//go:export memmove -func libc_memmove(dst, src unsafe.Pointer, size uintptr) { - memmove(dst, src, size) -} diff --git a/targets/cortex-m.json b/targets/cortex-m.json index 513a84634d..979c09d1fb 100644 --- a/targets/cortex-m.json +++ b/targets/cortex-m.json @@ -7,13 +7,12 @@ "scheduler": "tasks", "linker": "ld.lld", "rtlib": "compiler-rt", + "libc": "picolibc", "cflags": [ "-Oz", "-mthumb", "-Werror", "-fshort-enums", - "-nostdlibinc", - "-Wno-macro-redefined", "-fno-exceptions", "-fno-unwind-tables", "-ffunction-sections", "-fdata-sections" ], diff --git a/targets/gameboy-advance.json b/targets/gameboy-advance.json index 2ef4f8d1f5..f7ac76ebba 100644 --- a/targets/gameboy-advance.json +++ b/targets/gameboy-advance.json @@ -7,6 +7,7 @@ "compiler": "clang", "linker": "ld.lld", "rtlib": "compiler-rt", + "libc": "picolibc", "cflags": [ "-g", "--target=thumb4-none-eabi", @@ -14,7 +15,6 @@ "-Oz", "-Werror", "-fshort-enums", - "-Wno-macro-redefined", "-Qunused-arguments", "-fno-exceptions", "-fno-unwind-tables", "-ffunction-sections", "-fdata-sections" diff --git a/targets/riscv.json b/targets/riscv.json index f96354008d..f74c1767d6 100644 --- a/targets/riscv.json +++ b/targets/riscv.json @@ -7,13 +7,13 @@ "compiler": "clang", "linker": "ld.lld", "rtlib": "compiler-rt", + "libc": "picolibc", "cflags": [ "--target=riscv32--none", "-march=rv32imac", "-mabi=ilp32", "-Os", "-Werror", - "-nostdinc", "-fno-exceptions", "-fno-unwind-tables", "-ffunction-sections", "-fdata-sections" ], diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index 41bc6892cb..557dea5cf7 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -4,6 +4,7 @@ package main int fortytwo(void); #include "main.h" int mul(int, int); +#include */ import "C" @@ -109,6 +110,12 @@ func main() { var _ C.option3_t = C.option3A println("option 2A:", C.option2A) println("option 3A:", C.option3A) + + // libc: test whether C functions work at all. + buf1 := []byte("foobar\x00") + buf2 := make([]byte, len(buf1)) + C.strcpy((*C.char)(unsafe.Pointer(&buf2[0])), (*C.char)(unsafe.Pointer(&buf1[0]))) + println("copied string:", string(buf2[:C.strlen((*C.char)(unsafe.Pointer(&buf2[0])))])) } func printUnion(union C.joined_t) C.joined_t { diff --git a/testdata/cgo/out.txt b/testdata/cgo/out.txt index 73cb2b9904..fcb688467a 100644 --- a/testdata/cgo/out.txt +++ b/testdata/cgo/out.txt @@ -55,3 +55,4 @@ option F: 11 option G: 12 option 2A: 20 option 3A: 21 +copied string: foobar