builder, compiler: compile and cache packages in parallel
This commit switches from the previous behavior of compiling the whole program at once, to compiling every package in parallel and linking the LLVM bitcode files together for further whole-program optimization. This is a small performance win, but it has several advantages in the future: - There are many more things that can be done per package in parallel, avoiding the bottleneck at the end of the compiler phase. This should speed up the compiler futher. - This change is a necessary step towards a non-LTO build mode for fast incremental builds that only rebuild the changed package, when compiler speed is more important than binary size. - This change refactors the compiler in such a way that it will be easier to inspect the IR for one package only. Inspecting this IR will be very helpful for compiler developers.
Этот коммит содержится в:
родитель
dc1ff80e10
коммит
e2f532709f
21 изменённых файлов: 404 добавлений и 189 удалений
268
builder/build.go
268
builder/build.go
|
@ -4,14 +4,18 @@
|
||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -38,6 +42,26 @@ type BuildResult struct {
|
||||||
MainDir string
|
MainDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||||
|
// a cache key of compiled packages. It should contain all the information that
|
||||||
|
// goes into a compiled package to avoid using stale data.
|
||||||
|
//
|
||||||
|
// Right now it's still important to include a hash of every import, because a
|
||||||
|
// dependency might have a public constant that this package uses and thus this
|
||||||
|
// package will need to be recompiled if that constant changes. In the future,
|
||||||
|
// the type data should be serialized to disk which can then be used as cache
|
||||||
|
// key, avoiding the need for recompiling all dependencies when only the
|
||||||
|
// implementation of an imported package changes.
|
||||||
|
type packageAction struct {
|
||||||
|
ImportPath string
|
||||||
|
CompilerVersion int // compiler.Version
|
||||||
|
LLVMVersion string
|
||||||
|
Config *compiler.Config
|
||||||
|
CFlags []string
|
||||||
|
FileHashes map[string]string // hash of every file that's part of the package
|
||||||
|
Imports map[string]string // map from imported package to action ID hash
|
||||||
|
}
|
||||||
|
|
||||||
// Build performs a single package to executable Go build. It takes in a package
|
// Build performs a single package to executable Go build. It takes in a package
|
||||||
// name, an output path, and set of compile options and from that it manages the
|
// name, an output path, and set of compile options and from that it manages the
|
||||||
// whole compilation process.
|
// whole compilation process.
|
||||||
|
@ -45,6 +69,13 @@ type BuildResult struct {
|
||||||
// The error value may be of type *MultiError. Callers will likely want to check
|
// The error value may be of type *MultiError. Callers will likely want to check
|
||||||
// for this case and print such errors individually.
|
// for this case and print such errors individually.
|
||||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||||
|
// Create a temporary directory for intermediary files.
|
||||||
|
dir, err := ioutil.TempDir("", "tinygo")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
compilerConfig := &compiler.Config{
|
compilerConfig := &compiler.Config{
|
||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
CPU: config.CPU(),
|
CPU: config.CPU(),
|
||||||
|
@ -87,23 +118,200 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
// Makefile target.
|
// Makefile target.
|
||||||
var jobs []*compileJob
|
var jobs []*compileJob
|
||||||
|
|
||||||
// Add job to compile and optimize all Go files at once.
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// TODO: parallelize this.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
|
program := lprogram.LoadSSA()
|
||||||
|
|
||||||
|
// Add jobs to compile each package.
|
||||||
|
// Packages that have a cache hit will not be compiled again.
|
||||||
|
var packageJobs []*compileJob
|
||||||
|
packageBitcodePaths := make(map[string]string)
|
||||||
|
packageActionIDs := make(map[string]string)
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkg := pkg // necessary to avoid a race condition
|
||||||
|
|
||||||
|
// Create a cache key: a hash from the action ID below that contains all
|
||||||
|
// the parameters for the build.
|
||||||
|
actionID := packageAction{
|
||||||
|
ImportPath: pkg.ImportPath,
|
||||||
|
CompilerVersion: compiler.Version,
|
||||||
|
LLVMVersion: llvm.Version,
|
||||||
|
Config: compilerConfig,
|
||||||
|
CFlags: pkg.CFlags,
|
||||||
|
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||||
|
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||||
|
}
|
||||||
|
for filePath, hash := range pkg.FileHashes {
|
||||||
|
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||||
|
}
|
||||||
|
for _, imported := range pkg.Pkg.Imports() {
|
||||||
|
hash, ok := packageActionIDs[imported.Path()]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||||
|
}
|
||||||
|
actionID.Imports[imported.Path()] = hash
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(actionID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
hash := sha512.Sum512_224(buf)
|
||||||
|
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
// Determine the path of the bitcode file (which is a serialized version
|
||||||
|
// of a LLVM module).
|
||||||
|
cacheDir := goenv.Get("GOCACHE")
|
||||||
|
if cacheDir == "off" {
|
||||||
|
// Use temporary build directory instead, effectively disabling the
|
||||||
|
// build cache.
|
||||||
|
cacheDir = dir
|
||||||
|
}
|
||||||
|
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
||||||
|
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
||||||
|
|
||||||
|
// Check whether this package has been compiled before, and if so don't
|
||||||
|
// compile it again.
|
||||||
|
if _, err := os.Stat(bitcodePath); err == nil {
|
||||||
|
// Already cached, don't recreate this package.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The package has not yet been compiled, so create a job to do so.
|
||||||
|
job := &compileJob{
|
||||||
|
description: "compile package " + pkg.ImportPath,
|
||||||
|
run: func() error {
|
||||||
|
// Compile AST to IR. The compiler.CompilePackage function will
|
||||||
|
// build the SSA as needed.
|
||||||
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
|
if errs != nil {
|
||||||
|
return newMultiError(errs)
|
||||||
|
}
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the LLVM module as a bitcode file.
|
||||||
|
// Write to a temporary path that is renamed to the destination
|
||||||
|
// file to avoid race conditions with other TinyGo invocatiosn
|
||||||
|
// that might also be compiling this package at the same time.
|
||||||
|
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Work around a problem on Windows.
|
||||||
|
// For some reason, WriteBitcodeToFile causes TinyGo to
|
||||||
|
// exit with the following message:
|
||||||
|
// LLVM ERROR: IO failure on output stream: Bad file descriptor
|
||||||
|
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||||
|
defer buf.Dispose()
|
||||||
|
_, err = f.Write(buf.Bytes())
|
||||||
|
} else {
|
||||||
|
// Otherwise, write bitcode directly to the file (probably
|
||||||
|
// faster).
|
||||||
|
err = llvm.WriteBitcodeToFile(mod, f)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||||
|
// own, so create a somewhat useful error message here.
|
||||||
|
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(f.Name(), bitcodePath)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
packageJobs = append(packageJobs, job)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add job that links and optimizes all packages together.
|
||||||
var mod llvm.Module
|
var mod llvm.Module
|
||||||
var stackSizeLoads []string
|
var stackSizeLoads []string
|
||||||
programJob := &compileJob{
|
programJob := &compileJob{
|
||||||
description: "compile Go files",
|
description: "link+optimize packages (LTO)",
|
||||||
run: func() (err error) {
|
dependencies: packageJobs,
|
||||||
mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine)
|
run: func() error {
|
||||||
if err != nil {
|
// Load and link all the bitcode files. This does not yet optimize
|
||||||
return
|
// anything, it only links the bitcode files together.
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
mod = ctx.NewModule("")
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||||
|
}
|
||||||
|
err = llvm.LinkModules(mod, pkgMod)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to link module: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create runtime.initAll function that calls the runtime
|
||||||
|
// initializer of each package.
|
||||||
|
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||||
|
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
llvmInitFn.SetUnnamedAddr(true)
|
||||||
|
llvmInitFn.Param(0).SetName("context")
|
||||||
|
llvmInitFn.Param(1).SetName("parentHandle")
|
||||||
|
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
|
||||||
|
irbuilder := mod.Context().NewBuilder()
|
||||||
|
defer irbuilder.Dispose()
|
||||||
|
irbuilder.SetInsertPointAtEnd(block)
|
||||||
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||||
|
if pkgInit.IsNil() {
|
||||||
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
|
}
|
||||||
|
irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType), llvm.Undef(i8ptrType)}, "")
|
||||||
|
}
|
||||||
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
|
// After linking, functions should (as far as possible) be set to
|
||||||
|
// private linkage or internal linkage. The compiler package marks
|
||||||
|
// non-exported functions by setting the visibility to hidden or
|
||||||
|
// (for thunks) to linkonce_odr linkage. Change the linkage here to
|
||||||
|
// internal to benefit much more from interprocedural optimizations.
|
||||||
|
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
|
if fn.Visibility() == llvm.HiddenVisibility {
|
||||||
|
fn.SetVisibility(llvm.DefaultVisibility)
|
||||||
|
fn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
} else if fn.Linkage() == llvm.LinkOnceODRLinkage {
|
||||||
|
fn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the same for globals.
|
||||||
|
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||||
|
if global.Visibility() == llvm.HiddenVisibility {
|
||||||
|
global.SetVisibility(llvm.DefaultVisibility)
|
||||||
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
|
} else if global.Linkage() == llvm.LinkOnceODRLinkage {
|
||||||
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Options.PrintIR {
|
||||||
|
fmt.Println("; Generated LLVM IR:")
|
||||||
|
fmt.Println(mod.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all optimization passes, which are much more effective now
|
||||||
|
// that the optimizer can see the whole program at once.
|
||||||
|
err := optimizeProgram(mod, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure stack sizes are loaded from a separate section so they can be
|
// Make sure stack sizes are loaded from a separate section so they can be
|
||||||
// modified after linking.
|
// modified after linking.
|
||||||
if config.AutomaticStackSize() {
|
if config.AutomaticStackSize() {
|
||||||
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, programJob)
|
jobs = append(jobs, programJob)
|
||||||
|
@ -140,13 +348,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
// First add all jobs necessary to build this object file, then afterwards
|
// First add all jobs necessary to build this object file, then afterwards
|
||||||
// run all jobs in parallel as far as possible.
|
// run all jobs in parallel as far as possible.
|
||||||
|
|
||||||
// Create a temporary directory for intermediary files.
|
|
||||||
dir, err := ioutil.TempDir("", "tinygo")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// Add job to write the output object file.
|
// Add job to write the output object file.
|
||||||
objfile := filepath.Join(dir, "main.o")
|
objfile := filepath.Join(dir, "main.o")
|
||||||
outputObjectFileJob := &compileJob{
|
outputObjectFileJob := &compileJob{
|
||||||
|
@ -366,29 +567,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileWholeProgram compiles the entire *loader.Program to a LLVM module and
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// applies most necessary optimizations and transformations.
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
func compileWholeProgram(pkgName string, config *compileopts.Config, compilerConfig *compiler.Config, lprogram *loader.Program, machine llvm.TargetMachine) (llvm.Module, error) {
|
// optional and must be run as the compiler expects them to run.
|
||||||
// Compile AST to IR.
|
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||||
mod, errs := compiler.CompileProgram(lprogram, machine, compilerConfig, config.DumpSSA())
|
|
||||||
if errs != nil {
|
|
||||||
return mod, newMultiError(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Options.PrintIR {
|
|
||||||
fmt.Println("; Generated LLVM IR:")
|
|
||||||
fmt.Println(mod.String())
|
|
||||||
}
|
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
|
||||||
return mod, errors.New("verification error after IR construction")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := interp.Run(mod, config.DumpSSA())
|
err := interp.Run(mod, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mod, err
|
return err
|
||||||
}
|
}
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
return mod, errors.New("verification error after interpreting runtime.initAll")
|
return errors.New("verification error after interpreting runtime.initAll")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.GOOS() != "darwin" {
|
if config.GOOS() != "darwin" {
|
||||||
|
@ -403,13 +591,13 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
||||||
if config.WasmAbi() == "js" {
|
if config.WasmAbi() == "js" {
|
||||||
err := transform.ExternalInt64AsPtr(mod)
|
err := transform.ExternalInt64AsPtr(mod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mod, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization levels here are roughly the same as Clang, but probably not
|
// Optimization levels here are roughly the same as Clang, but probably not
|
||||||
// exactly.
|
// exactly.
|
||||||
errs = nil
|
var errs []error
|
||||||
switch config.Options.Opt {
|
switch config.Options.Opt {
|
||||||
case "none", "0":
|
case "none", "0":
|
||||||
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
||||||
|
@ -422,13 +610,13 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
||||||
case "z":
|
case "z":
|
||||||
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
|
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
|
||||||
default:
|
default:
|
||||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
return errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
||||||
}
|
}
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return mod, newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
return mod, errors.New("verification failure after LLVM optimization passes")
|
return errors.New("verification failure after LLVM optimization passes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
||||||
|
@ -442,7 +630,7 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
||||||
transform.DisableTailCalls(mod)
|
transform.DisableTailCalls(mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mod, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// functionStackSizes keeps stack size information about a single function
|
// functionStackSizes keeps stack size information about a single function
|
||||||
|
|
13
cgo/cgo.go
13
cgo/cgo.go
|
@ -42,6 +42,7 @@ type cgoPackage struct {
|
||||||
enums map[string]enumInfo
|
enums map[string]enumInfo
|
||||||
anonStructNum int
|
anonStructNum int
|
||||||
ldflags []string
|
ldflags []string
|
||||||
|
visitedFiles map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// constantInfo stores some information about a CGo constant found by libclang
|
// constantInfo stores some information about a CGo constant found by libclang
|
||||||
|
@ -156,9 +157,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. If there is one or more error, it returns these in the []error slice
|
// files, the LDFLAGS for this package, and a map of file hashes of the accessed
|
||||||
// but still modifies the AST.
|
// C header files. If there is one or more error, it returns these in the
|
||||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
|
// []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) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
|
@ -170,6 +172,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
typedefs: map[string]*typedefInfo{},
|
typedefs: map[string]*typedefInfo{},
|
||||||
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
||||||
enums: map[string]enumInfo{},
|
enums: map[string]enumInfo{},
|
||||||
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
|
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
|
||||||
|
@ -185,7 +188,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, []error{
|
return 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
|
||||||
|
@ -427,7 +430,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.errors
|
return p.generated, 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -4,6 +4,7 @@ package cgo
|
||||||
// modification. It does not touch the AST itself.
|
// modification. It does not touch the AST itself.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
|
@ -56,6 +57,7 @@ unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
|
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
@ -114,6 +116,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||||
}
|
}
|
||||||
defer C.clang_disposeTranslationUnit(unit)
|
defer C.clang_disposeTranslationUnit(unit)
|
||||||
|
|
||||||
|
// Report parser and type errors.
|
||||||
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
||||||
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
|
@ -134,10 +137,36 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract information required by CGo.
|
||||||
ref := storedRefs.Put(p)
|
ref := storedRefs.Put(p)
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
|
|
||||||
|
// Determine files read during CGo processing, for caching.
|
||||||
|
inclusionCallback := func(includedFile C.CXFile) {
|
||||||
|
// Get full file path.
|
||||||
|
path := getString(C.clang_getFileName(includedFile))
|
||||||
|
|
||||||
|
// Get contents of file (that should be in-memory).
|
||||||
|
size := C.size_t(0)
|
||||||
|
rawData := C.clang_getFileContents(unit, includedFile, &size)
|
||||||
|
if rawData == nil {
|
||||||
|
// Sanity check. This should (hopefully) never trigger.
|
||||||
|
panic("libclang: file contents was not loaded")
|
||||||
|
}
|
||||||
|
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||||
|
|
||||||
|
// Hash the contents if it isn't hashed yet.
|
||||||
|
if _, ok := p.visitedFiles[path]; !ok {
|
||||||
|
// already stored
|
||||||
|
sum := sha512.Sum512_224(data)
|
||||||
|
p.visitedFiles[path] = sum[:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||||
|
defer storedRefs.Remove(inclusionCallbackRef)
|
||||||
|
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
//export tinygo_clang_globals_visitor
|
||||||
|
@ -772,3 +801,9 @@ func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientDat
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export tinygo_clang_inclusion_visitor
|
||||||
|
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||||
|
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||||
|
callback(includedFile)
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,11 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
|
// package changes in a way that affects the generated LLVM module.
|
||||||
|
// This version is independent of the TinyGo version number.
|
||||||
|
const Version = 1
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
llvm.InitializeAllTargetMCs()
|
llvm.InitializeAllTargetMCs()
|
||||||
|
@ -242,15 +247,14 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompileProgram compiles the given package path or .go file path. Return an
|
// CompilePackage compiles a single package to a LLVM module.
|
||||||
// error when this fails (in any stage). If successful it returns the LLVM
|
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
// module. If not, one or more errors will be returned.
|
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||||
func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c := newCompilerContext("", machine, config, dumpSSA)
|
c.program = ssaPkg.Prog
|
||||||
|
|
||||||
c.program = lprogram.LoadSSA()
|
// Convert AST to SSA.
|
||||||
c.program.Build()
|
ssaPkg.Build()
|
||||||
c.runtimePkg = c.program.ImportedPackage("runtime").Pkg
|
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
|
@ -263,43 +267,17 @@ func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pkg := range lprogram.Sorted() {
|
// Load comments such as //go:extern on globals.
|
||||||
c.loadASTComments(pkg)
|
c.loadASTComments(pkg)
|
||||||
}
|
|
||||||
|
|
||||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
// Predeclare the runtime.alloc function, which is used by the wordpack
|
||||||
// functionality.
|
// functionality.
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
||||||
|
|
||||||
// Define all functions.
|
// Compile all functions, methods, and global variables in this package.
|
||||||
var initFuncs []llvm.Value
|
|
||||||
irbuilder := c.ctx.NewBuilder()
|
irbuilder := c.ctx.NewBuilder()
|
||||||
defer irbuilder.Dispose()
|
defer irbuilder.Dispose()
|
||||||
for _, pkg := range lprogram.Sorted() {
|
c.createPackage(irbuilder, ssaPkg)
|
||||||
ssaPkg := c.program.Package(pkg.Pkg)
|
|
||||||
c.createPackage(irbuilder, ssaPkg)
|
|
||||||
initFuncs = append(initFuncs, c.getFunction(ssaPkg.Members["init"].(*ssa.Function)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// After all packages are imported, add a synthetic initializer function
|
|
||||||
// that calls the initializer of each package.
|
|
||||||
initFn := c.program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)
|
|
||||||
llvmInitFn := c.getFunction(initFn)
|
|
||||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
|
||||||
llvmInitFn.SetUnnamedAddr(true)
|
|
||||||
if c.Debug {
|
|
||||||
difunc := c.attachDebugInfo(initFn)
|
|
||||||
pos := c.program.Fset.Position(initFn.Pos())
|
|
||||||
irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
|
||||||
}
|
|
||||||
llvmInitFn.Param(0).SetName("context")
|
|
||||||
llvmInitFn.Param(1).SetName("parentHandle")
|
|
||||||
block := c.ctx.AddBasicBlock(llvmInitFn, "entry")
|
|
||||||
irbuilder.SetInsertPointAtEnd(block)
|
|
||||||
for _, fn := range initFuncs {
|
|
||||||
irbuilder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "")
|
|
||||||
}
|
|
||||||
irbuilder.CreateRetVoid()
|
|
||||||
|
|
||||||
// see: https://reviews.llvm.org/D18355
|
// see: https://reviews.llvm.org/D18355
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
|
@ -320,22 +298,6 @@ func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.mod, c.diagnostics
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompilePackage compiles a single package to a LLVM module.
|
|
||||||
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
|
||||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
|
||||||
|
|
||||||
// Build SSA from AST.
|
|
||||||
ssaPkg := pkg.LoadSSA()
|
|
||||||
ssaPkg.Build()
|
|
||||||
|
|
||||||
// Compile all functions, methods, and global variables in this package.
|
|
||||||
irbuilder := c.ctx.NewBuilder()
|
|
||||||
defer irbuilder.Dispose()
|
|
||||||
c.createPackage(irbuilder, ssaPkg)
|
|
||||||
|
|
||||||
return c.mod, nil
|
return c.mod, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,8 +644,9 @@ func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
|
||||||
// debug info is added to the function.
|
// debug info is added to the function.
|
||||||
func (c *compilerContext) attachDebugInfoRaw(f *ssa.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
func (c *compilerContext) attachDebugInfoRaw(f *ssa.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
||||||
// Debug info for this function.
|
// Debug info for this function.
|
||||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
params := getParams(f.Signature)
|
||||||
for _, param := range f.Params {
|
diparams := make([]llvm.Metadata, 0, len(params))
|
||||||
|
for _, param := range params {
|
||||||
diparams = append(diparams, c.getDIType(param.Type()))
|
diparams = append(diparams, c.getDIType(param.Type()))
|
||||||
}
|
}
|
||||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||||
|
@ -792,7 +755,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||||
if !info.extern {
|
if !info.extern {
|
||||||
global := c.getGlobal(member)
|
global := c.getGlobal(member)
|
||||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,7 +778,7 @@ func (b *builder) createFunction() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !b.info.exported {
|
if !b.info.exported {
|
||||||
b.llvmFn.SetLinkage(llvm.InternalLinkage)
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,9 @@ func TestCompiler(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
|
program := lprogram.LoadSSA()
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
mod, errs := CompilePackage(testCase, pkg, machine, compilerConfig, false)
|
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
t.Log("error:", err)
|
t.Log("error:", err)
|
||||||
|
|
|
@ -352,7 +352,7 @@ func (b *builder) createRunDefers() {
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||||
for _, param := range callback.Params {
|
for _, param := range getParams(callback.Signature) {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||||
}
|
}
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||||
|
@ -361,7 +361,7 @@ func (b *builder) createRunDefers() {
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := range callback.Params {
|
for i := range getParams(callback.Signature) {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
||||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||||
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
||||||
funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage)
|
funcValueWithSignatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
}
|
}
|
||||||
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
|
@ -141,7 +141,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.
|
||||||
itfConcreteTypeGlobal = llvm.AddGlobal(b.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
itfConcreteTypeGlobal = llvm.AddGlobal(b.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
||||||
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
||||||
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
||||||
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
itfConcreteTypeGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
}
|
}
|
||||||
itfTypeCode := b.CreatePtrToInt(itfConcreteTypeGlobal, b.uintptrType, "")
|
itfTypeCode := b.CreatePtrToInt(itfConcreteTypeGlobal, b.uintptrType, "")
|
||||||
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
||||||
|
@ -80,7 +80,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
||||||
}
|
}
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
}
|
}
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||||
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
||||||
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
receiverType := c.getLLVMType(fn.Params[0].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 expandFormalParamType(receiverType, "", nil) {
|
||||||
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
||||||
|
@ -467,7 +467,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||||
wrapper.LastParam().SetName("parentHandle")
|
wrapper.LastParam().SetName("parentHandle")
|
||||||
|
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
|
|
||||||
// Create a new builder just to create this wrapper.
|
// Create a new builder just to create this wrapper.
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||||
}
|
}
|
||||||
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramInfos []paramInfo
|
var paramInfos []paramInfo
|
||||||
for _, param := range fn.Params {
|
for _, param := range getParams(fn.Signature) {
|
||||||
paramType := c.getLLVMType(param.Type())
|
paramType := c.getLLVMType(param.Type())
|
||||||
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
||||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||||
|
@ -183,7 +183,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||||
b := newBuilder(c, irbuilder, fn)
|
b := newBuilder(c, irbuilder, fn)
|
||||||
b.createFunction()
|
b.createFunction()
|
||||||
irbuilder.Dispose()
|
irbuilder.Dispose()
|
||||||
llvmFn.SetLinkage(llvm.InternalLinkage)
|
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
llvmFn.SetUnnamedAddr(true)
|
llvmFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +293,20 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getParams returns the function parameters, including the receiver at the
|
||||||
|
// start. This is an alternative to the Params member of *ssa.Function, which is
|
||||||
|
// not yet populated when the package has not yet been built.
|
||||||
|
func getParams(sig *types.Signature) []*types.Var {
|
||||||
|
params := []*types.Var{}
|
||||||
|
if sig.Recv() != nil {
|
||||||
|
params = append(params, sig.Recv())
|
||||||
|
}
|
||||||
|
for i := 0; i < sig.Params().Len(); i++ {
|
||||||
|
params = append(params, sig.Params().At(i))
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
// globalInfo contains some information about a specific global. By default,
|
// globalInfo contains some information about a specific global. By default,
|
||||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||||
// some symbols this is different (due to //go:extern for example).
|
// some symbols this is different (due to //go:extern for example).
|
||||||
|
|
30
compiler/testdata/basic.ll
предоставленный
30
compiler/testdata/basic.ll
предоставленный
|
@ -3,70 +3,72 @@ source_filename = "basic.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = add i32 %x, %y
|
%0 = add i32 %x, %y
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %x, %y
|
%0 = icmp eq i32 %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oeq float %x, %y
|
%0 = fcmp oeq float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp une float %x, %y
|
%0 = fcmp une float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt float %x, %y
|
%0 = fcmp olt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ole float %x, %y
|
%0 = fcmp ole float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ogt float %x, %y
|
%0 = fcmp ogt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oge float %x, %y
|
%0 = fcmp oge float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.r
|
ret float %x.r
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.i
|
ret float %x.i
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fadd float %x.r, %y.r
|
%0 = fadd float %x.r, %y.r
|
||||||
%1 = fadd float %x.i, %y.i
|
%1 = fadd float %x.i, %y.i
|
||||||
|
@ -75,7 +77,7 @@ entry:
|
||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fsub float %x.r, %y.r
|
%0 = fsub float %x.r, %y.r
|
||||||
%1 = fsub float %x.i, %y.i
|
%1 = fsub float %x.i, %y.i
|
||||||
|
@ -84,7 +86,7 @@ entry:
|
||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fmul float %x.r, %y.r
|
%0 = fmul float %x.r, %y.r
|
||||||
%1 = fmul float %x.i, %y.i
|
%1 = fmul float %x.i, %y.i
|
||||||
|
|
20
compiler/testdata/float.ll
предоставленный
20
compiler/testdata/float.ll
предоставленный
|
@ -3,12 +3,14 @@ source_filename = "float.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
|
@ -19,22 +21,22 @@ entry:
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret float 0x41F0000000000000
|
ret float 0x41F0000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i32 -1
|
ret i32 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = uitofp i32 %v to float
|
%0 = uitofp i32 %v to float
|
||||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||||
|
@ -43,7 +45,7 @@ entry:
|
||||||
ret i32 %1
|
ret i32 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
|
@ -55,7 +57,7 @@ entry:
|
||||||
ret float %1
|
ret float %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||||
|
@ -66,7 +68,7 @@ entry:
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||||
|
|
18
compiler/testdata/pointer.ll
предоставленный
18
compiler/testdata/pointer.ll
предоставленный
|
@ -3,46 +3,48 @@ source_filename = "pointer.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret [0 x i32] zeroinitializer
|
ret [0 x i32] zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i8* %x to i32*
|
%0 = bitcast i8* %x to i32*
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %x to i8*
|
%0 = bitcast i32* %x to i8*
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i8* %x
|
ret i8* %x
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
|
|
8
compiler/testdata/slice.ll
предоставленный
8
compiler/testdata/slice.ll
предоставленный
|
@ -3,17 +3,19 @@ source_filename = "slice.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.len
|
ret i32 %ints.len
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.cap
|
ret i32 %ints.cap
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -12,5 +12,5 @@ require (
|
||||||
go.bug.st/serial v1.1.2
|
go.bug.st/serial v1.1.2
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -57,5 +57,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f h1:FP5Do5omlQ/dLQ3Hfy7oyJo69VS5Hn46rZw004r0lGU=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
|
|
|
@ -2,6 +2,7 @@ package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha512"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -66,10 +68,12 @@ type PackageJSON struct {
|
||||||
type Package struct {
|
type Package struct {
|
||||||
PackageJSON
|
PackageJSON
|
||||||
|
|
||||||
program *Program
|
program *Program
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
Pkg *types.Package
|
FileHashes map[string][]byte
|
||||||
info types.Info
|
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||||
|
Pkg *types.Package
|
||||||
|
info types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
|
@ -118,7 +122,8 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||||
decoder := json.NewDecoder(buf)
|
decoder := json.NewDecoder(buf)
|
||||||
for {
|
for {
|
||||||
pkg := &Package{
|
pkg := &Package{
|
||||||
program: p,
|
program: p,
|
||||||
|
FileHashes: make(map[string][]byte),
|
||||||
info: types.Info{
|
info: types.Info{
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
|
@ -277,17 +282,15 @@ func (p *Program) Parse() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseFile is a wrapper around parser.ParseFile.
|
// parseFile is a wrapper around parser.ParseFile.
|
||||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
func (p *Package) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||||
if p.fset == nil {
|
originalPath := p.program.getOriginalPath(path)
|
||||||
p.fset = token.NewFileSet()
|
data, err := ioutil.ReadFile(path)
|
||||||
}
|
|
||||||
|
|
||||||
rd, err := os.Open(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rd.Close()
|
sum := sha512.Sum512_224(data)
|
||||||
return parser.ParseFile(p.fset, p.getOriginalPath(path), rd, mode)
|
p.FileHashes[originalPath] = sum[:]
|
||||||
|
return parser.ParseFile(p.program.fset, originalPath, data, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses and typechecks this package.
|
// Parse parses and typechecks this package.
|
||||||
|
@ -363,7 +366,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||||
if !filepath.IsAbs(file) {
|
if !filepath.IsAbs(file) {
|
||||||
file = filepath.Join(p.Dir, file)
|
file = filepath.Join(p.Dir, file)
|
||||||
}
|
}
|
||||||
f, err := p.program.parseFile(file, parser.ParseComments)
|
f, err := p.parseFile(file, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
return
|
return
|
||||||
|
@ -385,7 +388,11 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||||
if p.program.clangHeaders != "" {
|
if p.program.clangHeaders != "" {
|
||||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
||||||
}
|
}
|
||||||
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
p.CFlags = cflags
|
||||||
|
generated, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
||||||
|
for path, hash := range accessedFiles {
|
||||||
|
p.FileHashes[path] = hash
|
||||||
|
}
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,22 +77,9 @@ func Current() *Task
|
||||||
// This is implemented inside the compiler.
|
// This is implemented inside the compiler.
|
||||||
func Pause()
|
func Pause()
|
||||||
|
|
||||||
type taskHolder interface {
|
|
||||||
setState(*rawState) *rawState
|
|
||||||
returnTo(*rawState)
|
|
||||||
returnCurrent()
|
|
||||||
setReturnPtr(unsafe.Pointer)
|
|
||||||
getReturnPtr() unsafe.Pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no direct references to the task methods, they will not be discovered by the compiler, and this will trigger a compiler error.
|
|
||||||
// Instantiating this interface forces discovery of these methods.
|
|
||||||
var _ = taskHolder((*Task)(nil))
|
|
||||||
|
|
||||||
func fake() {
|
func fake() {
|
||||||
// Hack to ensure intrinsics are discovered.
|
// Hack to ensure intrinsics are discovered.
|
||||||
Current()
|
Current()
|
||||||
go func() {}()
|
|
||||||
Pause()
|
Pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,11 +173,12 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
// Only the name of the global is relevant, the object itself is
|
// Only the name of the global is relevant, the object itself is
|
||||||
// discarded afterwards.
|
// discarded afterwards.
|
||||||
name := global.Name()
|
name := global.Name()
|
||||||
t := &typeInfo{
|
if _, ok := p.types[name]; !ok {
|
||||||
name: name,
|
p.types[name] = &typeInfo{
|
||||||
typecode: global,
|
name: name,
|
||||||
|
typecode: global,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.types[name] = t
|
|
||||||
case typeInInterfacePtr:
|
case typeInInterfacePtr:
|
||||||
// Count per type how often it is put in an interface. Also, collect
|
// Count per type how often it is put in an interface. Also, collect
|
||||||
// all methods this type has (if it is named).
|
// all methods this type has (if it is named).
|
||||||
|
@ -185,7 +186,15 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
initializer := global.Initializer()
|
initializer := global.Initializer()
|
||||||
typecode := llvm.ConstExtractValue(initializer, []uint32{0})
|
typecode := llvm.ConstExtractValue(initializer, []uint32{0})
|
||||||
methodSet := llvm.ConstExtractValue(initializer, []uint32{1})
|
methodSet := llvm.ConstExtractValue(initializer, []uint32{1})
|
||||||
t := p.types[typecode.Name()]
|
typecodeName := typecode.Name()
|
||||||
|
t := p.types[typecodeName]
|
||||||
|
if t == nil {
|
||||||
|
t = &typeInfo{
|
||||||
|
name: typecodeName,
|
||||||
|
typecode: typecode,
|
||||||
|
}
|
||||||
|
p.types[typecodeName] = t
|
||||||
|
}
|
||||||
p.addTypeMethods(t, methodSet)
|
p.addTypeMethods(t, methodSet)
|
||||||
|
|
||||||
// Count the number of MakeInterface instructions, for sorting the
|
// Count the number of MakeInterface instructions, for sorting the
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче