Skip to content

Commit 50e3a35

Browse files
committed
compiler: share recoverable fault blocks
1 parent 87de5dc commit 50e3a35

5 files changed

Lines changed: 102 additions & 89 deletions

File tree

builder/sizes_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func TestBinarySize(t *testing.T) {
4343
tests := []sizeTest{
4444
// microcontrollers
4545
{"hifive1b", "examples/echo", 4285, 315, 0, 2252},
46-
{"microbit", "examples/serial", 2856, 356, 8, 2248},
46+
{"microbit", "examples/serial", 2860, 356, 8, 2248},
4747
{"wioterminal", "examples/pininterrupt", 7538, 1558, 120, 7248},
4848

4949
// TODO: also check wasm. Right now this is difficult, because

compiler/asserts.go

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
3838
}
3939
}
4040

41-
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, "lookup.throw")
42-
nextBlock := b.insertBasicBlock("lookup.next")
43-
b.currentBlockInfo.exit = nextBlock
44-
b.CreateCondBr(outOfBounds, faultBlock, nextBlock)
45-
46-
b.SetInsertPointAtEnd(faultBlock)
47-
if b.hasDeferFrame() {
48-
b.createFaultCheckpoint()
49-
}
5041
// Pass index and length for a detailed error message.
5142
// Truncate or extend to match the runtime's int type.
5243
idx := index
@@ -61,8 +52,14 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
6152
} else if length.Type().IntTypeWidth() > b.intType.IntTypeWidth() {
6253
length = b.CreateTrunc(length, b.intType, "")
6354
}
64-
b.createRuntimeCall("lookupPanic", []llvm.Value{idx, length}, "")
65-
b.CreateUnreachable()
55+
56+
faultBlock, idxPhi, lengthPhi := b.getLookupPanicBlock()
57+
currentBlock := b.GetInsertBlock()
58+
nextBlock := b.insertBasicBlock("lookup.next")
59+
b.currentBlockInfo.exit = nextBlock
60+
b.CreateCondBr(outOfBounds, faultBlock, nextBlock)
61+
idxPhi.AddIncoming([]llvm.Value{idx}, []llvm.BasicBlock{currentBlock})
62+
lengthPhi.AddIncoming([]llvm.Value{length}, []llvm.BasicBlock{currentBlock})
6663

6764
b.SetInsertPointAtEnd(nextBlock)
6865
}
@@ -274,25 +271,56 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
274271
}
275272
}
276273

277-
// Put the fault block at the end of the function and the next block at the
278-
// current insert position.
279-
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
274+
faultBlock := b.getRuntimeAssertBlock(blockPrefix, assertFunc)
280275
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
281276
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
282277

283278
// Now branch to the out-of-bounds or the regular block.
284279
b.CreateCondBr(assert, faultBlock, nextBlock)
285280

286-
// Fail: the assert triggered so panic.
287-
b.SetInsertPointAtEnd(faultBlock)
281+
// Ok: assert didn't trigger so continue normally.
282+
b.SetInsertPointAtEnd(nextBlock)
283+
}
284+
285+
func (b *builder) getLookupPanicBlock() (llvm.BasicBlock, llvm.Value, llvm.Value) {
286+
if !b.lookupPanicBlock.IsNil() {
287+
return b.lookupPanicBlock, b.lookupPanicIndexPhi, b.lookupPanicLengthPhi
288+
}
289+
savedBlock := b.GetInsertBlock()
290+
block := b.ctx.AddBasicBlock(b.llvmFn, "lookup.throw")
291+
b.lookupPanicBlock = block
292+
b.SetInsertPointAtEnd(block)
293+
idx := b.CreatePHI(b.intType, "lookup.index")
294+
length := b.CreatePHI(b.intType, "lookup.length")
295+
b.lookupPanicIndexPhi = idx
296+
b.lookupPanicLengthPhi = length
288297
if b.hasDeferFrame() {
289298
b.createFaultCheckpoint()
290299
}
291-
b.createRuntimeCall(assertFunc, nil, "")
300+
b.createRuntimeCall("lookupPanic", []llvm.Value{idx, length}, "")
292301
b.CreateUnreachable()
302+
b.SetInsertPointAtEnd(savedBlock)
303+
return block, idx, length
304+
}
293305

