mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 07:23:39 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13c0714fda | |||
| f880950c3e | |||
| 1bed192de0 | |||
| 896a848001 | |||
| fb03787b73 | |||
| 83a949647f | |||
| 99a41bec4e | |||
| 312f5d3833 | |||
| 35bf0746a1 | |||
| 8d93b9e545 | |||
| b44d41d9ec | |||
| 4be9802d26 |
@@ -172,7 +172,11 @@ commands:
|
|||||||
key: llvm-build-11-linux-v2-assert
|
key: llvm-build-11-linux-v2-assert
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run: make ASSERT=1
|
- run: |
|
||||||
|
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
||||||
|
# necessary to keep memory consumption down and avoid OOM (for a
|
||||||
|
# 2CPU/4GB executor).
|
||||||
|
GOFLAGS="-p=2" make ASSERT=1
|
||||||
- build-wasi-libc
|
- build-wasi-libc
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ tinygo:
|
|||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||||
|
|
||||||
test: wasi-libc
|
test: wasi-libc
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||||
|
|||||||
+66
-46
@@ -17,7 +17,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -55,6 +54,7 @@ type BuildResult struct {
|
|||||||
type packageAction struct {
|
type packageAction struct {
|
||||||
ImportPath string
|
ImportPath string
|
||||||
CompilerVersion int // compiler.Version
|
CompilerVersion int // compiler.Version
|
||||||
|
InterpVersion int // interp.Version
|
||||||
LLVMVersion string
|
LLVMVersion string
|
||||||
Config *compiler.Config
|
Config *compiler.Config
|
||||||
CFlags []string
|
CFlags []string
|
||||||
@@ -135,6 +135,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
actionID := packageAction{
|
actionID := packageAction{
|
||||||
ImportPath: pkg.ImportPath,
|
ImportPath: pkg.ImportPath,
|
||||||
CompilerVersion: compiler.Version,
|
CompilerVersion: compiler.Version,
|
||||||
|
InterpVersion: interp.Version,
|
||||||
LLVMVersion: llvm.Version,
|
LLVMVersion: llvm.Version,
|
||||||
Config: compilerConfig,
|
Config: compilerConfig,
|
||||||
CFlags: pkg.CFlags,
|
CFlags: pkg.CFlags,
|
||||||
@@ -179,7 +180,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// The package has not yet been compiled, so create a job to do so.
|
// The package has not yet been compiled, so create a job to do so.
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile package " + pkg.ImportPath,
|
description: "compile package " + pkg.ImportPath,
|
||||||
run: func() error {
|
run: func(*compileJob) error {
|
||||||
// Compile AST to IR. The compiler.CompilePackage function will
|
// Compile AST to IR. The compiler.CompilePackage function will
|
||||||
// build the SSA as needed.
|
// build the SSA as needed.
|
||||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
@@ -190,6 +191,21 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to interpret package initializers at compile time.
|
||||||
|
// It may only be possible to do this partially, in which case
|
||||||
|
// it is completed after all IR files are linked.
|
||||||
|
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||||
|
if pkgInit.IsNil() {
|
||||||
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
|
}
|
||||||
|
err := interp.RunFunc(pkgInit, config.DumpSSA())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize the LLVM module as a bitcode file.
|
// Serialize the LLVM module as a bitcode file.
|
||||||
// Write to a temporary path that is renamed to the destination
|
// Write to a temporary path that is renamed to the destination
|
||||||
// file to avoid race conditions with other TinyGo invocatiosn
|
// file to avoid race conditions with other TinyGo invocatiosn
|
||||||
@@ -233,7 +249,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
programJob := &compileJob{
|
programJob := &compileJob{
|
||||||
description: "link+optimize packages (LTO)",
|
description: "link+optimize packages (LTO)",
|
||||||
dependencies: packageJobs,
|
dependencies: packageJobs,
|
||||||
run: func() error {
|
run: func(*compileJob) error {
|
||||||
// Load and link all the bitcode files. This does not yet optimize
|
// Load and link all the bitcode files. This does not yet optimize
|
||||||
// anything, it only links the bitcode files together.
|
// anything, it only links the bitcode files together.
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
@@ -353,7 +369,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
outputObjectFileJob := &compileJob{
|
outputObjectFileJob := &compileJob{
|
||||||
description: "generate output file",
|
description: "generate output file",
|
||||||
dependencies: []*compileJob{programJob},
|
dependencies: []*compileJob{programJob},
|
||||||
run: func() error {
|
result: objfile,
|
||||||
|
run: func(*compileJob) error {
|
||||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -367,40 +384,32 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
tmppath := executable // final file
|
tmppath := executable // final file
|
||||||
ldflags := append(config.LDFlags(), "-o", executable, objfile)
|
ldflags := append(config.LDFlags(), "-o", executable)
|
||||||
|
|
||||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||||
// a cache.
|
// a cache.
|
||||||
if config.Target.RTLib == "compiler-rt" {
|
if config.Target.RTLib == "compiler-rt" {
|
||||||
path, job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
|
job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if job != nil {
|
jobs = append(jobs, job.dependencies...)
|
||||||
// The library was not loaded from cache so needs to be compiled
|
jobs = append(jobs, job)
|
||||||
// (and then stored in the cache).
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
jobs = append(jobs, job.dependencies...)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
}
|
|
||||||
ldflags = append(ldflags, path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add libc dependency if needed.
|
// Add libc dependency if needed.
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
switch config.Target.Libc {
|
switch config.Target.Libc {
|
||||||
case "picolibc":
|
case "picolibc":
|
||||||
path, job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if job != nil {
|
// The library needs to be compiled (cache miss).
|
||||||
// The library needs to be compiled (cache miss).
|
jobs = append(jobs, job.dependencies...)
|
||||||
jobs = append(jobs, job.dependencies...)
|
jobs = append(jobs, job)
|
||||||
jobs = append(jobs, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
}
|
|
||||||
ldflags = append(ldflags, path)
|
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
@@ -416,44 +425,37 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Add jobs to compile extra files. These files are in C or assembly and
|
// 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
|
// contain things like the interrupt vector table and low level operations
|
||||||
// such as stack switching.
|
// such as stack switching.
|
||||||
for i, path := range config.ExtraFiles() {
|
for _, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile extra file " + path,
|
description: "compile extra file " + path,
|
||||||
run: func() error {
|
run: func(job *compileJob) error {
|
||||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
|
||||||
if err != nil {
|
job.result = result
|
||||||
return &commandError{"failed to build", path, err}
|
return err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
ldflags = append(ldflags, outpath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add jobs to compile C files in all packages. This is part of CGo.
|
// Add jobs to compile C files in all packages. This is part of CGo.
|
||||||
// TODO: do this as part of building the package to be able to link the
|
// TODO: do this as part of building the package to be able to link the
|
||||||
// bitcode files together.
|
// bitcode files together.
|
||||||
for i, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
for j, filename := range pkg.CFiles {
|
pkg := pkg
|
||||||
file := filepath.Join(pkg.Dir, filename)
|
for _, filename := range pkg.CFiles {
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
|
abspath := filepath.Join(pkg.Dir, filename)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile CGo file " + file,
|
description: "compile CGo file " + abspath,
|
||||||
run: func() error {
|
run: func(job *compileJob) error {
|
||||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
|
||||||
if err != nil {
|
job.result = result
|
||||||
return &commandError{"failed to build", file, err}
|
return err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
ldflags = append(ldflags, outpath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,7 +470,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
jobs = append(jobs, &compileJob{
|
jobs = append(jobs, &compileJob{
|
||||||
description: "link",
|
description: "link",
|
||||||
dependencies: linkerDependencies,
|
dependencies: linkerDependencies,
|
||||||
run: func() error {
|
run: func(job *compileJob) error {
|
||||||
|
for _, dependency := range job.dependencies {
|
||||||
|
if dependency.result == "" {
|
||||||
|
return errors.New("dependency without result: " + dependency.description)
|
||||||
|
}
|
||||||
|
ldflags = append(ldflags, dependency.result)
|
||||||
|
}
|
||||||
|
if config.Options.PrintCommands {
|
||||||
|
fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " "))
|
||||||
|
}
|
||||||
err = link(config.Target.Linker, ldflags...)
|
err = link(config.Target.Linker, ldflags...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to link", executable, err}
|
return &commandError{"failed to link", executable, err}
|
||||||
@@ -575,8 +586,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
if config.VerifyIR() {
|
||||||
return errors.New("verification error after interpreting runtime.initAll")
|
// Only verify if we really need it.
|
||||||
|
// The IR has already been verified before writing the bitcode to disk
|
||||||
|
// and the interp function above doesn't need to do a lot as most of the
|
||||||
|
// package initializers have already run. Additionally, verifying this
|
||||||
|
// linked IR is _expensive_ because dead code hasn't been removed yet,
|
||||||
|
// easily costing a few hundred milliseconds. Therefore, only do it when
|
||||||
|
// specifically requested.
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return errors.New("verification error after interpreting runtime.initAll")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.GOOS() != "darwin" {
|
if config.GOOS() != "darwin" {
|
||||||
|
|||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
// This file implements a wrapper around the C compiler (Clang) which uses a
|
||||||
|
// build cache.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// compileAndCacheCFile compiles a C or assembly file using a build cache.
|
||||||
|
// Compiling the same file again (if nothing changed, including included header
|
||||||
|
// files) the output is loaded from the build cache instead.
|
||||||
|
//
|
||||||
|
// Its operation is a bit complex (more complex than Go package build caching)
|
||||||
|
// because the list of file dependencies is only known after the file is
|
||||||
|
// compiled. However, luckily compilers have a flag to write a list of file
|
||||||
|
// dependencies in Makefile syntax which can be used for caching.
|
||||||
|
//
|
||||||
|
// Because of this complexity, every file has in fact two cached build outputs:
|
||||||
|
// the file itself, and the list of dependencies. Its operation is as follows:
|
||||||
|
//
|
||||||
|
// depfile = hash(path, compiler, cflags, ...)
|
||||||
|
// if depfile exists:
|
||||||
|
// outfile = hash of all files and depfile name
|
||||||
|
// if outfile exists:
|
||||||
|
// # cache hit
|
||||||
|
// return outfile
|
||||||
|
// # cache miss
|
||||||
|
// tmpfile = compile file
|
||||||
|
// read dependencies (side effect of compile)
|
||||||
|
// write depfile
|
||||||
|
// outfile = hash of all files and depfile name
|
||||||
|
// rename tmpfile to outfile
|
||||||
|
//
|
||||||
|
// There are a few edge cases that are not handled:
|
||||||
|
// - If a file is added to an include path, that file may be included instead of
|
||||||
|
// some other file. This would be fixed by also including lookup failures in the
|
||||||
|
// dependencies file, but I'm not aware of a compiler which does that.
|
||||||
|
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||||
|
// details.
|
||||||
|
// - A header file may be changed to add/remove an include. This invalidates the
|
||||||
|
// depfile but without invalidating its name. For this reason, the depfile is
|
||||||
|
// written on each new compilation (even when it seems unnecessary). However, it
|
||||||
|
// could in rare cases lead to a stale file fetched from the cache.
|
||||||
|
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
|
||||||
|
// Hash input file.
|
||||||
|
fileHash, err := hashFile(abspath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create cache key for the dependencies file.
|
||||||
|
buf, err := json.Marshal(struct {
|
||||||
|
Path string
|
||||||
|
Hash string
|
||||||
|
Compiler string
|
||||||
|
Flags []string
|
||||||
|
LLVMVersion string
|
||||||
|
}{
|
||||||
|
Path: abspath,
|
||||||
|
Hash: fileHash,
|
||||||
|
Compiler: config.Target.Compiler,
|
||||||
|
Flags: config.CFlags(),
|
||||||
|
LLVMVersion: llvm.Version,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
depfileNameHashBuf := sha512.Sum512_224(buf)
|
||||||
|
depfileNameHash := hex.EncodeToString(depfileNameHashBuf[:])
|
||||||
|
|
||||||
|
// Load dependencies file, if possible.
|
||||||
|
depfileName := "dep-" + depfileNameHash + ".json"
|
||||||
|
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
|
||||||
|
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
|
||||||
|
var dependencies []string // sorted list of dependency paths
|
||||||
|
if err == nil {
|
||||||
|
// There is a dependency file, that's great!
|
||||||
|
// Parse it first.
|
||||||
|
err := json.Unmarshal(depfileBuf, &dependencies)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse dependencies JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain hashes of all the files listed as a dependency.
|
||||||
|
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
|
||||||
|
if err == nil {
|
||||||
|
if _, err := os.Stat(outpath); err == nil {
|
||||||
|
return outpath, nil
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
// expected either nil or IsNotExist
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
objTmpFile.Close()
|
||||||
|
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
depTmpFile.Close()
|
||||||
|
flags := append([]string{}, cflags...) // copy cflags
|
||||||
|
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||||
|
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||||
|
if config.Options.PrintCommands {
|
||||||
|
fmt.Printf("%s %s\n", config.Target.Compiler, strings.Join(flags, " "))
|
||||||
|
}
|
||||||
|
err = runCCompiler(config.Target.Compiler, flags...)
|
||||||
|
if err != nil {
|
||||||
|
return "", &commandError{"failed to build", abspath, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create sorted and uniqued slice of dependencies.
|
||||||
|
dependencyPaths, err := readDepFile(depTmpFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dependencyPaths = append(dependencyPaths, abspath) // necessary for .s files
|
||||||
|
dependencySet := make(map[string]struct{}, len(dependencyPaths))
|
||||||
|
var dependencySlice []string
|
||||||
|
for _, path := range dependencyPaths {
|
||||||
|
if _, ok := dependencySet[path]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dependencySet[path] = struct{}{}
|
||||||
|
dependencySlice = append(dependencySlice, path)
|
||||||
|
}
|
||||||
|
sort.Strings(dependencySlice)
|
||||||
|
|
||||||
|
// Write dependencies file.
|
||||||
|
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||||
|
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
_, err = f.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = os.Rename(f.Name(), depfileCachePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move temporary object file to final location.
|
||||||
|
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = os.Rename(objTmpFile.Name(), outpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outpath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a cache path (a path in GOCACHE) to store the output of a compiler
|
||||||
|
// job. This path is based on the dep file name (which is a hash of metadata
|
||||||
|
// including compiler flags) and the hash of all input files in the paths slice.
|
||||||
|
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
|
||||||
|
// Hash all input files.
|
||||||
|
fileHashes := make(map[string]string, len(paths))
|
||||||
|
for _, path := range paths {
|
||||||
|
hash, err := hashFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fileHashes[path] = hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate a cache key based on the above hashes.
|
||||||
|
buf, err := json.Marshal(struct {
|
||||||
|
DepfileHash string
|
||||||
|
FileHashes map[string]string
|
||||||
|
}{
|
||||||
|
DepfileHash: depfileNameHash,
|
||||||
|
FileHashes: fileHashes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
outFileNameBuf := sha512.Sum512_224(buf)
|
||||||
|
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
||||||
|
|
||||||
|
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
|
||||||
|
return outpath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashFile hashes the given file path and returns the hash as a hex string.
|
||||||
|
func hashFile(path string) (string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to hash file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
fileHasher := sha512.New512_224()
|
||||||
|
_, err = io.Copy(fileHasher, f)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to hash file: %w", err)
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(fileHasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readDepFile reads a dependency file in NMake (Visual Studio make) format. The
|
||||||
|
// file is assumed to have a single target named deps.
|
||||||
|
//
|
||||||
|
// There are roughly three make syntax variants:
|
||||||
|
// - BSD make, which doesn't support any escaping. This means that many special
|
||||||
|
// characters are not supported in file names.
|
||||||
|
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||||
|
// find a file it tries to fall back with the literal path name (to match BSD
|
||||||
|
// make).
|
||||||
|
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||||
|
// any weird characters.
|
||||||
|
// Clang supports two variants: a format that's a compromise between BSD and GNU
|
||||||
|
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
|
||||||
|
// is at least somewhat sane. This last format isn't perfect either: it does not
|
||||||
|
// correctly handle filenames with quote marks in them. Those are generally not
|
||||||
|
// allowed on Windows, but of course can be used on POSIX like systems. Still,
|
||||||
|
// it's the most sane of any of the formats so readDepFile will use that format.
|
||||||
|
func readDepFile(filename string) ([]string, error) {
|
||||||
|
buf, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return parseDepFile(string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDepFile(s string) ([]string, error) {
|
||||||
|
// This function makes no attempt at parsing anything other than Clang -MD
|
||||||
|
// -MV output.
|
||||||
|
|
||||||
|
// For Windows: replace CRLF with LF to make the logic below simpler.
|
||||||
|
s = strings.ReplaceAll(s, "\r\n", "\n")
|
||||||
|
|
||||||
|
// Collapse all lines ending in a backslash. These backslashes are really
|
||||||
|
// just a way to continue a line without making very long lines.
|
||||||
|
s = strings.ReplaceAll(s, "\\\n", " ")
|
||||||
|
|
||||||
|
// Only use the first line, which is expected to begin with "deps:".
|
||||||
|
line := strings.SplitN(s, "\n", 2)[0]
|
||||||
|
if !strings.HasPrefix(line, "deps:") {
|
||||||
|
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(line[len("deps:"):])
|
||||||
|
|
||||||
|
var deps []string
|
||||||
|
for line != "" {
|
||||||
|
if line[0] == '"' {
|
||||||
|
// File path is quoted. Path ends with double quote.
|
||||||
|
// This does not handle double quotes in path names, which is a
|
||||||
|
// problem on non-Windows systems.
|
||||||
|
line = line[1:]
|
||||||
|
end := strings.IndexByte(line, '"')
|
||||||
|
if end < 0 {
|
||||||
|
return nil, errors.New("readDepFile: path is incorrectly quoted")
|
||||||
|
}
|
||||||
|
dep := line[:end]
|
||||||
|
line = strings.TrimSpace(line[end+1:])
|
||||||
|
deps = append(deps, dep)
|
||||||
|
} else {
|
||||||
|
// File path is not quoted. Path ends in space or EOL.
|
||||||
|
end := strings.IndexFunc(line, unicode.IsSpace)
|
||||||
|
if end < 0 {
|
||||||
|
// last dependency
|
||||||
|
deps = append(deps, line)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dep := line[:end]
|
||||||
|
line = strings.TrimSpace(line[end:])
|
||||||
|
deps = append(deps, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deps, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSplitDepFile(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
in string
|
||||||
|
out []string
|
||||||
|
}{
|
||||||
|
{`deps: foo bar`, []string{"foo", "bar"}},
|
||||||
|
{`deps: foo "bar"`, []string{"foo", "bar"}},
|
||||||
|
{`deps: "foo" bar`, []string{"foo", "bar"}},
|
||||||
|
{`deps: "foo bar"`, []string{"foo bar"}},
|
||||||
|
{`deps: "foo bar" `, []string{"foo bar"}},
|
||||||
|
{"deps: foo\nbar", []string{"foo"}},
|
||||||
|
{"deps: foo \\\nbar", []string{"foo", "bar"}},
|
||||||
|
{"deps: foo\\bar \\\nbaz", []string{"foo\\bar", "baz"}},
|
||||||
|
{"deps: foo\\bar \\\r\n baz", []string{"foo\\bar", "baz"}}, // Windows uses CRLF line endings
|
||||||
|
} {
|
||||||
|
out, err := parseDepFile(tc.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test #%d failed: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, tc.out) {
|
||||||
|
t.Errorf("test #%d failed: expected %#v but got %#v", i, tc.out, out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-4
@@ -27,12 +27,23 @@ const (
|
|||||||
type compileJob struct {
|
type compileJob struct {
|
||||||
description string // description, only used for logging
|
description string // description, only used for logging
|
||||||
dependencies []*compileJob
|
dependencies []*compileJob
|
||||||
run func() error
|
result string // result (path)
|
||||||
|
run func(*compileJob) (err error)
|
||||||
state jobState
|
state jobState
|
||||||
err error // error if finished
|
err error // error if finished
|
||||||
duration time.Duration // how long it took to run this job (only set after finishing)
|
duration time.Duration // how long it took to run this job (only set after finishing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dummyCompileJob returns a new *compileJob that produces an output without
|
||||||
|
// doing anything. This can be useful where a *compileJob producing an output is
|
||||||
|
// expected but nothing needs to be done, for example for a load from a cache.
|
||||||
|
func dummyCompileJob(result string) *compileJob {
|
||||||
|
return &compileJob{
|
||||||
|
description: "<dummy>",
|
||||||
|
result: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// readyToRun returns whether this job is ready to run: it is itself not yet
|
// readyToRun returns whether this job is ready to run: it is itself not yet
|
||||||
// started and all dependencies are finished.
|
// started and all dependencies are finished.
|
||||||
func (job *compileJob) readyToRun() bool {
|
func (job *compileJob) readyToRun() bool {
|
||||||
@@ -150,9 +161,11 @@ func nextJob(jobs []*compileJob) *compileJob {
|
|||||||
func jobWorker(workerChan, doneChan chan *compileJob) {
|
func jobWorker(workerChan, doneChan chan *compileJob) {
|
||||||
for job := range workerChan {
|
for job := range workerChan {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err := job.run()
|
if job.run != nil {
|
||||||
if err != nil {
|
err := job.run(job)
|
||||||
job.err = err
|
if err != nil {
|
||||||
|
job.err = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
job.duration = time.Since(start)
|
job.duration = time.Since(start)
|
||||||
doneChan <- job
|
doneChan <- job
|
||||||
|
|||||||
+16
-17
@@ -42,30 +42,28 @@ func (l *Library) sourcePaths(target string) []string {
|
|||||||
// The resulting file is stored in the provided tmpdir, which is expected to be
|
// The resulting file is stored in the provided tmpdir, which is expected to be
|
||||||
// removed after the Load call.
|
// removed after the Load call.
|
||||||
func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
||||||
path, job, err := l.load(target, "", tmpdir)
|
job, err := l.load(target, "", tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if job != nil {
|
jobs := append([]*compileJob{job}, job.dependencies...)
|
||||||
jobs := append([]*compileJob{job}, job.dependencies...)
|
err = runJobs(jobs)
|
||||||
err = runJobs(jobs)
|
return job.result, err
|
||||||
}
|
|
||||||
return path, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load returns a path to the library file for the given target, loading it from
|
// load returns a compile job to build this library file for the given target
|
||||||
// cache if possible. It will return a non-zero compiler job if the library
|
// and CPU. It may return a dummy compileJob if the library build is already
|
||||||
// wasn't cached, this job (and its dependencies) must be run before the library
|
// cached. The path is stored as job.result but is only valid if the job and
|
||||||
// path is valid.
|
// job.dependencies have been run.
|
||||||
// The provided tmpdir will be used to store intermediary files and possibly the
|
// The provided tmpdir will be used to store intermediary files and possibly the
|
||||||
// output archive file, it is expected to be removed after use.
|
// output archive file, it is expected to be removed after use.
|
||||||
func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob, err error) {
|
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
|
||||||
// Try to load a precompiled library.
|
// Try to load a precompiled library.
|
||||||
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
||||||
if _, err := os.Stat(precompiledPath); err == nil {
|
if _, err := os.Stat(precompiledPath); err == nil {
|
||||||
// Found a precompiled library for this OS/architecture. Return the path
|
// Found a precompiled library for this OS/architecture. Return the path
|
||||||
// directly.
|
// directly.
|
||||||
return precompiledPath, nil, nil
|
return dummyCompileJob(precompiledPath), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var outfile string
|
var outfile string
|
||||||
@@ -78,7 +76,7 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
// Try to fetch this library from the cache.
|
// Try to fetch this library from the cache.
|
||||||
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
|
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
|
||||||
// Cache hit.
|
// Cache hit.
|
||||||
return path, nil, err
|
return dummyCompileJob(path), nil
|
||||||
}
|
}
|
||||||
// Cache miss, build it now.
|
// Cache miss, build it now.
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
||||||
err = os.Mkdir(dir, 0777)
|
err = os.Mkdir(dir, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precalculate the flags to the compiler invocation.
|
// Precalculate the flags to the compiler invocation.
|
||||||
@@ -113,7 +111,8 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
arpath := filepath.Join(dir, l.name+".a")
|
arpath := filepath.Join(dir, l.name+".a")
|
||||||
job = &compileJob{
|
job = &compileJob{
|
||||||
description: "ar " + l.name + ".a",
|
description: "ar " + l.name + ".a",
|
||||||
run: func() error {
|
result: arpath,
|
||||||
|
run: func(*compileJob) error {
|
||||||
// Create an archive of all object files.
|
// Create an archive of all object files.
|
||||||
err := makeArchive(arpath, objs)
|
err := makeArchive(arpath, objs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -133,7 +132,7 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
objs = append(objs, objpath)
|
objs = append(objs, objpath)
|
||||||
job.dependencies = append(job.dependencies, &compileJob{
|
job.dependencies = append(job.dependencies, &compileJob{
|
||||||
description: "compile " + srcpath,
|
description: "compile " + srcpath,
|
||||||
run: func() error {
|
run: func(*compileJob) error {
|
||||||
var compileArgs []string
|
var compileArgs []string
|
||||||
compileArgs = append(compileArgs, args...)
|
compileArgs = append(compileArgs, args...)
|
||||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||||
@@ -146,5 +145,5 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return arpath, job, nil
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-14
@@ -41,7 +41,8 @@ type cgoPackage struct {
|
|||||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||||
enums map[string]enumInfo
|
enums map[string]enumInfo
|
||||||
anonStructNum int
|
anonStructNum int
|
||||||
ldflags []string
|
cflags []string // CFlags from #cgo lines
|
||||||
|
ldflags []string // LDFlags from #cgo lines
|
||||||
visitedFiles map[string][]byte
|
visitedFiles map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,10 +158,10 @@ typedef unsigned long long _Cgo_ulonglong;
|
|||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
// with libclang, and modifies the AST to use this information. It returns a
|
// with libclang, and modifies the AST to use this information. It returns a
|
||||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
// newly created *ast.File that should be added to the list of to-be-parsed
|
||||||
// files, the LDFLAGS for this package, and a map of file hashes of the accessed
|
// files, the CFLAGS and LDFLAGS found in #cgo lines, and a map of file hashes
|
||||||
// C header files. If there is one or more error, it returns these in the
|
// of the accessed C header files. If there is one or more error, it returns
|
||||||
// []error slice but still modifies the AST.
|
// these in the []error slice but still modifies the AST.
|
||||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, map[string][]byte, []error) {
|
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
@@ -175,11 +176,6 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
visitedFiles: map[string][]byte{},
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// Add a new location for the following file.
|
||||||
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
||||||
generatedTokenPos.SetLines([]int{0})
|
generatedTokenPos.SetLines([]int{0})
|
||||||
@@ -188,7 +184,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
// Find the absolute path for this package.
|
// Find the absolute path for this package.
|
||||||
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, []error{
|
return nil, nil, nil, nil, []error{
|
||||||
scanner.Error{
|
scanner.Error{
|
||||||
Pos: fset.Position(files[0].Pos()),
|
Pos: fset.Position(files[0].Pos()),
|
||||||
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
||||||
@@ -363,7 +359,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
makePathsAbsolute(flags, packagePath)
|
makePathsAbsolute(flags, packagePath)
|
||||||
cflags = append(cflags, flags...)
|
p.cflags = append(p.cflags, flags...)
|
||||||
case "LDFLAGS":
|
case "LDFLAGS":
|
||||||
flags, err := shlex.Split(value)
|
flags, err := shlex.Split(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -386,6 +382,13 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define CFlags that will be used while parsing the package.
|
||||||
|
// 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.
|
||||||
|
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
||||||
|
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
||||||
|
|
||||||
// Process all CGo imports.
|
// Process all CGo imports.
|
||||||
for _, genDecl := range statements {
|
for _, genDecl := range statements {
|
||||||
cgoComment := genDecl.Doc.Text()
|
cgoComment := genDecl.Doc.Text()
|
||||||
@@ -395,7 +398,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
pos = genDecl.Doc.Pos()
|
pos = genDecl.Doc.Pos()
|
||||||
}
|
}
|
||||||
position := fset.PositionFor(pos, true)
|
position := fset.PositionFor(pos, true)
|
||||||
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line)
|
p.parseFragment(cgoComment+cgoTypes, cflagsForCGo, position.Filename, position.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declare functions found by libclang.
|
// Declare functions found by libclang.
|
||||||
@@ -430,7 +433,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
// Print the newly generated in-memory AST, for debugging.
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
//ast.Print(fset, p.generated)
|
//ast.Print(fset, p.generated)
|
||||||
|
|
||||||
return p.generated, p.ldflags, p.visitedFiles, p.errors
|
return p.generated, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the AST with CGo.
|
// Process the AST with CGo.
|
||||||
cgoAST, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
cgoAST, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||||
|
|
||||||
// Check the AST for type errors.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
|
|||||||
@@ -230,7 +230,13 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
|
||||||
|
var WindowsBuildNotSupportedErr = errors.New("Building Windows binaries is currently not supported. Try specifying a different target")
|
||||||
|
|
||||||
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
|
if goos == "windows" {
|
||||||
|
return nil, WindowsBuildNotSupportedErr
|
||||||
|
}
|
||||||
// No target spec available. Use the default one, useful on most systems
|
// No target spec available. Use the default one, useful on most systems
|
||||||
// with a regular OS.
|
// with a regular OS.
|
||||||
spec := TargetSpec{
|
spec := TargetSpec{
|
||||||
|
|||||||
+22
-10
@@ -58,10 +58,10 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
|
|||||||
|
|
||||||
// Expand an argument type to a list that can be used in a function call
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// parameter list.
|
// parameter list.
|
||||||
func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldInfos := flattenAggregateType(t, name, goType)
|
fieldInfos := c.flattenAggregateType(t, name, goType)
|
||||||
if len(fieldInfos) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
return fieldInfos
|
return fieldInfos
|
||||||
} else {
|
} else {
|
||||||
@@ -105,7 +105,7 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
|||||||
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldInfos := flattenAggregateType(v.Type(), "", nil)
|
fieldInfos := b.flattenAggregateType(v.Type(), "", nil)
|
||||||
if len(fieldInfos) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
fields := b.flattenAggregate(v)
|
fields := b.flattenAggregate(v)
|
||||||
if len(fields) != len(fieldInfos) {
|
if len(fields) != len(fieldInfos) {
|
||||||
@@ -124,12 +124,15 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
|||||||
|
|
||||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||||
// with the passed in type if this is not possible.
|
// with the passed in type if this is not possible.
|
||||||
func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
typeFlags := getTypeFlags(goType)
|
typeFlags := getTypeFlags(goType)
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
|
var paramInfos []paramInfo
|
||||||
for i, subfield := range t.StructElementTypes() {
|
for i, subfield := range t.StructElementTypes() {
|
||||||
|
if c.targetData.TypeAllocSize(subfield) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
suffix := strconv.Itoa(i)
|
suffix := strconv.Itoa(i)
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
// Try to come up with a good suffix for this struct field,
|
// Try to come up with a good suffix for this struct field,
|
||||||
@@ -152,7 +155,7 @@ func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramIn
|
|||||||
suffix = []string{"context", "funcptr"}[i]
|
suffix = []string{"context", "funcptr"}[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
for i := range subInfos {
|
for i := range subInfos {
|
||||||
subInfos[i].flags |= typeFlags
|
subInfos[i].flags |= typeFlags
|
||||||
}
|
}
|
||||||
@@ -218,8 +221,11 @@ func extractSubfield(t types.Type, field int) types.Type {
|
|||||||
func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := make([]uint64, 0, t.StructElementTypesCount())
|
var fields []uint64
|
||||||
for fieldIndex, field := range t.StructElementTypes() {
|
for fieldIndex, field := range t.StructElementTypes() {
|
||||||
|
if c.targetData.TypeAllocSize(field) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
suboffsets := c.flattenAggregateTypeOffsets(field)
|
suboffsets := c.flattenAggregateTypeOffsets(field)
|
||||||
offset := c.targetData.ElementOffset(t, fieldIndex)
|
offset := c.targetData.ElementOffset(t, fieldIndex)
|
||||||
for i := range suboffsets {
|
for i := range suboffsets {
|
||||||
@@ -238,8 +244,11 @@ func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
|||||||
func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
|
func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount())
|
var fields []llvm.Value
|
||||||
for i := range v.Type().StructElementTypes() {
|
for i, field := range v.Type().StructElementTypes() {
|
||||||
|
if b.targetData.TypeAllocSize(field) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
subfield := b.CreateExtractValue(v, i, "")
|
subfield := b.CreateExtractValue(v, i, "")
|
||||||
subfields := b.flattenAggregate(subfield)
|
subfields := b.flattenAggregate(subfield)
|
||||||
fields = append(fields, subfields...)
|
fields = append(fields, subfields...)
|
||||||
@@ -266,10 +275,13 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val
|
|||||||
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
flattened := flattenAggregateType(t, "", nil)
|
flattened := b.flattenAggregateType(t, "", nil)
|
||||||
if len(flattened) <= maxFieldsPerParam {
|
if len(flattened) <= maxFieldsPerParam {
|
||||||
value := llvm.ConstNull(t)
|
value := llvm.ConstNull(t)
|
||||||
for i, subtyp := range t.StructElementTypes() {
|
for i, subtyp := range t.StructElementTypes() {
|
||||||
|
if b.targetData.TypeAllocSize(subtyp) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
structField, remaining := b.collapseFormalParamInternal(subtyp, fields)
|
structField, remaining := b.collapseFormalParamInternal(subtyp, fields)
|
||||||
fields = remaining
|
fields = remaining
|
||||||
value = b.CreateInsertValue(value, structField, i, "")
|
value = b.CreateInsertValue(value, structField, i, "")
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
// package changes in a way that affects the generated LLVM module.
|
// package changes in a way that affects the generated LLVM module.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 5 // last change: add method set to interface types
|
const Version = 6 // last change: fix issue 1304
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
@@ -829,7 +829,7 @@ func (b *builder) createFunction() {
|
|||||||
for _, param := range b.fn.Params {
|
for _, param := range b.fn.Params {
|
||||||
llvmType := b.getLLVMType(param.Type())
|
llvmType := b.getLLVMType(param.Type())
|
||||||
fields := make([]llvm.Value, 0, 1)
|
fields := make([]llvm.Value, 0, 1)
|
||||||
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
||||||
param := b.llvmFn.Param(llvmParamIndex)
|
param := b.llvmFn.Param(llvmParamIndex)
|
||||||
param.SetName(info.name)
|
param.SetName(info.name)
|
||||||
fields = append(fields, param)
|
fields = append(fields, param)
|
||||||
|
|||||||
+2
-2
@@ -124,13 +124,13 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
|||||||
// The receiver is not an interface, but a i8* type.
|
// The receiver is not an interface, but a i8* type.
|
||||||
recv = c.i8ptrType
|
recv = c.i8ptrType
|
||||||
}
|
}
|
||||||
for _, info := range expandFormalParamType(recv, "", nil) {
|
for _, info := range c.expandFormalParamType(recv, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for i := 0; i < typ.Params().Len(); i++ {
|
||||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||||
for _, info := range expandFormalParamType(subType, "", nil) {
|
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
|
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
|
||||||
var expandedReceiverType []llvm.Type
|
var expandedReceiverType []llvm.Type
|
||||||
for _, info := range expandFormalParamType(receiverType, "", nil) {
|
for _, info := range c.expandFormalParamType(receiverType, "", nil) {
|
||||||
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
var paramInfos []paramInfo
|
var paramInfos []paramInfo
|
||||||
for _, param := range getParams(fn.Signature) {
|
for _, param := range getParams(fn.Signature) {
|
||||||
paramType := c.getLLVMType(param.Type())
|
paramType := c.getLLVMType(param.Type())
|
||||||
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type())
|
||||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ var (
|
|||||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is one of the errors that can be returned from toLLVMValue when the
|
||||||
|
// passed type does not fit the data to serialize. It is recoverable by
|
||||||
|
// serializing without a type (using rawValue.rawLLVMValue).
|
||||||
|
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
||||||
|
|
||||||
func isRecoverableError(err error) bool {
|
func isRecoverableError(err error) bool {
|
||||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||||
}
|
}
|
||||||
|
|||||||
+124
-5
@@ -11,6 +11,12 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version of the interp package. It must be incremented whenever the interp
|
||||||
|
// package is changed in a way that affects the output so that cached package
|
||||||
|
// builds will be invalidated.
|
||||||
|
// This version is independent of the TinyGo version number.
|
||||||
|
const Version = 1
|
||||||
|
|
||||||
// Enable extra checks, which should be disabled by default.
|
// Enable extra checks, which should be disabled by default.
|
||||||
// This may help track down bugs by adding a few more sanity checks.
|
// This may help track down bugs by adding a few more sanity checks.
|
||||||
const checks = true
|
const checks = true
|
||||||
@@ -32,9 +38,7 @@ type runner struct {
|
|||||||
callsExecuted uint64
|
callsExecuted uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
func newRunner(mod llvm.Module, debug bool) *runner {
|
||||||
// Set debug to true if it should print output while running.
|
|
||||||
func Run(mod llvm.Module, debug bool) error {
|
|
||||||
r := runner{
|
r := runner{
|
||||||
mod: mod,
|
mod: mod,
|
||||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||||
@@ -47,6 +51,13 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||||
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
|
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
|
// Set debug to true if it should print output while running.
|
||||||
|
func Run(mod llvm.Module, debug bool) error {
|
||||||
|
r := newRunner(mod, debug)
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
bb := initAll.EntryBasicBlock()
|
bb := initAll.EntryBasicBlock()
|
||||||
@@ -117,7 +128,7 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
r.pkgName = ""
|
r.pkgName = ""
|
||||||
|
|
||||||
// Update all global variables in the LLVM module.
|
// Update all global variables in the LLVM module.
|
||||||
mem := memoryView{r: &r}
|
mem := memoryView{r: r}
|
||||||
for _, obj := range r.objects {
|
for _, obj := range r.objects {
|
||||||
if obj.llvmGlobal.IsNil() {
|
if obj.llvmGlobal.IsNil() {
|
||||||
continue
|
continue
|
||||||
@@ -125,7 +136,34 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
if obj.buffer == nil {
|
if obj.buffer == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
initializer := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||||
|
if err == errInvalidPtrToIntSize {
|
||||||
|
// This can happen when a previous interp run did not have the
|
||||||
|
// correct LLVM type for a global and made something up. In that
|
||||||
|
// case, some fields could be written out as a series of (null)
|
||||||
|
// bytes even though they actually contain a pointer value.
|
||||||
|
// As a fallback, use asRawValue to get something of the correct
|
||||||
|
// memory layout.
|
||||||
|
initializer, err := obj.buffer.asRawValue(r).rawLLVMValue(&mem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
initializerType := initializer.Type()
|
||||||
|
newGlobal := llvm.AddGlobal(mod, initializerType, obj.llvmGlobal.Name()+".tmp")
|
||||||
|
newGlobal.SetInitializer(initializer)
|
||||||
|
newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
|
||||||
|
newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
|
||||||
|
// TODO: copy debug info, unnamed_addr, ...
|
||||||
|
bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type())
|
||||||
|
obj.llvmGlobal.ReplaceAllUsesWith(bitcast)
|
||||||
|
name := obj.llvmGlobal.Name()
|
||||||
|
obj.llvmGlobal.EraseFromParentAsGlobal()
|
||||||
|
newGlobal.SetName(name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
||||||
panic("initializer type mismatch")
|
panic("initializer type mismatch")
|
||||||
}
|
}
|
||||||
@@ -135,6 +173,87 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunFunc evaluates a single package initializer at compile time.
|
||||||
|
// Set debug to true if it should print output while running.
|
||||||
|
func RunFunc(fn llvm.Value, debug bool) error {
|
||||||
|
// Create and initialize *runner object.
|
||||||
|
mod := fn.GlobalParent()
|
||||||
|
r := newRunner(mod, debug)
|
||||||
|
initName := fn.Name()
|
||||||
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
|
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
||||||
|
}
|
||||||
|
r.pkgName = initName[:len(initName)-len(".init")]
|
||||||
|
|
||||||
|
// Create new function with the interp result.
|
||||||
|
newFn := llvm.AddFunction(mod, fn.Name()+".tmp", fn.Type().ElementType())
|
||||||
|
newFn.SetLinkage(fn.Linkage())
|
||||||
|
newFn.SetVisibility(fn.Visibility())
|
||||||
|
entry := mod.Context().AddBasicBlock(newFn, "entry")
|
||||||
|
|
||||||
|
// Create a builder, to insert instructions that could not be evaluated at
|
||||||
|
// compile time.
|
||||||
|
r.builder = mod.Context().NewBuilder()
|
||||||
|
defer r.builder.Dispose()
|
||||||
|
r.builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
// Copy debug information.
|
||||||
|
subprogram := fn.Subprogram()
|
||||||
|
if !subprogram.IsNil() {
|
||||||
|
newFn.SetSubprogram(subprogram)
|
||||||
|
r.builder.SetCurrentDebugLocation(subprogram.SubprogramLine(), 0, subprogram, llvm.Metadata{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the initializer, filling the .init.tmp function.
|
||||||
|
if r.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
||||||
|
}
|
||||||
|
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||||
|
if callErr != nil {
|
||||||
|
if isRecoverableError(callErr.Err) {
|
||||||
|
// Could not finish, but could recover from it.
|
||||||
|
if r.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||||
|
}
|
||||||
|
newFn.EraseFromParentAsFunction()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return callErr
|
||||||
|
}
|
||||||
|
for index, obj := range pkgMem.objects {
|
||||||
|
r.objects[index] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update globals with values determined while running the initializer above.
|
||||||
|
mem := memoryView{r: r}
|
||||||
|
for _, obj := range r.objects {
|
||||||
|
if obj.llvmGlobal.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if obj.buffer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
||||||
|
panic("initializer type mismatch")
|
||||||
|
}
|
||||||
|
obj.llvmGlobal.SetInitializer(initializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize: remove the old init function and replace it with the new
|
||||||
|
// (.init.tmp) function.
|
||||||
|
r.builder.CreateRetVoid()
|
||||||
|
fnName := fn.Name()
|
||||||
|
fn.ReplaceAllUsesWith(newFn)
|
||||||
|
fn.EraseFromParentAsFunction()
|
||||||
|
newFn.SetName(fnName)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// getFunction returns the compiled version of the given LLVM function. It
|
// getFunction returns the compiled version of the given LLVM function. It
|
||||||
// compiles the function if necessary and caches the result.
|
// compiles the function if necessary and caches the result.
|
||||||
func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||||
|
|||||||
+38
-6
@@ -282,7 +282,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
|
||||||
}
|
}
|
||||||
// Extract the type code global from the first parameter.
|
// Extract the type code global from the first parameter.
|
||||||
typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0)
|
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
typecodeID := typecodeIDPtrToInt.Operand(0)
|
||||||
|
|
||||||
// Get the type class.
|
// Get the type class.
|
||||||
// See also: getClassAndValueFromTypeCode in transform/reflect.go.
|
// See also: getClassAndValueFromTypeCode in transform/reflect.go.
|
||||||
@@ -315,8 +319,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
|
||||||
}
|
}
|
||||||
assertedType := operands[2].toLLVMValue(inst.llvmInst.Operand(1).Type(), &mem)
|
assertedType, err := operands[2].toLLVMValue(inst.llvmInst.Operand(1).Type(), &mem)
|
||||||
actualTypePtrToInt := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
actualTypePtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
actualType := actualTypePtrToInt.Operand(0)
|
actualType := actualTypePtrToInt.Operand(0)
|
||||||
if actualType.Name()+"$id" == assertedType.Name() {
|
if actualType.Name()+"$id" == assertedType.Name() {
|
||||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||||
@@ -377,7 +387,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
|
|
||||||
// Load the first param, which is the type code (ptrtoint of the
|
// Load the first param, which is the type code (ptrtoint of the
|
||||||
// type code global).
|
// type code global).
|
||||||
typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0).Initializer()
|
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
typecodeID := typecodeIDPtrToInt.Operand(0).Initializer()
|
||||||
|
|
||||||
// Load the method set, which is part of the typecodeID object.
|
// Load the method set, which is part of the typecodeID object.
|
||||||
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
||||||
@@ -534,6 +548,13 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result := mem.load(ptr, uint32(size))
|
result := mem.load(ptr, uint32(size))
|
||||||
|
if result == nil {
|
||||||
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
|
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
|
||||||
}
|
}
|
||||||
@@ -556,7 +577,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
|
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
|
||||||
}
|
}
|
||||||
mem.store(val, ptr)
|
ok := mem.store(val, ptr)
|
||||||
|
if !ok {
|
||||||
|
// Could not store the value, do it at runtime.
|
||||||
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, err
|
||||||
|
}
|
||||||
|
}
|
||||||
case llvm.Alloca:
|
case llvm.Alloca:
|
||||||
// Alloca normally allocates some stack memory. In the interpreter,
|
// Alloca normally allocates some stack memory. In the interpreter,
|
||||||
// it allocates a global instead.
|
// it allocates a global instead.
|
||||||
@@ -888,7 +916,11 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
for i := 0; i < numOperands; i++ {
|
for i := 0; i < numOperands; i++ {
|
||||||
operand := inst.llvmInst.Operand(i)
|
operand := inst.llvmInst.Operand(i)
|
||||||
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
||||||
operand = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
|
var err error
|
||||||
|
operand, err = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
operands[i] = operand
|
operands[i] = operand
|
||||||
}
|
}
|
||||||
|
|||||||
+104
-55
@@ -259,12 +259,17 @@ func (mv *memoryView) put(index uint32, obj object) {
|
|||||||
mv.objects[index] = obj
|
mv.objects[index] = obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the value behind the given pointer.
|
// Load the value behind the given pointer. Returns nil if the pointer points to
|
||||||
|
// an external global.
|
||||||
func (mv *memoryView) load(p pointerValue, size uint32) value {
|
func (mv *memoryView) load(p pointerValue, size uint32) value {
|
||||||
if checks && mv.hasExternalStore(p) {
|
if checks && mv.hasExternalStore(p) {
|
||||||
panic("interp: load from object with external store")
|
panic("interp: load from object with external store")
|
||||||
}
|
}
|
||||||
obj := mv.get(p.index())
|
obj := mv.get(p.index())
|
||||||
|
if obj.buffer == nil {
|
||||||
|
// External global, return nil.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if p.offset() == 0 && size == obj.size {
|
if p.offset() == 0 && size == obj.size {
|
||||||
return obj.buffer.clone()
|
return obj.buffer.clone()
|
||||||
}
|
}
|
||||||
@@ -280,12 +285,17 @@ func (mv *memoryView) load(p pointerValue, size uint32) value {
|
|||||||
|
|
||||||
// Store to the value behind the given pointer. This overwrites the value in the
|
// Store to the value behind the given pointer. This overwrites the value in the
|
||||||
// memory view, so that the changed value is discarded when the memory view is
|
// memory view, so that the changed value is discarded when the memory view is
|
||||||
// reverted.
|
// reverted. Returns true on success, false if the object to store to is
|
||||||
func (mv *memoryView) store(v value, p pointerValue) {
|
// external.
|
||||||
|
func (mv *memoryView) store(v value, p pointerValue) bool {
|
||||||
if checks && mv.hasExternalLoadOrStore(p) {
|
if checks && mv.hasExternalLoadOrStore(p) {
|
||||||
panic("interp: store to object with external load/store")
|
panic("interp: store to object with external load/store")
|
||||||
}
|
}
|
||||||
obj := mv.get(p.index())
|
obj := mv.get(p.index())
|
||||||
|
if obj.buffer == nil {
|
||||||
|
// External global, return false (for a failure).
|
||||||
|
return false
|
||||||
|
}
|
||||||
if checks && p.offset()+v.len(mv.r) > obj.size {
|
if checks && p.offset()+v.len(mv.r) > obj.size {
|
||||||
panic("interp: store out of bounds")
|
panic("interp: store out of bounds")
|
||||||
}
|
}
|
||||||
@@ -301,6 +311,7 @@ func (mv *memoryView) store(v value, p pointerValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mv.put(p.index(), obj)
|
mv.put(p.index(), obj)
|
||||||
|
return true // success
|
||||||
}
|
}
|
||||||
|
|
||||||
// value is some sort of value, comparable to a LLVM constant. It can be
|
// value is some sort of value, comparable to a LLVM constant. It can be
|
||||||
@@ -314,7 +325,7 @@ type value interface {
|
|||||||
asRawValue(*runner) rawValue
|
asRawValue(*runner) rawValue
|
||||||
Uint() uint64
|
Uint() uint64
|
||||||
Int() int64
|
Int() int64
|
||||||
toLLVMValue(llvm.Type, *memoryView) llvm.Value
|
toLLVMValue(llvm.Type, *memoryView) (llvm.Value, error)
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,25 +416,25 @@ func (v literalValue) Int() int64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
switch llvmType.TypeKind() {
|
switch llvmType.TypeKind() {
|
||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
switch value := v.value.(type) {
|
switch value := v.value.(type) {
|
||||||
case uint64:
|
case uint64:
|
||||||
return llvm.ConstInt(llvmType, value, false)
|
return llvm.ConstInt(llvmType, value, false), nil
|
||||||
case uint32:
|
case uint32:
|
||||||
return llvm.ConstInt(llvmType, uint64(value), false)
|
return llvm.ConstInt(llvmType, uint64(value), false), nil
|
||||||
case uint16:
|
case uint16:
|
||||||
return llvm.ConstInt(llvmType, uint64(value), false)
|
return llvm.ConstInt(llvmType, uint64(value), false), nil
|
||||||
case uint8:
|
case uint8:
|
||||||
return llvm.ConstInt(llvmType, uint64(value), false)
|
return llvm.ConstInt(llvmType, uint64(value), false), nil
|
||||||
default:
|
default:
|
||||||
panic("inpterp: unknown literal type")
|
return llvm.Value{}, errors.New("interp: unknown literal type")
|
||||||
}
|
}
|
||||||
case llvm.DoubleTypeKind:
|
case llvm.DoubleTypeKind:
|
||||||
return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64)))
|
return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64))), nil
|
||||||
case llvm.FloatTypeKind:
|
case llvm.FloatTypeKind:
|
||||||
return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32))))
|
return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32)))), nil
|
||||||
default:
|
default:
|
||||||
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
||||||
}
|
}
|
||||||
@@ -507,7 +518,7 @@ func (v pointerValue) llvmValue(mem *memoryView) llvm.Value {
|
|||||||
// toLLVMValue returns the LLVM value for this pointer, which may be a GEP or
|
// toLLVMValue returns the LLVM value for this pointer, which may be a GEP or
|
||||||
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
|
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
|
||||||
// be different than expected.
|
// be different than expected.
|
||||||
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
// Obtain the llvmValue, creating it if it doesn't exist yet.
|
// Obtain the llvmValue, creating it if it doesn't exist yet.
|
||||||
llvmValue := v.llvmValue(mem)
|
llvmValue := v.llvmValue(mem)
|
||||||
if llvmValue.IsNil() {
|
if llvmValue.IsNil() {
|
||||||
@@ -518,7 +529,10 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
if obj.llvmType.IsNil() {
|
if obj.llvmType.IsNil() {
|
||||||
// Create an initializer without knowing the global type.
|
// Create an initializer without knowing the global type.
|
||||||
// This is probably the result of a runtime.alloc call.
|
// This is probably the result of a runtime.alloc call.
|
||||||
initializer := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
|
initializer, err := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
globalType := initializer.Type()
|
globalType := initializer.Type()
|
||||||
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
|
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
@@ -537,9 +551,12 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
// Set the initializer for the global. Do this after creation to avoid
|
// Set the initializer for the global. Do this after creation to avoid
|
||||||
// infinite recursion between creating the global and creating the
|
// infinite recursion between creating the global and creating the
|
||||||
// contents of the global (if the global contains itself).
|
// contents of the global (if the global contains itself).
|
||||||
initializer := obj.buffer.toLLVMValue(globalType, mem)
|
initializer, err := obj.buffer.toLLVMValue(globalType, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
if checks && initializer.Type() != globalType {
|
if checks && initializer.Type() != globalType {
|
||||||
panic("allocated value does not match allocated type")
|
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
|
||||||
}
|
}
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
}
|
}
|
||||||
@@ -552,7 +569,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.IsNil() {
|
if llvmType.IsNil() {
|
||||||
return llvmValue
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.TypeKind() != llvm.PointerTypeKind {
|
if llvmType.TypeKind() != llvm.PointerTypeKind {
|
||||||
@@ -564,7 +581,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
// This can be worked around by simply converting to a raw value,
|
// This can be worked around by simply converting to a raw value,
|
||||||
// rawValue knows how to create such structs.
|
// rawValue knows how to create such structs.
|
||||||
if v.offset() != 0 {
|
if v.offset() != 0 {
|
||||||
panic("offset set without known pointer type")
|
return llvm.Value{}, errors.New("interp: offset set without known pointer type")
|
||||||
}
|
}
|
||||||
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
||||||
}
|
}
|
||||||
@@ -575,14 +592,14 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
if v.offset() != 0 {
|
if v.offset() != 0 {
|
||||||
// This should never happen, if offset is non-zero, the types
|
// This should never happen, if offset is non-zero, the types
|
||||||
// shouldn't match.
|
// shouldn't match.
|
||||||
panic("offset set while there is no way to convert the type")
|
return llvm.Value{}, errors.New("interp: offset set while there is no way to convert the type")
|
||||||
}
|
}
|
||||||
return llvmValue
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.offset() == 0 {
|
if v.offset() == 0 {
|
||||||
// Offset is zero, so we can just bitcast to get a correct pointer.
|
// Offset is zero, so we can just bitcast to get a correct pointer.
|
||||||
return llvm.ConstBitCast(llvmValue, llvmType)
|
return llvm.ConstBitCast(llvmValue, llvmType), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to make a constant GEP for pointer arithmetic.
|
// We need to make a constant GEP for pointer arithmetic.
|
||||||
@@ -606,11 +623,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
offset -= int64(mem.r.targetData.ElementOffset(objectElementType, element))
|
offset -= int64(mem.r.targetData.ElementOffset(objectElementType, element))
|
||||||
objectElementType = objectElementType.StructElementTypes()[element]
|
objectElementType = objectElementType.StructElementTypes()[element]
|
||||||
default:
|
default:
|
||||||
panic("pointer index with something other than a struct or array?")
|
return llvm.Value{}, errors.New("interp: pointer index with something other than a struct or array?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
panic("offset has somehow gone negative, this should be impossible")
|
return llvm.Value{}, errors.New("interp: offset has somehow gone negative, this should be impossible")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally do the gep, using the above computed indices.
|
// Finally do the gep, using the above computed indices.
|
||||||
@@ -618,9 +635,9 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
// the bits of the pointer are now correct, just not the type).
|
// the bits of the pointer are now correct, just not the type).
|
||||||
gep := llvm.ConstInBoundsGEP(llvmValue, indices)
|
gep := llvm.ConstInBoundsGEP(llvmValue, indices)
|
||||||
if gep.Type() != llvmType {
|
if gep.Type() != llvmType {
|
||||||
return llvm.ConstBitCast(gep, llvmType)
|
return llvm.ConstBitCast(gep, llvmType), nil
|
||||||
}
|
}
|
||||||
return gep
|
return gep, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapValue implements a Go map which is created at compile time and stored as a
|
// mapValue implements a Go map which is created at compile time and stored as a
|
||||||
@@ -713,7 +730,11 @@ func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryV
|
|||||||
// Create data for keys.
|
// Create data for keys.
|
||||||
var keyValues []llvm.Value
|
var keyValues []llvm.Value
|
||||||
for _, key := range b.keys {
|
for _, key := range b.keys {
|
||||||
keyValues = append(keyValues, key.rawLLVMValue(mem))
|
keyValue, err := key.rawLLVMValue(mem)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
keyValues = append(keyValues, keyValue)
|
||||||
}
|
}
|
||||||
if len(b.keys) < 8 {
|
if len(b.keys) < 8 {
|
||||||
keyValues = append(keyValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.keySize)*(8-len(b.keys)))))
|
keyValues = append(keyValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.keySize)*(8-len(b.keys)))))
|
||||||
@@ -726,7 +747,11 @@ func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryV
|
|||||||
// Create data for values.
|
// Create data for values.
|
||||||
var valueValues []llvm.Value
|
var valueValues []llvm.Value
|
||||||
for _, value := range b.values {
|
for _, value := range b.values {
|
||||||
valueValues = append(valueValues, value.rawLLVMValue(mem))
|
v, err := value.rawLLVMValue(mem)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
valueValues = append(valueValues, v)
|
||||||
}
|
}
|
||||||
if len(b.values) < 8 {
|
if len(b.values) < 8 {
|
||||||
valueValues = append(valueValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.valueSize)*(8-len(b.values)))))
|
valueValues = append(valueValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.valueSize)*(8-len(b.values)))))
|
||||||
@@ -750,9 +775,9 @@ func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryV
|
|||||||
return bucket
|
return bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) llvm.Value {
|
func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
if !v.hashmap.IsNil() {
|
if !v.hashmap.IsNil() {
|
||||||
return v.hashmap
|
return v.hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a slice of buckets with all the keys and values in the hashmap.
|
// Create a slice of buckets with all the keys and values in the hashmap.
|
||||||
@@ -772,12 +797,12 @@ func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
copy(keyValue.buf[v.keySize/2:], literalValue{key.size}.asRawValue(v.r).buf)
|
copy(keyValue.buf[v.keySize/2:], literalValue{key.size}.asRawValue(v.r).buf)
|
||||||
case rawValue:
|
case rawValue:
|
||||||
if key.hasPointer() {
|
if key.hasPointer() {
|
||||||
panic("todo: map key with pointer")
|
return llvm.Value{}, errors.New("interp: todo: map key with pointer")
|
||||||
}
|
}
|
||||||
data = key.buf
|
data = key.buf
|
||||||
keyValue = key
|
keyValue = key
|
||||||
default:
|
default:
|
||||||
panic("unknown map key type")
|
return llvm.Value{}, errors.New("interp: unknown map key type")
|
||||||
}
|
}
|
||||||
buf := make([]byte, len(data))
|
buf := make([]byte, len(data))
|
||||||
for i, p := range data {
|
for i, p := range data {
|
||||||
@@ -821,7 +846,7 @@ func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
})
|
})
|
||||||
|
|
||||||
v.hashmap = hashmap
|
v.hashmap = hashmap
|
||||||
return v.hashmap
|
return v.hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putString does a map assign operation, assuming that the map is of type
|
// putString does a map assign operation, assuming that the map is of type
|
||||||
@@ -1018,7 +1043,7 @@ func (v rawValue) equal(rhs rawValue) bool {
|
|||||||
// goes. The resulting value does not have a specified type, but it will be the
|
// goes. The resulting value does not have a specified type, but it will be the
|
||||||
// same size and have the same bytes if it was created with a provided LLVM type
|
// same size and have the same bytes if it was created with a provided LLVM type
|
||||||
// (through toLLVMValue).
|
// (through toLLVMValue).
|
||||||
func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
|
||||||
var structFields []llvm.Value
|
var structFields []llvm.Value
|
||||||
ctx := mem.r.mod.Context()
|
ctx := mem.r.mod.Context()
|
||||||
int8Type := ctx.Int8Type()
|
int8Type := ctx.Int8Type()
|
||||||
@@ -1042,7 +1067,10 @@ func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
|||||||
for i := uint32(0); i < uint32(len(v.buf)); {
|
for i := uint32(0); i < uint32(len(v.buf)); {
|
||||||
if v.buf[i] > 255 {
|
if v.buf[i] > 255 {
|
||||||
addBytes()
|
addBytes()
|
||||||
field := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
|
field, err := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
elementType := field.Type().ElementType()
|
elementType := field.Type().ElementType()
|
||||||
if elementType.TypeKind() == llvm.StructTypeKind {
|
if elementType.TypeKind() == llvm.StructTypeKind {
|
||||||
// There are some special pointer types that should be used as a
|
// There are some special pointer types that should be used as a
|
||||||
@@ -1065,12 +1093,12 @@ func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
|||||||
|
|
||||||
// Return the created data.
|
// Return the created data.
|
||||||
if len(structFields) == 1 {
|
if len(structFields) == 1 {
|
||||||
return structFields[0]
|
return structFields[0], nil
|
||||||
}
|
}
|
||||||
return ctx.ConstStruct(structFields, false)
|
return ctx.ConstStruct(structFields, false), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
isZero := true
|
isZero := true
|
||||||
for _, p := range v.buf {
|
for _, p := range v.buf {
|
||||||
if p != 0 {
|
if p != 0 {
|
||||||
@@ -1079,7 +1107,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isZero {
|
if isZero {
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType), nil
|
||||||
}
|
}
|
||||||
switch llvmType.TypeKind() {
|
switch llvmType.TypeKind() {
|
||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
@@ -1088,7 +1116,17 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return llvm.ConstPtrToInt(ptr.toLLVMValue(llvm.Type{}, mem), llvmType)
|
if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.i8ptrType) {
|
||||||
|
// Probably trying to serialize a pointer to a byte array,
|
||||||
|
// perhaps as a result of rawLLVMValue() in a previous interp
|
||||||
|
// run.
|
||||||
|
return llvm.Value{}, errInvalidPtrToIntSize
|
||||||
|
}
|
||||||
|
v, err := ptr.toLLVMValue(llvm.Type{}, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
return llvm.ConstPtrToInt(v, llvmType), nil
|
||||||
}
|
}
|
||||||
var n uint64
|
var n uint64
|
||||||
switch llvmType.IntTypeWidth() {
|
switch llvmType.IntTypeWidth() {
|
||||||
@@ -1108,7 +1146,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
default:
|
default:
|
||||||
panic("unknown integer size")
|
panic("unknown integer size")
|
||||||
}
|
}
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false), nil
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldTypes := llvmType.StructElementTypes()
|
fieldTypes := llvmType.StructElementTypes()
|
||||||
fields := make([]llvm.Value, len(fieldTypes))
|
fields := make([]llvm.Value, len(fieldTypes))
|
||||||
@@ -1117,12 +1155,16 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
field := rawValue{
|
field := rawValue{
|
||||||
buf: v.buf[offset:],
|
buf: v.buf[offset:],
|
||||||
}
|
}
|
||||||
fields[i] = field.toLLVMValue(fieldType, mem)
|
var err error
|
||||||
|
fields[i], err = field.toLLVMValue(fieldType, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if llvmType.StructName() != "" {
|
if llvmType.StructName() != "" {
|
||||||
return llvm.ConstNamedStruct(llvmType, fields)
|
return llvm.ConstNamedStruct(llvmType, fields), nil
|
||||||
}
|
}
|
||||||
return llvmType.Context().ConstStruct(fields, false)
|
return llvmType.Context().ConstStruct(fields, false), nil
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
numElements := llvmType.ArrayLength()
|
numElements := llvmType.ArrayLength()
|
||||||
childType := llvmType.ElementType()
|
childType := llvmType.ElementType()
|
||||||
@@ -1133,27 +1175,34 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
field := rawValue{
|
field := rawValue{
|
||||||
buf: v.buf[offset:],
|
buf: v.buf[offset:],
|
||||||
}
|
}
|
||||||
fields[i] = field.toLLVMValue(childType, mem)
|
var err error
|
||||||
|
fields[i], err = field.toLLVMValue(childType, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
if checks && fields[i].Type() != childType {
|
if checks && fields[i].Type() != childType {
|
||||||
panic("child type doesn't match")
|
panic("child type doesn't match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return llvm.ConstArray(childType, fields)
|
return llvm.ConstArray(childType, fields), nil
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
if v.buf[0] > 255 {
|
if v.buf[0] > 255 {
|
||||||
// This is a regular pointer.
|
// This is a regular pointer.
|
||||||
llvmValue := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
|
llvmValue, err := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
if llvmValue.Type() != llvmType {
|
if llvmValue.Type() != llvmType {
|
||||||
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
||||||
}
|
}
|
||||||
return llvmValue
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
// This is either a null pointer or a raw pointer for memory-mapped I/O
|
// This is either a null pointer or a raw pointer for memory-mapped I/O
|
||||||
// (such as 0xe000ed00).
|
// (such as 0xe000ed00).
|
||||||
ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint()
|
ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint()
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
// Null pointer.
|
// Null pointer.
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType), nil
|
||||||
}
|
}
|
||||||
var ptrValue llvm.Value // the underlying int
|
var ptrValue llvm.Value // the underlying int
|
||||||
switch mem.r.pointerSize {
|
switch mem.r.pointerSize {
|
||||||
@@ -1164,19 +1213,19 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
case 2:
|
case 2:
|
||||||
ptrValue = llvm.ConstInt(llvmType.Context().Int16Type(), ptr, false)
|
ptrValue = llvm.ConstInt(llvmType.Context().Int16Type(), ptr, false)
|
||||||
default:
|
default:
|
||||||
panic("unknown pointer size")
|
return llvm.Value{}, errors.New("interp: unknown pointer size")
|
||||||
}
|
}
|
||||||
return llvm.ConstIntToPtr(ptrValue, llvmType)
|
return llvm.ConstIntToPtr(ptrValue, llvmType), nil
|
||||||
case llvm.DoubleTypeKind:
|
case llvm.DoubleTypeKind:
|
||||||
b := rawValue{v.buf[:8]}.Uint()
|
b := rawValue{v.buf[:8]}.Uint()
|
||||||
f := math.Float64frombits(b)
|
f := math.Float64frombits(b)
|
||||||
return llvm.ConstFloat(llvmType, f)
|
return llvm.ConstFloat(llvmType, f), nil
|
||||||
case llvm.FloatTypeKind:
|
case llvm.FloatTypeKind:
|
||||||
b := uint32(rawValue{v.buf[:4]}.Uint())
|
b := uint32(rawValue{v.buf[:4]}.Uint())
|
||||||
f := math.Float32frombits(b)
|
f := math.Float32frombits(b)
|
||||||
return llvm.ConstFloat(llvmType, float64(f))
|
return llvm.ConstFloat(llvmType, float64(f)), nil
|
||||||
default:
|
default:
|
||||||
panic("todo: raw value to LLVM value: " + llvmType.String())
|
return llvm.Value{}, errors.New("interp: todo: raw value to LLVM value: " + llvmType.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1365,8 +1414,8 @@ func (v localValue) Int() int64 {
|
|||||||
panic("interp: localValue.Int")
|
panic("interp: localValue.Int")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
return v.value
|
return v.value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runner) getValue(llvmValue llvm.Value) value {
|
func (r *runner) getValue(llvmValue llvm.Value) value {
|
||||||
|
|||||||
+6
-6
@@ -382,14 +382,14 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
|
|
||||||
// Do CGo processing.
|
// Do CGo processing.
|
||||||
if len(p.CgoFiles) != 0 {
|
if len(p.CgoFiles) != 0 {
|
||||||
var cflags []string
|
var initialCFlags []string
|
||||||
cflags = append(cflags, p.program.config.CFlags()...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
||||||
cflags = append(cflags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
if p.program.clangHeaders != "" {
|
if p.program.clangHeaders != "" {
|
||||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
initialCFlags = append(initialCFlags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
||||||
}
|
}
|
||||||
p.CFlags = cflags
|
generated, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, initialCFlags)
|
||||||
generated, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
p.CFlags = append(initialCFlags, cflags...)
|
||||||
for path, hash := range accessedFiles {
|
for path, hash := range accessedFiles {
|
||||||
p.FileHashes[path] = hash
|
p.FileHashes[path] = hash
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func copyFile(src, dst string) error {
|
|||||||
// executeCommand is a simple wrapper to exec.Cmd
|
// executeCommand is a simple wrapper to exec.Cmd
|
||||||
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
||||||
if options.PrintCommands {
|
if options.PrintCommands {
|
||||||
fmt.Printf("%s %s\n ", name, strings.Join(arg, " "))
|
fmt.Printf("%s %s\n", name, strings.Join(arg, " "))
|
||||||
}
|
}
|
||||||
return exec.Command(name, arg...)
|
return exec.Command(name, arg...)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-14
@@ -15,7 +15,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -129,18 +128,6 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles.
|
|
||||||
// Therefore, we put a lock around builds and run everything else in parallel.
|
|
||||||
var buildLock sync.Mutex
|
|
||||||
|
|
||||||
// runBuild is a thread-safe wrapper around Build.
|
|
||||||
func runBuild(src, out string, opts *compileopts.Options) error {
|
|
||||||
buildLock.Lock()
|
|
||||||
defer buildLock.Unlock()
|
|
||||||
|
|
||||||
return Build(src, out, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
@@ -177,7 +164,7 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
|||||||
}
|
}
|
||||||
|
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = runBuild("./"+path, binary, config)
|
err = Build("./"+path, binary, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|||||||
@@ -1646,6 +1646,7 @@ func (usbcdc *USBCDC) Flush() error {
|
|||||||
|
|
||||||
if usbcdc.waitTxc {
|
if usbcdc.waitTxc {
|
||||||
// waiting for the next flush(), because the transmission is not complete
|
// waiting for the next flush(), because the transmission is not complete
|
||||||
|
usbcdc.waitTxcRetryCount++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
usbcdc.waitTxc = true
|
usbcdc.waitTxc = true
|
||||||
@@ -1700,7 +1701,7 @@ func (usbcdc *USBCDC) WriteByte(c byte) error {
|
|||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
UART0.waitTxc = false
|
UART0.waitTxc = false
|
||||||
UART0.waitTxcRetryCount = 0
|
UART0.waitTxcRetryCount = 0
|
||||||
usbcdc.TxIdx.Set(0)
|
UART0.TxIdx.Set(0)
|
||||||
usbLineInfo.lineState = 0
|
usbLineInfo.lineState = 0
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
break
|
break
|
||||||
@@ -1868,6 +1869,7 @@ func handleUSB(intr interrupt.Interrupt) {
|
|||||||
|
|
||||||
// Start of frame
|
// Start of frame
|
||||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||||
|
UART0.Flush()
|
||||||
// if you want to blink LED showing traffic, this would be the place...
|
// if you want to blink LED showing traffic, this would be the place...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1931,13 +1933,7 @@ func handleUSB(intr interrupt.Interrupt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
|
|
||||||
UART0.waitTxcRetryCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UART0.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
func initEndpoint(ep, config uint32) {
|
||||||
|
|||||||
@@ -1850,6 +1850,7 @@ func (usbcdc *USBCDC) Flush() error {
|
|||||||
|
|
||||||
if usbcdc.waitTxc {
|
if usbcdc.waitTxc {
|
||||||
// waiting for the next flush(), because the transmission is not complete
|
// waiting for the next flush(), because the transmission is not complete
|
||||||
|
usbcdc.waitTxcRetryCount++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
usbcdc.waitTxc = true
|
usbcdc.waitTxc = true
|
||||||
@@ -1904,7 +1905,7 @@ func (usbcdc *USBCDC) WriteByte(c byte) error {
|
|||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
UART0.waitTxc = false
|
UART0.waitTxc = false
|
||||||
UART0.waitTxcRetryCount = 0
|
UART0.waitTxcRetryCount = 0
|
||||||
usbcdc.TxIdx.Set(0)
|
UART0.TxIdx.Set(0)
|
||||||
usbLineInfo.lineState = 0
|
usbLineInfo.lineState = 0
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
break
|
break
|
||||||
@@ -2074,6 +2075,7 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
|||||||
|
|
||||||
// Start of frame
|
// Start of frame
|
||||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||||
|
UART0.Flush()
|
||||||
// if you want to blink LED showing traffic, this would be the place...
|
// if you want to blink LED showing traffic, this would be the place...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2137,13 +2139,7 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
|
|
||||||
UART0.waitTxcRetryCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UART0.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
func initEndpoint(ep, config uint32) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func (usbcdc *USBCDC) Flush() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the USB CDC interface.
|
// WriteByte writes a byte of data to the USB CDC interface.
|
||||||
func (usbcdc USBCDC) WriteByte(c byte) error {
|
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||||
// Supposedly to handle problem with Windows USB serial ports?
|
// Supposedly to handle problem with Windows USB serial ports?
|
||||||
if usbLineInfo.lineState > 0 {
|
if usbLineInfo.lineState > 0 {
|
||||||
ok := false
|
ok := false
|
||||||
@@ -199,6 +199,7 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
|||||||
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
||||||
nrf.USBD.EVENTS_SOF.Set(0)
|
nrf.USBD.EVENTS_SOF.Set(0)
|
||||||
USB.Flush()
|
USB.Flush()
|
||||||
|
// if you want to blink LED showing traffic, this would be the place...
|
||||||
}
|
}
|
||||||
|
|
||||||
// USBD ready event
|
// USBD ready event
|
||||||
|
|||||||
Vendored
+17
@@ -74,6 +74,14 @@ func main() {
|
|||||||
//Test deferred builtins
|
//Test deferred builtins
|
||||||
testDeferBuiltinClose()
|
testDeferBuiltinClose()
|
||||||
testDeferBuiltinDelete()
|
testDeferBuiltinDelete()
|
||||||
|
|
||||||
|
// Check for issue 1304.
|
||||||
|
// There are two fields in this struct, one of which is zero-length so the
|
||||||
|
// other covers the entire struct. This led to a verification error for
|
||||||
|
// debug info, which used DW_OP_LLVM_fragment for a field that practically
|
||||||
|
// covered the entire variable.
|
||||||
|
var x issue1304
|
||||||
|
x.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFunc(f func(int), arg int) {
|
func runFunc(f func(int), arg int) {
|
||||||
@@ -211,3 +219,12 @@ func foo(bar *Bar) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type issue1304 struct {
|
||||||
|
a [0]int // zero-length field
|
||||||
|
b int // field 'b' covers entire struct
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x issue1304) call() {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -20,6 +20,8 @@ int globalUnionSize = sizeof(globalUnion);
|
|||||||
option_t globalOption = optionG;
|
option_t globalOption = optionG;
|
||||||
bitfield_t globalBitfield = {244, 15, 1, 2, 47, 5};
|
bitfield_t globalBitfield = {244, 15, 1, 2, 47, 5};
|
||||||
|
|
||||||
|
int cflagsConstant = SOME_CONSTANT;
|
||||||
|
|
||||||
int smallEnumWidth = sizeof(option2_t);
|
int smallEnumWidth = sizeof(option2_t);
|
||||||
|
|
||||||
int fortytwo() {
|
int fortytwo() {
|
||||||
|
|||||||
Vendored
+4
@@ -5,6 +5,7 @@ int fortytwo(void);
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
int mul(int, int);
|
int mul(int, int);
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#cgo CFLAGS: -DSOME_CONSTANT=17
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@@ -118,6 +119,9 @@ func main() {
|
|||||||
// Check that enums are considered the same width in C and CGo.
|
// Check that enums are considered the same width in C and CGo.
|
||||||
println("enum width matches:", unsafe.Sizeof(C.option2_t(0)) == uintptr(C.smallEnumWidth))
|
println("enum width matches:", unsafe.Sizeof(C.option2_t(0)) == uintptr(C.smallEnumWidth))
|
||||||
|
|
||||||
|
// Check whether CFLAGS are correctly passed on to compiled C files.
|
||||||
|
println("CFLAGS value:", C.cflagsConstant)
|
||||||
|
|
||||||
// libc: test whether C functions work at all.
|
// libc: test whether C functions work at all.
|
||||||
buf1 := []byte("foobar\x00")
|
buf1 := []byte("foobar\x00")
|
||||||
buf2 := make([]byte, len(buf1))
|
buf2 := make([]byte, len(buf1))
|
||||||
|
|||||||
Vendored
+2
@@ -139,6 +139,8 @@ extern bitfield_t globalBitfield;
|
|||||||
|
|
||||||
extern int smallEnumWidth;
|
extern int smallEnumWidth;
|
||||||
|
|
||||||
|
extern int cflagsConstant;
|
||||||
|
|
||||||
// test duplicate definitions
|
// test duplicate definitions
|
||||||
int add(int a, int b);
|
int add(int a, int b);
|
||||||
extern int global;
|
extern int global;
|
||||||
|
|||||||
Vendored
+1
@@ -58,4 +58,5 @@ option G: 12
|
|||||||
option 2A: 20
|
option 2A: 20
|
||||||
option 3A: 21
|
option 3A: 21
|
||||||
enum width matches: true
|
enum width matches: true
|
||||||
|
CFLAGS value: 17
|
||||||
copied string: foobar
|
copied string: foobar
|
||||||
|
|||||||
@@ -703,6 +703,20 @@ func (p *lowerInterfacesPass) createInterfaceMethodFunc(itf *interfaceInfo, sign
|
|||||||
// function.
|
// function.
|
||||||
receiver = p.builder.CreateBitCast(receiver, function.FirstParam().Type(), "")
|
receiver = p.builder.CreateBitCast(receiver, function.FirstParam().Type(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether the called function has the same signature as would be
|
||||||
|
// expected from the parameters. This can happen in rare cases when
|
||||||
|
// named struct types are renamed after merging multiple LLVM modules.
|
||||||
|
paramTypes := []llvm.Type{receiver.Type()}
|
||||||
|
for _, param := range params {
|
||||||
|
paramTypes = append(paramTypes, param.Type())
|
||||||
|
}
|
||||||
|
calledFunctionType := function.Type()
|
||||||
|
sig := llvm.PointerType(llvm.FunctionType(calledFunctionType.ElementType().ReturnType(), paramTypes, false), calledFunctionType.PointerAddressSpace())
|
||||||
|
if sig != function.Type() {
|
||||||
|
function = p.builder.CreateBitCast(function, sig, "")
|
||||||
|
}
|
||||||
|
|
||||||
retval := p.builder.CreateCall(function, append([]llvm.Value{receiver}, params...), "")
|
retval := p.builder.CreateCall(function, append([]llvm.Value{receiver}, params...), "")
|
||||||
if retval.Type().TypeKind() == llvm.VoidTypeKind {
|
if retval.Type().TypeKind() == llvm.VoidTypeKind {
|
||||||
p.builder.CreateRetVoid()
|
p.builder.CreateRetVoid()
|
||||||
|
|||||||
Reference in New Issue
Block a user