aboutsummaryrefslogtreecommitdiffhomepage
path: root/cgo
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-01-31 20:52:18 +0100
committerRon Evans <[email protected]>2021-03-21 11:51:35 +0100
commite2f532709f97b37575d4216c9605323221b63b93 (patch)
treefae98b6b8ba8870b5b20d323184adf31fd8d90b0 /cgo
parentdc1ff80e10b8d4ec414d7a7491477f1768917ddc (diff)
downloadtinygo-e2f532709f97b37575d4216c9605323221b63b93.tar.gz
tinygo-e2f532709f97b37575d4216c9605323221b63b93.zip
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.
Diffstat (limited to 'cgo')
-rw-r--r--cgo/cgo.go13
-rw-r--r--cgo/cgo_test.go2
-rw-r--r--cgo/libclang.go35
3 files changed, 44 insertions, 6 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go
index 73d26e1d8..c496cfef2 100644
--- a/cgo/cgo.go
+++ b/cgo/cgo.go
@@ -42,6 +42,7 @@ type cgoPackage struct {
enums map[string]enumInfo
anonStructNum int
ldflags []string
+ visitedFiles map[string][]byte
}
// 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
// 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
-// files. If there is one or more error, it returns these in the []error slice
-// but still modifies the AST.
-func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
+// files, the LDFLAGS for this package, and a map of file hashes of the accessed
+// C header files. If there is one or more error, it returns these in the
+// []error slice but still modifies the AST.
+func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, map[string][]byte, []error) {
p := &cgoPackage{
dir: dir,
fset: fset,
@@ -170,6 +172,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
typedefs: map[string]*typedefInfo{},
elaboratedTypes: map[string]*elaboratedTypeInfo{},
enums: map[string]enumInfo{},
+ visitedFiles: map[string][]byte{},
}
// 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.
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
if err != nil {
- return nil, nil, []error{
+ return nil, nil, nil, []error{
scanner.Error{
Pos: fset.Position(files[0].Pos()),
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.
//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
diff --git a/cgo/cgo_test.go b/cgo/cgo_test.go
index e0fbc6335..33a49b963 100644
--- a/cgo/cgo_test.go
+++ b/cgo/cgo_test.go
@@ -65,7 +65,7 @@ func TestCGo(t *testing.T) {
}
// 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.
var typecheckErrors []error
diff --git a/cgo/libclang.go b/cgo/libclang.go
index f1ba3baa1..8e9af8a08 100644
--- a/cgo/libclang.go
+++ b/cgo/libclang.go
@@ -4,6 +4,7 @@ package cgo
// modification. It does not touch the AST itself.
import (
+ "crypto/sha512"
"fmt"
"go/ast"
"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_struct_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"
@@ -114,6 +116,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
}
defer C.clang_disposeTranslationUnit(unit)
+ // Report parser and type errors.
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
addDiagnostic := func(diagnostic C.CXDiagnostic) {
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)
defer storedRefs.Remove(ref)
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
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
@@ -772,3 +801,9 @@ func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientDat
}
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)
+}