diff options
author | Ayke van Laethem <[email protected]> | 2019-05-13 16:29:57 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2019-05-14 09:59:00 +0200 |
commit | 17c42810d095a3b7c3eeaa6c123dc89a5b8c82f1 (patch) | |
tree | d114a052e6db402b8d76ae8b3372183f4fce1ec8 /compiler/llvm.go | |
parent | 064d00155023e47caaa25529eda0dbf3b39b63f7 (diff) | |
download | tinygo-17c42810d095a3b7c3eeaa6c123dc89a5b8c82f1.tar.gz tinygo-17c42810d095a3b7c3eeaa6c123dc89a5b8c82f1.zip |
compiler: improve hashmaps by avoiding dynamic allocas
By moving all allocas used in hashmap operations to the entry block, the
stack frame remains at a fixed size known at compile time. This avoids
stack overflows when doing map operations in loops and in general
improves code quality: the compiled size of testdata/map.go went from
3776 to 3632 in .text size.
Diffstat (limited to 'compiler/llvm.go')
-rw-r--r-- | compiler/llvm.go | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/compiler/llvm.go b/compiler/llvm.go index 77fc9c44c..a49573456 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -22,6 +22,36 @@ func getUses(value llvm.Value) []llvm.Value { return uses } +// createEntryBlockAlloca creates a new alloca in the entry block, even though +// the IR builder is located elsewhere. It assumes that the insert point is +// after the last instruction in the current block. Also, it adds lifetime +// information to the IR signalling that the alloca won't be used before this +// point. +// +// This is useful for creating temporary allocas for intrinsics. Don't forget to +// end the lifetime after you're done with it. +func (c *Compiler) createEntryBlockAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { + currentBlock := c.builder.GetInsertBlock() + c.builder.SetInsertPointBefore(currentBlock.Parent().EntryBasicBlock().FirstInstruction()) + alloca = c.builder.CreateAlloca(t, name) + c.builder.SetInsertPointAtEnd(currentBlock) + bitcast = c.builder.CreateBitCast(alloca, c.i8ptrType, name+".bitcast") + size = llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(t), false) + c.builder.CreateCall(c.getLifetimeStartFunc(), []llvm.Value{size, bitcast}, "") + return +} + +// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it +// first if it doesn't exist yet. +func (c *Compiler) getLifetimeStartFunc() llvm.Value { + fn := c.mod.NamedFunction("llvm.lifetime.start.p0i8") + if fn.IsNil() { + fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false) + fn = llvm.AddFunction(c.mod, "llvm.lifetime.start.p0i8", fnType) + } + return fn +} + // getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it // first if it doesn't exist yet. func (c *Compiler) getLifetimeEndFunc() llvm.Value { |