aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2022-04-19 15:31:23 +0200
committerRon Evans <[email protected]>2022-05-30 07:53:28 +0200
commit777d3f3ea5a570615b615e323c977f73d6674507 (patch)
treeac5bf4b4e169f37b81cec5dfcddfc6e1b0814899
parentea3b5dc68979da59e6a62ed64bce7369ad1f0e22 (diff)
downloadtinygo-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.go11
-rw-r--r--compiler/compiler.go1
-rw-r--r--compiler/compiler_test.go1
-rw-r--r--compiler/llvmutil/llvm.go2
-rw-r--r--compiler/llvmutil/wordpack.go6
-rw-r--r--interp/interp.go8
-rw-r--r--interp/interp_test.go2
-rw-r--r--transform/allocs.go2
-rw-r--r--transform/gc.go3
-rw-r--r--transform/interface-lowering.go6
-rw-r--r--transform/reflect.go6
-rw-r--r--transform/rtcalls.go4
-rw-r--r--transform/transform_test.go3
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{