Skip to content
Permalink
Browse files

all: use compiler-rt for builtins

  • Loading branch information...
aykevl committed Oct 7, 2018
1 parent e8f2119 commit 22da104530a6b2166df6ef8ba1b167d214376d7c
Showing with 347 additions and 10 deletions.
  1. +4 −0 .gitmodules
  2. +2 −2 .travis.yml
  3. +108 −0 buildcache.go
  4. +186 −0 builtins.go
  5. +2 −0 docs/installation.rst
  6. +1 −0 lib/compiler-rt
  7. +27 −0 main.go
  8. +13 −8 target.go
  9. +1 −0 targets/bluepill.json
  10. +2 −0 targets/microbit.json
  11. +1 −0 targets/pca10040.json
@@ -10,3 +10,7 @@
[submodule "lib/cmsis-svd"]
path = lib/cmsis-svd
url = https://github.com/posborne/cmsis-svd
[submodule "lib/compiler-rt"]
path = lib/compiler-rt
url = https://github.com/llvm-mirror/compiler-rt.git
branch = release_70
@@ -8,8 +8,8 @@ before_install:
- echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-7 main" | sudo tee -a /etc/apt/sources.list
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" | sudo tee -a /etc/apt/sources.list
- sudo apt-get update -qq
- sudo apt-get install llvm-7-dev gcc-8 gcc-arm-none-eabi --allow-unauthenticated -y
- sudo ln -s /usr/bin/gcc-8 /usr/local/bin/cc # work around missing -no-pie in old GCC version
- sudo apt-get install llvm-7-dev clang-7 gcc-arm-none-eabi --allow-unauthenticated -y
- sudo ln -s /usr/bin/clang-7 /usr/local/bin/cc # work around missing -no-pie in old GCC version

install:
- go get github.com/aykevl/go-llvm
@@ -0,0 +1,108 @@
package main

import (
"io"
"os"
"path/filepath"
"time"
)

// Get the cache directory, usually ~/.cache/tinygo
func cacheDir() string {
home := getHomeDir()
dir := filepath.Join(home, ".cache", "tinygo")
return dir
}

// Return the newest timestamp of all the file paths passed in. Used to check
// for stale caches.
func cacheTimestamp(paths []string) (time.Time, error) {
var timestamp time.Time
for _, path := range paths {
st, err := os.Stat(path)
if err != nil {
return time.Time{}, err
}
if timestamp.IsZero() {
timestamp = st.ModTime()
} else if timestamp.Before(st.ModTime()) {
timestamp = st.ModTime()
}
}
return timestamp, nil
}

// Try to load a given file from the cache. Return "", nil if no cached file can
// be found (or the file is stale), return the absolute path if there is a cache
// and return an error on I/O errors.
//
// TODO: the configKey is currently ignored. It is supposed to be used as extra
// data for the cache key, like the compiler version and arguments.
func cacheLoad(name, configKey string, sourceFiles []string) (string, error) {
dir := cacheDir()
cachepath := filepath.Join(dir, name)
cacheStat, err := os.Stat(cachepath)
if os.IsNotExist(err) {
return "", nil // does not exist
} else if err != nil {
return "", err // cannot stat cache file
}

sourceTimestamp, err := cacheTimestamp(sourceFiles)
if err != nil {
return "", err // cannot stat source files
}

if cacheStat.ModTime().After(sourceTimestamp) {
return cachepath, nil
} else {
os.Remove(cachepath)
// stale cache
return "", nil
}
}

// Store the file located at tmppath in the cache with the given name. The
// tmppath may or may not be gone afterwards.
//
// Note: the configKey is ignored, see cacheLoad.
func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string, error) {
// get the last modified time
if len(sourceFiles) == 0 {
panic("cache: no source files")
}

// TODO: check the config key

dir := cacheDir()
err := os.MkdirAll(dir, 0777)
if err != nil {
return "", err
}
cachepath := filepath.Join(dir, name)
err = os.Rename(tmppath, cachepath)
if err != nil {
inf, err := os.Open(tmppath)
if err != nil {
return "", err
}
defer inf.Close()
outf, err := os.Create(cachepath + ".tmp")
if err != nil {
return "", err
}

_, err = io.Copy(outf, inf)
if err != nil {
return "", err
}

err = os.Rename(cachepath+".tmp", cachepath)
if err != nil {
return "", err
}

return cachepath, outf.Close()
}
return cachepath, nil
}
@@ -0,0 +1,186 @@
package main

import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
)

