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.
Этот коммит содержится в:
Ayke van Laethem 2021-01-31 20:52:18 +01:00 коммит произвёл Ron Evans
родитель dc1ff80e10
коммит e2f532709f
21 изменённых файлов: 404 добавлений и 189 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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 предоставленный
Просмотреть файл

@ -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 предоставленный
Просмотреть файл

@ -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 предоставленный
Просмотреть файл

@ -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 предоставленный
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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