diff --git a/builder/sizes_test.go b/builder/sizes_test.go index 5717c769c..4d80a1b79 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -43,7 +43,7 @@ func TestBinarySize(t *testing.T) { tests := []sizeTest{ // microcontrollers {"hifive1b", "examples/echo", 3817, 299, 0, 2252}, - {"microbit", "examples/serial", 2816, 356, 8, 2248}, + {"microbit", "examples/serial", 2820, 356, 8, 2248}, {"wioterminal", "examples/pininterrupt", 7206, 1510, 120, 7248}, // TODO: also check wasm. Right now this is difficult, because diff --git a/compiler/asserts.go b/compiler/asserts.go index 8296cca52..7e3a8b150 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -241,25 +241,35 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc } } - // Put the fault block at the end of the function and the next block at the - // current insert position. - faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw") + faultBlock := b.getRuntimeAssertBlock(blockPrefix, assertFunc) nextBlock := b.insertBasicBlock(blockPrefix + ".next") b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes // Now branch to the out-of-bounds or the regular block. b.CreateCondBr(assert, faultBlock, nextBlock) - // Fail: the assert triggered so panic. - b.SetInsertPointAtEnd(faultBlock) + // Ok: assert didn't trigger so continue normally. + b.SetInsertPointAtEnd(nextBlock) +} + +func (b *builder) getRuntimeAssertBlock(blockPrefix, assertFunc string) llvm.BasicBlock { + if b.runtimeAssertBlocks == nil { + b.runtimeAssertBlocks = make(map[string]llvm.BasicBlock) + } + if block := b.runtimeAssertBlocks[assertFunc]; !block.IsNil() { + return block + } + savedBlock := b.GetInsertBlock() + block := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw") + b.runtimeAssertBlocks[assertFunc] = block + b.SetInsertPointAtEnd(block) if b.hasDeferFrame() { b.createFaultCheckpoint() } b.createRuntimeCall(assertFunc, nil, "") b.CreateUnreachable() - - // Ok: assert didn't trigger so continue normally. - b.SetInsertPointAtEnd(nextBlock) + b.SetInsertPointAtEnd(savedBlock) + return block } // extendInteger extends the value to at least targetType using a zero or sign diff --git a/compiler/compiler.go b/compiler/compiler.go index ebb7bf223..c08687e8e 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -176,6 +176,9 @@ type builder struct { deferBuiltinFuncs map[ssa.Value]deferBuiltin runDefersBlock []llvm.BasicBlock afterDefersBlock []llvm.BasicBlock + + runtimeAssertBlocks map[string]llvm.BasicBlock + interfaceAssertBlock llvm.BasicBlock } func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder { diff --git a/compiler/interface.go b/compiler/interface.go index a589b0d82..1ee478bf5 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -828,18 +828,10 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value { return tuple } else { // Type assert without comma-ok. If it fails, panic. - faultBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.throw") + faultBlock := b.getInterfaceAssertBlock() b.currentBlockInfo.exit = okBlock b.CreateCondBr(commaOk, okBlock, faultBlock) - // Fault: emit a checkpoint (for recover) and panic. - b.SetInsertPointAtEnd(faultBlock) - if b.hasDeferFrame() { - b.createFaultCheckpoint() - } - b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "") - b.CreateUnreachable() - // OK: extract the value from the interface. b.SetInsertPointAtEnd(okBlock) if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { @@ -849,6 +841,23 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value { } } +func (b *builder) getInterfaceAssertBlock() llvm.BasicBlock { + if !b.interfaceAssertBlock.IsNil() { + return b.interfaceAssertBlock + } + savedBlock := b.GetInsertBlock() + block := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.throw") + b.interfaceAssertBlock = block + b.SetInsertPointAtEnd(block) + if b.hasDeferFrame() { + b.createFaultCheckpoint() + } + b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "") + b.CreateUnreachable() + b.SetInsertPointAtEnd(savedBlock) + return block +} + // getMethodsString returns a string to be used in the "tinygo-methods" string // attribute for interface functions. func (c *compilerContext) getMethodsString(itf *types.Interface) string { diff --git a/compiler/testdata/generics.ll b/compiler/testdata/generics.ll index de682b76c..2a3fcd0ab 100644 --- a/compiler/testdata/generics.ll +++ b/compiler/testdata/generics.ll @@ -29,13 +29,13 @@ entry: %a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3 store float %a.X, ptr %a, align 4 - %a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4 - store float %a.Y, ptr %a.repack9, align 4 + %a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4 + store float %a.Y, ptr %a.repack5, align 4 %b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3 store float %b.X, ptr %b, align 4 - %b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4 - store float %b.Y, ptr %b.repack11, align 4 + %b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4 + store float %b.Y, ptr %b.repack7, align 4 call void @main.checkSize(i32 4, ptr undef) #3 call void @main.checkSize(i32 8, ptr undef) #3 %complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 @@ -43,29 +43,29 @@ entry: br i1 false, label %deref.throw, label %deref.next deref.next: ; preds = %entry - br i1 false, label %deref.throw1, label %deref.next2 + br i1 false, label %deref.throw, label %deref.next1 -deref.next2: ; preds = %deref.next +deref.next1: ; preds = %deref.next %0 = load float, ptr %a, align 4 %1 = load float, ptr %b, align 4 %2 = fadd float %0, %1 - br i1 false, label %deref.throw3, label %deref.next4 + br i1 false, label %deref.throw, label %deref.next2 -deref.next4: ; preds = %deref.next2 - br i1 false, label %deref.throw5, label %deref.next6 +deref.next2: ; preds = %deref.next1 + br i1 false, label %deref.throw, label %deref.next3 -deref.next6: ; preds = %deref.next4 +deref.next3: ; preds = %deref.next2 %3 = getelementptr inbounds nuw i8, ptr %b, i32 4 %4 = getelementptr inbounds nuw i8, ptr %a, i32 4 %5 = load float, ptr %4, align 4 %6 = load float, ptr %3, align 4 - br i1 false, label %store.throw, label %store.next + br i1 false, label %deref.throw, label %store.next -store.next: ; preds = %deref.next6 +store.next: ; preds = %deref.next3 store float %2, ptr %complit, align 4 - br i1 false, label %store.throw7, label %store.next8 + br i1 false, label %deref.throw, label %store.next4 -store.next8: ; preds = %store.next +store.next4: ; preds = %store.next %7 = getelementptr inbounds nuw i8, ptr %complit, i32 4 %8 = fadd float %5, %6 store float %8, ptr %7, align 4 @@ -74,22 +74,7 @@ store.next8: ; preds = %store.next %10 = insertvalue %"main.Point[float32]" %9, float %8, 1 ret %"main.Point[float32]" %10 -deref.throw: ; preds = %entry - unreachable - -deref.throw1: ; preds = %deref.next - unreachable - -deref.throw3: ; preds = %deref.next2 - unreachable - -deref.throw5: ; preds = %deref.next4 - unreachable - -store.throw: ; preds = %deref.next6 - unreachable - -store.throw7: ; preds = %store.next +deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry unreachable } @@ -107,13 +92,13 @@ entry: %a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3 store i32 %a.X, ptr %a, align 4 - %a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4 - store i32 %a.Y, ptr %a.repack9, align 4 + %a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4 + store i32 %a.Y, ptr %a.repack5, align 4 %b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3 store i32 %b.X, ptr %b, align 4 - %b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4 - store i32 %b.Y, ptr %b.repack11, align 4 + %b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4 + store i32 %b.Y, ptr %b.repack7, align 4 call void @main.checkSize(i32 4, ptr undef) #3 call void @main.checkSize(i32 8, ptr undef) #3 %complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 @@ -121,29 +106,29 @@ entry: br i1 false, label %deref.throw, label %deref.next deref.next: ; preds = %entry - br i1 false, label %deref.throw1, label %deref.next2 + br i1 false, label %deref.throw, label %deref.next1 -deref.next2: ; preds = %deref.next +deref.next1: ; preds = %deref.next %0 = load i32, ptr %a, align 4 %1 = load i32, ptr %b, align 4 %2 = add i32 %0, %1 - br i1 false, label %deref.throw3, label %deref.next4 + br i1 false, label %deref.throw, label %deref.next2 -deref.next4: ; preds = %deref.next2 - br i1 false, label %deref.throw5, label %deref.next6 +deref.next2: ; preds = %deref.next1 + br i1 false, label %deref.throw, label %deref.next3 -deref.next6: ; preds = %deref.next4 +deref.next3: ; preds = %deref.next2 %3 = getelementptr inbounds nuw i8, ptr %b, i32 4 %4 = getelementptr inbounds nuw i8, ptr %a, i32 4 %5 = load i32, ptr %4, align 4 %6 = load i32, ptr %3, align 4 - br i1 false, label %store.throw, label %store.next + br i1 false, label %deref.throw, label %store.next -store.next: ; preds = %deref.next6 +store.next: ; preds = %deref.next3 store i32 %2, ptr %complit, align 4 - br i1 false, label %store.throw7, label %store.next8 + br i1 false, label %deref.throw, label %store.next4 -store.next8: ; preds = %store.next +store.next4: ; preds = %store.next %7 = getelementptr inbounds nuw i8, ptr %complit, i32 4 %8 = add i32 %5, %6 store i32 %8, ptr %7, align 4 @@ -152,22 +137,7 @@ store.next8: ; preds = %store.next %10 = insertvalue %"main.Point[int]" %9, i32 %8, 1 ret %"main.Point[int]" %10 -deref.throw: ; preds = %entry - unreachable - -deref.throw1: ; preds = %deref.next - unreachable - -deref.throw3: ; preds = %deref.next2 - unreachable - -deref.throw5: ; preds = %deref.next4 - unreachable - -store.throw: ; preds = %deref.next6 - unreachable - -store.throw7: ; preds = %store.next +deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry unreachable }