// These are the GENERIC_SOURCES according to CMakeList.txt.
var genericBuiltins = []string{
"absvdi2",
"absvsi2",
"absvti2",
"adddf3",
"addsf3",
"addtf3",
"addvdi3",
"addvsi3",
"addvti3",
"apple_versioning",
"ashldi3",
"ashlti3",
"ashrdi3",
"ashrti3",
"bswapdi2",
"bswapsi2",
"clzdi2",
"clzsi2",
"clzti2",
"cmpdi2",
"cmpti2",
"comparedf2",
"comparesf2",
"ctzdi2",
"ctzsi2",
"ctzti2",
"divdc3",
"divdf3",
"divdi3",
"divmoddi4",
"divmodsi4",
"divsc3",
"divsf3",
"divsi3",
"divtc3",
"divti3",
"divtf3",
"extendsfdf2",
"extendhfsf2",
"ffsdi2",
"ffssi2",
"ffsti2",
"fixdfdi",
"fixdfsi",
"fixdfti",
"fixsfdi",
"fixsfsi",
"fixsfti",
"fixunsdfdi",
"fixunsdfsi",
"fixunsdfti",
"fixunssfdi",
"fixunssfsi",
"fixunssfti",
"floatdidf",
"floatdisf",
"floatsidf",
"floatsisf",
"floattidf",
"floattisf",
"floatundidf",
"floatundisf",
"floatunsidf",
"floatunsisf",
"floatuntidf",
"floatuntisf",
//"int_util",
"lshrdi3",
"lshrti3",
"moddi3",
"modsi3",
"modti3",
"muldc3",
"muldf3",
"muldi3",
"mulodi4",
"mulosi4",
"muloti4",
"mulsc3",
"mulsf3",
"multi3",
"multf3",
"mulvdi3",
"mulvsi3",
"mulvti3",
"negdf2",
"negdi2",
"negsf2",
"negti2",
"negvdi2",
"negvsi2",
"negvti2",
"os_version_check",
"paritydi2",
"paritysi2",
"parityti2",
"popcountdi2",
"popcountsi2",
"popcountti2",
"powidf2",
"powisf2",
"powitf2",
"subdf3",
"subsf3",
"subvdi3",
"subvsi3",
"subvti3",
"subtf3",
"trampoline_setup",
"truncdfhf2",
"truncdfsf2",
"truncsfhf2",
"ucmpdi2",
"ucmpti2",
"udivdi3",
"udivmoddi4",
"udivmodsi4",
"udivmodti4",
"udivsi3",
"udivti3",
"umoddi3",
"umodsi3",
"umodti3",
}

// Get the builtins archive, possibly generating it as needed.
func loadBuiltins(target string) (path string, err error) {
outfile := "librt-" + target + ".a"
builtinsDir := filepath.Join(sourceDir(), "lib", "compiler-rt", "lib", "builtins")

srcs := make([]string, len(genericBuiltins))
for i, name := range genericBuiltins {
srcs[i] = filepath.Join(builtinsDir, name+".c")
}

if path, err := cacheLoad(outfile, commands["clang"], srcs); path != "" || err != nil {
return path, err
}

dir, err := ioutil.TempDir("", "tinygo-builtins")
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(genericBuiltins))
for _, name := range genericBuiltins {
objpath := filepath.Join(dir, name+".o")
objs = append(objs, objpath)
srcpath := filepath.Join(builtinsDir, name+".c")
cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "--target="+target, "-o", objpath, srcpath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = dir
err = cmd.Run()
if err != nil {
return "", err
}
}

// Put all builtins in an archive to link as a static library.
arpath := filepath.Join(dir, "librt.a")
cmd := exec.Command(commands["ar"], append([]string{"cr", arpath}, objs...)...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = dir
err = cmd.Run()
if err != nil {
return "", err
}

return cacheStore(arpath, outfile, commands["clang"], srcs)
}
@@ -27,6 +27,8 @@ final binary and flashing it needs some extra tools.
* binutils (``arm-none-eabi-objcopy``) for producing .hex files for
flashing.
* GCC (``arm-none-eabi-gcc``) for linking object files.
* Clang 7 (``clang-7``) for building the `compiler runtime library
<https://compiler-rt.llvm.org/>`_.
* The flashing tool for the particular chip, like ``openocd`` or
``nrfjprog``.

Submodule compiler-rt added at a4cbb0
27 main.go
@@ -17,6 +17,11 @@ import (
"github.com/aykevl/tinygo/compiler"
)

var commands = map[string]string{
"ar": "ar",
"clang": "clang-7",
}

// Helper function for Compiler object.
func Compile(pkgName, outpath string, spec *TargetSpec, printIR, dumpSSA, debug bool, printSizes string, action func(string) error) error {
config := compiler.Config{
@@ -95,10 +100,24 @@ func Compile(pkgName, outpath string, spec *TargetSpec, printIR, dumpSSA, debug
return err
}

// Load builtins library from the cache, possibly compiling it on the
// fly.
var cachePath string
if spec.CompilerRT {
librt, err := loadBuiltins(spec.Triple)
if err != nil {
return err
}
cachePath, _ = filepath.Split(librt)
}

// Link the object file with the system compiler.
executable := filepath.Join(dir, "main")
tmppath := executable // final file
args := append(spec.PreLinkArgs, "-o", executable, objfile)
if spec.CompilerRT {
args = append(args, "-L", cachePath, "-lrt-"+spec.Triple)
}
cmd := exec.Command(spec.Linker, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -371,6 +390,14 @@ func main() {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
case "clean":
// remove cache directory
dir := cacheDir()
err := os.RemoveAll(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "cannot clean cache:", err)
os.Exit(1)
}
case "help":
usage()
case "run":
Oops, something went wrong.

0 comments on commit 22da104

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.