diff options
author | Ayke van Laethem <[email protected]> | 2022-04-19 15:31:23 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2022-05-30 07:53:28 +0200 |
commit | 777d3f3ea5a570615b615e323c977f73d6674507 (patch) | |
tree | ac5bf4b4e169f37b81cec5dfcddfc6e1b0814899 | |
parent | ea3b5dc68979da59e6a62ed64bce7369ad1f0e22 (diff) | |
download | tinygo-777d3f3ea5a570615b615e323c977f73d6674507.tar.gz tinygo-777d3f3ea5a570615b615e323c977f73d6674507.zip |
builder: free LLVM objects after use
This reduces the TinyGo memory consumption when running
make tinygo-test
from 5.8GB to around 2GB on my laptop.
-rw-r--r-- | builder/build.go | 11 | ||||
-rw-r--r-- | compiler/compiler.go | 1 | ||||
-rw-r--r-- | compiler/compiler_test.go | 1 | ||||
-rw-r--r-- | compiler/llvmutil/llvm.go | 2 | ||||
-rw-r--r-- | compiler/llvmutil/wordpack.go | 6 | ||||
-rw-r--r-- | interp/interp.go | 8 | ||||
-rw-r--r-- | interp/interp_test.go | 2 | ||||
-rw-r--r-- | transform/allocs.go | 2 | ||||
-rw-r--r-- | transform/gc.go | 3 | ||||
-rw-r--r-- | transform/interface-lowering.go | 6 | ||||
-rw-r--r-- | transform/reflect.go | 6 | ||||
-rw-r--r-- | transform/rtcalls.go | 4 | ||||
-rw-r--r-- | transform/transform_test.go | 3 |
13 files changed, 49 insertions, 6 deletions
diff --git a/builder/build.go b/builder/build.go index 77a4e1647..629121398 100644 --- a/builder/build.go +++ b/builder/build.go @@ -188,6 +188,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil if err != nil { return err } + defer machine.Dispose() // Load entire program AST into memory. lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{ @@ -287,6 +288,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil // 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()) + defer mod.Context().Dispose() + defer mod.Dispose() if errs != nil { return newMultiError(errs) } @@ -432,6 +435,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil // Add job that links and optimizes all packages together. var mod llvm.Module + defer func() { + if !mod.IsNil() { + ctx := mod.Context() + mod.Dispose() + ctx.Dispose() + } + }() var stackSizeLoads []string programJob := &compileJob{ description: "link+optimize packages (LTO)", @@ -534,6 +544,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil if err != nil { return err } + defer llvmBuf.Dispose() return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666) case ".bc": var buf llvm.MemoryBuffer diff --git a/compiler/compiler.go b/compiler/compiler.go index dd7a8ea30..f316c5912 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -301,6 +301,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, }), ) c.dibuilder.Finalize() + c.dibuilder.Destroy() } return c.mod, c.diagnostics diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index f88a4a331..199c71200 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -106,6 +106,7 @@ func TestCompiler(t *testing.T) { if err != nil { t.Fatal("failed to create target machine:", err) } + defer machine.Dispose() // Load entire program AST into memory. lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{ diff --git a/compiler/llvmutil/llvm.go b/compiler/llvmutil/llvm.go index 21bc6c67f..74404a3e9 100644 --- a/compiler/llvmutil/llvm.go +++ b/compiler/llvmutil/llvm.go @@ -34,6 +34,7 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { ctx := t.Context() targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) alloca = CreateEntryBlockAlloca(builder, t, name) bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast") @@ -46,6 +47,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value { ctx := mod.Context() targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) alloca := CreateEntryBlockAlloca(builder, t, name) diff --git a/compiler/llvmutil/wordpack.go b/compiler/llvmutil/wordpack.go index 541a20d8a..41d4c581d 100644 --- a/compiler/llvmutil/wordpack.go +++ b/compiler/llvmutil/wordpack.go @@ -15,8 +15,9 @@ import ( func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value { ctx := mod.Context() targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) - uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8) + uintptrType := ctx.IntType(targetData.PointerSize() * 8) valueTypes := make([]llvm.Type, len(values)) for i, value := range values { @@ -127,8 +128,9 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { ctx := mod.Context() targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) - uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8) + uintptrType := ctx.IntType(targetData.PointerSize() * 8) packedType := ctx.StructType(valueTypes, false) diff --git a/interp/interp.go b/interp/interp.go index 58de4a3b6..87aadf37e 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -50,10 +50,17 @@ func newRunner(mod llvm.Module, debug bool) *runner { return &r } +// Dispose deallocates all alloated LLVM resources. +func (r *runner) dispose() { + r.targetData.Dispose() + r.targetData = llvm.TargetData{} +} + // Run evaluates runtime.initAll function as much as possible at compile time. // Set debug to true if it should print output while running. func Run(mod llvm.Module, debug bool) error { r := newRunner(mod, debug) + defer r.dispose() initAll := mod.NamedFunction("runtime.initAll") bb := initAll.EntryBasicBlock() @@ -196,6 +203,7 @@ func RunFunc(fn llvm.Value, debug bool) error { // Create and initialize *runner object. mod := fn.GlobalParent() r := newRunner(mod, debug) + defer r.dispose() initName := fn.Name() if !strings.HasSuffix(initName, ".init") { return errorAt(fn, "interp: unexpected function name (expected *.init)") diff --git a/interp/interp_test.go b/interp/interp_test.go index a1bea65ef..7b2bb6197 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -40,6 +40,7 @@ func TestInterp(t *testing.T) { func runTest(t *testing.T, pathPrefix string) { // Read the input IR. ctx := llvm.NewContext() + defer ctx.Dispose() buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll") os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching if err != nil { @@ -49,6 +50,7 @@ func runTest(t *testing.T, pathPrefix string) { if err != nil { t.Fatalf("could not load module:\n%v", err) } + defer mod.Dispose() // Perform the transform. err = Run(mod, false) diff --git a/transform/allocs.go b/transform/allocs.go index 35fa1b0bb..af7c70b28 100644 --- a/transform/allocs.go +++ b/transform/allocs.go @@ -36,8 +36,10 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, logger func(tok } targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) builder := mod.Context().NewBuilder() + defer builder.Dispose() for _, heapalloc := range getUses(allocator) { logAllocs := printAllocs != nil && printAllocs.MatchString(heapalloc.InstructionParent().Parent().Name()) diff --git a/transform/gc.go b/transform/gc.go index 190760ace..c8326f699 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -33,7 +33,9 @@ func MakeGCStackSlots(mod llvm.Module) bool { ctx := mod.Context() builder := ctx.NewBuilder() + defer builder.Dispose() targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() uintptrType := ctx.IntType(targetData.PointerSize() * 8) // Look at *all* functions to see whether they are free of function pointer @@ -326,6 +328,7 @@ func AddGlobalsBitmap(mod llvm.Module) bool { ctx := mod.Context() targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() uintptrType := ctx.IntType(targetData.PointerSize() * 8) // Collect all globals that contain pointers (and thus must be scanned by diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index d2d4b4f48..7a01c2ea7 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -101,19 +101,23 @@ type lowerInterfacesPass struct { // before LLVM can work on them. This is done so that a few cleanup passes can // run before assigning the final type codes. func LowerInterfaces(mod llvm.Module, config *compileopts.Config) error { + targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() p := &lowerInterfacesPass{ mod: mod, config: config, builder: mod.Context().NewBuilder(), ctx: mod.Context(), - uintptrType: mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8), + uintptrType: mod.Context().IntType(targetData.PointerSize() * 8), types: make(map[string]*typeInfo), signatures: make(map[string]*signatureInfo), interfaces: make(map[string]*interfaceInfo), } + defer p.builder.Dispose() if config.Debug() { p.dibuilder = llvm.NewDIBuilder(mod) + defer p.dibuilder.Destroy() defer p.dibuilder.Finalize() p.difiles = make(map[string]llvm.Metadata) } diff --git a/transform/reflect.go b/transform/reflect.go index 188b0a3c1..49bd449ac 100644 --- a/transform/reflect.go +++ b/transform/reflect.go @@ -161,10 +161,12 @@ func LowerReflect(mod llvm.Module) { }) // Assign typecodes the way the reflect package expects. - uintptrType := mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8) + targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() + uintptrType := mod.Context().IntType(targetData.PointerSize() * 8) state := typeCodeAssignmentState{ fallbackIndex: 1, - uintptrLen: llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8, + uintptrLen: targetData.PointerSize() * 8, namedBasicTypes: make(map[string]int), namedNonBasicTypes: make(map[string]int), arrayTypes: make(map[string]int), diff --git a/transform/rtcalls.go b/transform/rtcalls.go index 49f7f0d94..6cd5cc4c9 100644 --- a/transform/rtcalls.go +++ b/transform/rtcalls.go @@ -114,7 +114,9 @@ func OptimizeReflectImplements(mod llvm.Module) { defer builder.Dispose() // Get a few useful object for use later. - uintptrType := mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8) + targetData := llvm.NewTargetData(mod.DataLayout()) + defer targetData.Dispose() + uintptrType := mod.Context().IntType(targetData.PointerSize() * 8) // Look up the (reflect.Value).Implements() method. var implementsFunc llvm.Value diff --git a/transform/transform_test.go b/transform/transform_test.go index bde1351ff..9ad724346 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -32,6 +32,7 @@ var defaultTestConfig = &compileopts.Config{ func testTransform(t *testing.T, pathPrefix string, transform func(mod llvm.Module)) { // Read the input IR. ctx := llvm.NewContext() + defer ctx.Dispose() buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll") os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching if err != nil { @@ -41,6 +42,7 @@ func testTransform(t *testing.T, pathPrefix string, transform func(mod llvm.Modu if err != nil { t.Fatalf("could not load module:\n%v", err) } + defer mod.Dispose() // Perform the transform. transform(mod) @@ -141,6 +143,7 @@ func compileGoFileForTesting(t *testing.T, filename string) llvm.Module { if err != nil { t.Fatal("failed to create target machine:", err) } + defer machine.Dispose() // Load entire program AST into memory. lprogram, err := loader.Load(config, []string{filename}, config.ClangHeaders, types.Config{ |