294-
// Ok: assert didn't trigger so continue normally.
295-
b.SetInsertPointAtEnd(nextBlock)
306+
func (b *builder) getRuntimeAssertBlock(blockPrefix, assertFunc string) llvm.BasicBlock {
307+
if b.runtimeAssertBlocks == nil {
308+
b.runtimeAssertBlocks = make(map[string]llvm.BasicBlock)
309+
}
310+
if block := b.runtimeAssertBlocks[assertFunc]; !block.IsNil() {
311+
return block
312+
}
313+
savedBlock := b.GetInsertBlock()
314+
block := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
315+
b.runtimeAssertBlocks[assertFunc] = block
316+
b.SetInsertPointAtEnd(block)
317+
if b.hasDeferFrame() {
318+
b.createFaultCheckpoint()
319+
}
320+
b.createRuntimeCall(assertFunc, nil, "")
321+
b.CreateUnreachable()
322+
b.SetInsertPointAtEnd(savedBlock)
323+
return block
296324
}
297325

298326
// extendInteger extends the value to at least targetType using a zero or sign

compiler/compiler.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ type builder struct {
176176
deferBuiltinFuncs map[ssa.Value]deferBuiltin
177177
runDefersBlock []llvm.BasicBlock
178178
afterDefersBlock []llvm.BasicBlock
179+
180+
lookupPanicBlock llvm.BasicBlock
181+
lookupPanicIndexPhi llvm.Value
182+
lookupPanicLengthPhi llvm.Value
183+
runtimeAssertBlocks map[string]llvm.BasicBlock
184+
interfaceAssertBlock llvm.BasicBlock
179185
}
180186

181187
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {

compiler/interface.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -828,18 +828,10 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
828828
return tuple
829829
} else {
830830
// Type assert without comma-ok. If it fails, panic.
831-
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.throw")
831+
faultBlock := b.getInterfaceAssertBlock()
832832
b.currentBlockInfo.exit = okBlock
833833
b.CreateCondBr(commaOk, okBlock, faultBlock)
834834

835-
// Fault: emit a checkpoint (for recover) and panic.
836-
b.SetInsertPointAtEnd(faultBlock)
837-
if b.hasDeferFrame() {
838-
b.createFaultCheckpoint()
839-
}
840-
b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "")
841-
b.CreateUnreachable()
842-
843835
// OK: extract the value from the interface.
844836
b.SetInsertPointAtEnd(okBlock)
845837
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
@@ -849,6 +841,23 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
849841
}
850842
}
851843

844+
func (b *builder) getInterfaceAssertBlock() llvm.BasicBlock {
845+
if !b.interfaceAssertBlock.IsNil() {
846+
return b.interfaceAssertBlock
847+
}
848+
savedBlock := b.GetInsertBlock()
849+
block := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.throw")
850+
b.interfaceAssertBlock = block
851+
b.SetInsertPointAtEnd(block)
852+
if b.hasDeferFrame() {
853+
b.createFaultCheckpoint()
854+
}
855+
b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "")
856+
b.CreateUnreachable()
857+
b.SetInsertPointAtEnd(savedBlock)
858+
return block
859+
}
860+
852861
// getMethodsString returns a string to be used in the "tinygo-methods" string
853862
// attribute for interface functions.
854863
func (c *compilerContext) getMethodsString(itf *types.Interface) string {

compiler/testdata/generics.ll

Lines changed: 30 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -29,43 +29,43 @@ entry:
2929
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
3030
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3
3131
store float %a.X, ptr %a, align 4
32-
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4
33-
store float %a.Y, ptr %a.repack9, align 4
32+
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
33+
store float %a.Y, ptr %a.repack5, align 4
3434
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
3535
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3
3636
store float %b.X, ptr %b, align 4
37-
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4
38-
store float %b.Y, ptr %b.repack11, align 4
37+
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
38+
store float %b.Y, ptr %b.repack7, align 4
3939
call void @main.checkSize(i32 4, ptr undef) #3
4040
call void @main.checkSize(i32 8, ptr undef) #3
4141
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
4242
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
4343
br i1 false, label %deref.throw, label %deref.next
4444

4545
deref.next: ; preds = %entry
46-
br i1 false, label %deref.throw1, label %deref.next2
46+
br i1 false, label %deref.throw, label %deref.next1
4747

48-
deref.next2: ; preds = %deref.next
48+
deref.next1: ; preds = %deref.next
4949
%0 = load float, ptr %a, align 4
5050
%1 = load float, ptr %b, align 4
5151
%2 = fadd float %0, %1
52-
br i1 false, label %deref.throw3, label %deref.next4
52+
br i1 false, label %deref.throw, label %deref.next2
5353

54-
deref.next4: ; preds = %deref.next2
55-
br i1 false, label %deref.throw5, label %deref.next6
54+
deref.next2: ; preds = %deref.next1
55+
br i1 false, label %deref.throw, label %deref.next3
5656

57-
deref.next6: ; preds = %deref.next4
57+
deref.next3: ; preds = %deref.next2
5858
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
5959
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
6060
%5 = load float, ptr %4, align 4
6161
%6 = load float, ptr %3, align 4
62-
br i1 false, label %store.throw, label %store.next
62+
br i1 false, label %deref.throw, label %store.next
6363

64-
store.next: ; preds = %deref.next6
64+
store.next: ; preds = %deref.next3
6565
store float %2, ptr %complit, align 4
66-
br i1 false, label %store.throw7, label %store.next8
66+
br i1 false, label %deref.throw, label %store.next4
6767

68-
store.next8: ; preds = %store.next
68+
store.next4: ; preds = %store.next
6969
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
7070
%8 = fadd float %5, %6
7171
store float %8, ptr %7, align 4
@@ -74,22 +74,7 @@ store.next8: ; preds = %store.next
7474
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1
7575
ret %"main.Point[float32]" %10
7676

77-
deref.throw: ; preds = %entry
78-
unreachable
79-
80-
deref.throw1: ; preds = %deref.next
81-
unreachable
82-
83-
deref.throw3: ; preds = %deref.next2
84-
unreachable
85-
86-
deref.throw5: ; preds = %deref.next4
87-
unreachable
88-
89-
store.throw: ; preds = %deref.next6
90-
unreachable
91-
92-
store.throw7: ; preds = %store.next
77+
deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
9378
unreachable
9479
}
9580

