aboutsummaryrefslogtreecommitdiffhomepage
path: root/compiler/llvm.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-05-13 16:29:57 +0200
committerRon Evans <[email protected]>2019-05-14 09:59:00 +0200
commit17c42810d095a3b7c3eeaa6c123dc89a5b8c82f1 (patch)
treed114a052e6db402b8d76ae8b3372183f4fce1ec8 /compiler/llvm.go
parent064d00155023e47caaa25529eda0dbf3b39b63f7 (diff)
downloadtinygo-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.go30
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 {