@@ -6,6 +6,7 @@ package transform
6
6
import (
7
7
"sort"
8
8
"strconv"
9
+ "strings"
9
10
10
11
"github.com/tinygo-org/tinygo/compiler/llvmutil"
11
12
"tinygo.org/x/go-llvm"
@@ -55,17 +56,30 @@ func LowerFuncValues(mod llvm.Module) {
55
56
funcValueWithSignaturePtr := llvm .PointerType (mod .GetTypeByName ("runtime.funcValueWithSignature" ), 0 )
56
57
signatures := map [string ]* funcSignatureInfo {}
57
58
for global := mod .FirstGlobal (); ! global .IsNil (); global = llvm .NextGlobal (global ) {
58
- if global .Type () != funcValueWithSignaturePtr {
59
+ var sig , funcVal llvm.Value
60
+ switch {
61
+ case global .Type () == funcValueWithSignaturePtr :
62
+ sig = llvm .ConstExtractValue (global .Initializer (), []uint32 {1 })
63
+ funcVal = global
64
+ case strings .HasPrefix (global .Name (), "reflect/types.type:func:{" ):
65
+ sig = global
66
+ default :
59
67
continue
60
68
}
61
- sig := llvm . ConstExtractValue ( global . Initializer (), [] uint32 { 1 })
69
+
62
70
name := sig .Name ()
71
+ var funcValueWithSignatures []llvm.Value
72
+ if funcVal .IsNil () {
73
+ funcValueWithSignatures = []llvm.Value {}
74
+ } else {
75
+ funcValueWithSignatures = []llvm.Value {funcVal }
76
+ }
63
77
if info , ok := signatures [name ]; ok {
64
- info .funcValueWithSignatures = append (info .funcValueWithSignatures , global )
78
+ info .funcValueWithSignatures = append (info .funcValueWithSignatures , funcValueWithSignatures ... )
65
79
} else {
66
80
signatures [name ] = & funcSignatureInfo {
67
81
sig : sig ,
68
- funcValueWithSignatures : []llvm. Value { global } ,
82
+ funcValueWithSignatures : funcValueWithSignatures ,
69
83
}
70
84
}
71
85
}
@@ -123,95 +137,64 @@ func LowerFuncValues(mod llvm.Module) {
123
137
panic ("expected all call uses to be runtime.getFuncPtr" )
124
138
}
125
139
funcID := getFuncPtrCall .Operand (1 )
126
- switch len (functions ) {
127
- case 0 :
128
- // There are no functions used in a func value that implement
129
- // this signature. The only possible value is a nil value.
130
- for _ , inttoptr := range getUses (getFuncPtrCall ) {
131
- if inttoptr .IsAIntToPtrInst ().IsNil () {
132
- panic ("expected inttoptr" )
133
- }
134
- nilptr := llvm .ConstPointerNull (inttoptr .Type ())
135
- inttoptr .ReplaceAllUsesWith (nilptr )
136
- inttoptr .EraseFromParentAsInstruction ()
140
+
141
+ // There are functions used in a func value that
142
+ // implement this signature.
143
+ // What we'll do is transform the following:
144
+ // rawPtr := runtime.getFuncPtr(func.ptr)
145
+ // if rawPtr == nil {
146
+ // runtime.nilPanic()
147
+ // }
148
+ // result := rawPtr(...args, func.context)
149
+ // into this:
150
+ // if false {
151
+ // runtime.nilPanic()
152
+ // }
153
+ // var result // Phi
154
+ // switch fn.id {
155
+ // case 0:
156
+ // runtime.nilPanic()
157
+ // case 1:
158
+ // result = call first implementation...
159
+ // case 2:
160
+ // result = call second implementation...
161
+ // default:
162
+ // unreachable
163
+ // }
164
+
165
+ // Remove some casts, checks, and the old call which we're going
166
+ // to replace.
167
+ for _ , callIntPtr := range getUses (getFuncPtrCall ) {
168
+ if ! callIntPtr .IsACallInst ().IsNil () && callIntPtr .CalledValue ().Name () == "internal/task.start" {
169
+ // Special case for goroutine starts.
170
+ addFuncLoweringSwitch (mod , builder , funcID , callIntPtr , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
171
+ i8ptrType := llvm .PointerType (ctx .Int8Type (), 0 )
172
+ calleeValue := builder .CreatePtrToInt (funcPtr , uintptrType , "" )
173
+ start := mod .NamedFunction ("internal/task.start" )
174
+ builder .CreateCall (start , []llvm.Value {calleeValue , callIntPtr .Operand (1 ), llvm .Undef (i8ptrType ), llvm .ConstNull (i8ptrType )}, "" )
175
+ return llvm.Value {} // void so no return value
176
+ }, functions )
177
+ callIntPtr .EraseFromParentAsInstruction ()
178
+ continue
137
179
}
138
- getFuncPtrCall .EraseFromParentAsInstruction ()
139
- case 1 :
140
- // There is exactly one function with this signature that is
141
- // used in a func value. The func value itself can be either nil
142
- // or this one function.
143
- builder .SetInsertPointBefore (getFuncPtrCall )
144
- zero := llvm .ConstInt (uintptrType , 0 , false )
145
- isnil := builder .CreateICmp (llvm .IntEQ , funcID , zero , "" )
146
- funcPtrNil := llvm .ConstPointerNull (functions [0 ].funcPtr .Type ())
147
- funcPtr := builder .CreateSelect (isnil , funcPtrNil , functions [0 ].funcPtr , "" )
148
- for _ , inttoptr := range getUses (getFuncPtrCall ) {
149
- if inttoptr .IsAIntToPtrInst ().IsNil () {
150
- panic ("expected inttoptr" )
151
- }
152
- inttoptr .ReplaceAllUsesWith (funcPtr )
153
- inttoptr .EraseFromParentAsInstruction ()
180
+ if callIntPtr .IsAIntToPtrInst ().IsNil () {
181
+ panic ("expected inttoptr" )
154
182
}
155
- getFuncPtrCall .EraseFromParentAsInstruction ()
156
- default :
157
- // There are multiple functions used in a func value that
158
- // implement this signature.
159
- // What we'll do is transform the following:
160
- // rawPtr := runtime.getFuncPtr(func.ptr)
161
- // if rawPtr == nil {
162
- // runtime.nilPanic()
163
- // }
164
- // result := rawPtr(...args, func.context)
165
- // into this:
166
- // if false {
167
- // runtime.nilPanic()
168
- // }
169
- // var result // Phi
170
- // switch fn.id {
171
- // case 0:
172
- // runtime.nilPanic()
173
- // case 1:
174
- // result = call first implementation...
175
- // case 2:
176
- // result = call second implementation...
177
- // default:
178
- // unreachable
179
- // }
180
-
181
- // Remove some casts, checks, and the old call which we're going
182
- // to replace.
183
- for _ , callIntPtr := range getUses (getFuncPtrCall ) {
184
- if ! callIntPtr .IsACallInst ().IsNil () && callIntPtr .CalledValue ().Name () == "internal/task.start" {
185
- // Special case for goroutine starts.
186
- addFuncLoweringSwitch (mod , builder , funcID , callIntPtr , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
187
- i8ptrType := llvm .PointerType (ctx .Int8Type (), 0 )
188
- calleeValue := builder .CreatePtrToInt (funcPtr , uintptrType , "" )
189
- start := mod .NamedFunction ("internal/task.start" )
190
- builder .CreateCall (start , []llvm.Value {calleeValue , callIntPtr .Operand (1 ), llvm .Undef (i8ptrType ), llvm .ConstNull (i8ptrType )}, "" )
191
- return llvm.Value {} // void so no return value
183
+ for _ , ptrUse := range getUses (callIntPtr ) {
184
+ if ! ptrUse .IsAICmpInst ().IsNil () {
185
+ ptrUse .ReplaceAllUsesWith (llvm .ConstInt (ctx .Int1Type (), 0 , false ))
186
+ } else if ! ptrUse .IsACallInst ().IsNil () && ptrUse .CalledValue () == callIntPtr {
187
+ addFuncLoweringSwitch (mod , builder , funcID , ptrUse , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
188
+ return builder .CreateCall (funcPtr , params , "" )
192
189
}, functions )
193
- callIntPtr .EraseFromParentAsInstruction ()
194
- continue
195
- }
196
- if callIntPtr .IsAIntToPtrInst ().IsNil () {
197
- panic ("expected inttoptr" )
198
- }
199
- for _ , ptrUse := range getUses (callIntPtr ) {
200
- if ! ptrUse .IsAICmpInst ().IsNil () {
201
- ptrUse .ReplaceAllUsesWith (llvm .ConstInt (ctx .Int1Type (), 0 , false ))
202
- } else if ! ptrUse .IsACallInst ().IsNil () && ptrUse .CalledValue () == callIntPtr {
203
- addFuncLoweringSwitch (mod , builder , funcID , ptrUse , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
204
- return builder .CreateCall (funcPtr , params , "" )
205
- }, functions )
206
- } else {
207
- panic ("unexpected getFuncPtrCall" )
208
- }
209
- ptrUse .EraseFromParentAsInstruction ()
190
+ } else {
191
+ panic ("unexpected getFuncPtrCall" )
210
192
}
211
- callIntPtr .EraseFromParentAsInstruction ()
193
+ ptrUse .EraseFromParentAsInstruction ()
212
194
}
213
- getFuncPtrCall .EraseFromParentAsInstruction ()
195
+ callIntPtr .EraseFromParentAsInstruction ()
214
196
}
197
+ getFuncPtrCall .EraseFromParentAsInstruction ()
215
198
}
216
199
}
217
200
}
@@ -270,13 +253,18 @@ func addFuncLoweringSwitch(mod llvm.Module, builder llvm.Builder, funcID, call l
270
253
phiBlocks [i ] = bb
271
254
phiValues [i ] = result
272
255
}
273
- // Create the PHI node so that the call result flows into the
274
- // next block (after the split). This is only necessary when the
275
- // call produced a value.
276
256
if call .Type ().TypeKind () != llvm .VoidTypeKind {
277
- builder .SetInsertPointBefore (nextBlock .FirstInstruction ())
278
- phi := builder .CreatePHI (call .Type (), "" )
279
- phi .AddIncoming (phiValues , phiBlocks )
280
- call .ReplaceAllUsesWith (phi )
257
+ if len (functions ) > 0 {
258
+ // Create the PHI node so that the call result flows into the
259
+ // next block (after the split). This is only necessary when the
260
+ // call produced a value.
261
+ builder .SetInsertPointBefore (nextBlock .FirstInstruction ())
262
+ phi := builder .CreatePHI (call .Type (), "" )
263
+ phi .AddIncoming (phiValues , phiBlocks )
264
+ call .ReplaceAllUsesWith (phi )
265
+ } else {
266
+ // This is always a nil panic, so replace the call result with undef.
267
+ call .ReplaceAllUsesWith (llvm .Undef (call .Type ()))
268
+ }
281
269
}
282
270
}
0 commit comments