@@ -107,43 +92,43 @@ entry:
10792
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
10893
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3
10994
store i32 %a.X, ptr %a, align 4
110-
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4
111-
store i32 %a.Y, ptr %a.repack9, align 4
95+
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
96+
store i32 %a.Y, ptr %a.repack5, align 4
11297
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
11398
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3
11499
store i32 %b.X, ptr %b, align 4
115-
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4
116-
store i32 %b.Y, ptr %b.repack11, align 4
100+
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
101+
store i32 %b.Y, ptr %b.repack7, align 4
117102
call void @main.checkSize(i32 4, ptr undef) #3
118103
call void @main.checkSize(i32 8, ptr undef) #3
119104
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
120105
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
121106
br i1 false, label %deref.throw, label %deref.next
122107

123108
deref.next: ; preds = %entry
124-
br i1 false, label %deref.throw1, label %deref.next2
109+
br i1 false, label %deref.throw, label %deref.next1
125110

126-
deref.next2: ; preds = %deref.next
111+
deref.next1: ; preds = %deref.next
127112
%0 = load i32, ptr %a, align 4
128113
%1 = load i32, ptr %b, align 4
129114
%2 = add i32 %0, %1
130-
br i1 false, label %deref.throw3, label %deref.next4
115+
br i1 false, label %deref.throw, label %deref.next2
131116

132-
deref.next4: ; preds = %deref.next2
133-
br i1 false, label %deref.throw5, label %deref.next6
117+
deref.next2: ; preds = %deref.next1
118+
br i1 false, label %deref.throw, label %deref.next3
134119

135-
deref.next6: ; preds = %deref.next4
120+
deref.next3: ; preds = %deref.next2
136121
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
137122
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
138123
%5 = load i32, ptr %4, align 4
139124
%6 = load i32, ptr %3, align 4
140-
br i1 false, label %store.throw, label %store.next
125+
br i1 false, label %deref.throw, label %store.next
141126

142-
store.next: ; preds = %deref.next6
127+
store.next: ; preds = %deref.next3
143128
store i32 %2, ptr %complit, align 4
144-
br i1 false, label %store.throw7, label %store.next8
129+
br i1 false, label %deref.throw, label %store.next4
145130

146-
store.next8: ; preds = %store.next
131+
store.next4: ; preds = %store.next
147132
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
148133
%8 = add i32 %5, %6
149134
store i32 %8, ptr %7, align 4
@@ -152,22 +137,7 @@ store.next8: ; preds = %store.next
152137
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1
153138
ret %"main.Point[int]" %10
154139

155-
deref.throw: ; preds = %entry
156-
unreachable
157-
158-
deref.throw1: ; preds = %deref.next
159-
unreachable
160-
161-
deref.throw3: ; preds = %deref.next2
162-
unreachable
163-
164-
deref.throw5: ; preds = %deref.next4
165-
unreachable
166-
167-
store.throw: ; preds = %deref.next6
168-
unreachable
169-
170-
store.throw7: ; preds = %store.next
140+
deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
171141
unreachable
172142
}
173143

0 commit comments

Comments
 (0)