@@ -152,11 +152,11 @@ func (c *Compiler) LowerFuncValues() {
152
152
// There are multiple functions used in a func value that
153
153
// implement this signature.
154
154
// What we'll do is transform the following:
155
- // rawPtr := runtime.getFuncPtr(fn )
156
- // if func. rawPtr == nil {
155
+ // rawPtr := runtime.getFuncPtr(func.ptr )
156
+ // if rawPtr == nil {
157
157
// runtime.nilPanic()
158
158
// }
159
- // result := func. rawPtr(...args, func.context)
159
+ // result := rawPtr(...args, func.context)
160
160
// into this:
161
161
// if false {
162
162
// runtime.nilPanic()
@@ -175,95 +175,111 @@ func (c *Compiler) LowerFuncValues() {
175
175
176
176
// Remove some casts, checks, and the old call which we're going
177
177
// to replace.
178
- var funcCall llvm.Value
179
- for _ , inttoptr := range getUses (getFuncPtrCall ) {
180
- if inttoptr .IsAIntToPtrInst ().IsNil () {
178
+ for _ , callIntPtr := range getUses (getFuncPtrCall ) {
179
+ if ! callIntPtr .IsACallInst ().IsNil () && callIntPtr .CalledValue ().Name () == "runtime.makeGoroutine" {
180
+ for _ , inttoptr := range getUses (callIntPtr ) {
181
+ if inttoptr .IsAIntToPtrInst ().IsNil () {
182
+ panic ("expected a inttoptr" )
183
+ }
184
+ for _ , use := range getUses (inttoptr ) {
185
+ c .addFuncLoweringSwitch (funcID , use , c .emitStartGoroutine , functions )
186
+ use .EraseFromParentAsInstruction ()
187
+ }
188
+ inttoptr .EraseFromParentAsInstruction ()
189
+ }
190
+ callIntPtr .EraseFromParentAsInstruction ()
191
+ continue
192
+ }
193
+ if callIntPtr .IsAIntToPtrInst ().IsNil () {
181
194
panic ("expected inttoptr" )
182
195
}
183
- for _ , ptrUse := range getUses (inttoptr ) {
196
+ for _ , ptrUse := range getUses (callIntPtr ) {
184
197
if ! ptrUse .IsABitCastInst ().IsNil () {
185
198
for _ , bitcastUse := range getUses (ptrUse ) {
186
- if bitcastUse .IsACallInst ().IsNil () || bitcastUse .CalledValue ().Name () != "runtime.isnil" {
199
+ if bitcastUse .IsACallInst ().IsNil () || bitcastUse .CalledValue ().IsAFunction ().IsNil () {
200
+ panic ("expected a call instruction" )
201
+ }
202
+ switch bitcastUse .CalledValue ().Name () {
203
+ case "runtime.isnil" :
204
+ bitcastUse .ReplaceAllUsesWith (llvm .ConstInt (c .ctx .Int1Type (), 0 , false ))
205
+ bitcastUse .EraseFromParentAsInstruction ()
206
+ default :
187
207
panic ("expected a call to runtime.isnil" )
188
208
}
189
- bitcastUse .ReplaceAllUsesWith (llvm .ConstInt (c .ctx .Int1Type (), 0 , false ))
190
- bitcastUse .EraseFromParentAsInstruction ()
191
209
}
192
- ptrUse .EraseFromParentAsInstruction ()
193
- } else if ! ptrUse .IsACallInst ().IsNil () && ptrUse .CalledValue () == inttoptr {
194
- if ! funcCall .IsNil () {
195
- panic ("multiple calls on a single runtime.getFuncPtr" )
196
- }
197
- funcCall = ptrUse
210
+ } else if ! ptrUse .IsACallInst ().IsNil () && ptrUse .CalledValue () == callIntPtr {
211
+ c .addFuncLoweringSwitch (funcID , ptrUse , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
212
+ return c .builder .CreateCall (funcPtr , params , "" )
213
+ }, functions )
198
214
} else {
199
215
panic ("unexpected getFuncPtrCall" )
200
216
}
217
+ ptrUse .EraseFromParentAsInstruction ()
201
218
}
219
+ callIntPtr .EraseFromParentAsInstruction ()
202
220
}
203
- if funcCall .IsNil () {
204
- panic ("expected exactly one call use of a runtime.getFuncPtr" )
205
- }
206
-
207
- // The block that cannot be reached with correct funcValues (to
208
- // help the optimizer).
209
- c .builder .SetInsertPointBefore (funcCall )
210
- defaultBlock := llvm .AddBasicBlock (funcCall .InstructionParent ().Parent (), "func.default" )
211
- c .builder .SetInsertPointAtEnd (defaultBlock )
212
- c .builder .CreateUnreachable ()
221
+ getFuncPtrCall .EraseFromParentAsInstruction ()
222
+ }
223
+ }
224
+ }
225
+ }
213
226
214
- // Create the switch.
215
- c .builder .SetInsertPointBefore (funcCall )
216
- sw := c .builder .CreateSwitch (funcID , defaultBlock , len (functions )+ 1 )
227
+ // addFuncLoweringSwitch creates a new switch on a function ID and inserts calls
228
+ // to the newly created direct calls. The funcID is the number to switch on,
229
+ // call is the call instruction to replace, and createCall is the callback that
230
+ // actually creates the new call. By changing createCall to something other than
231
+ // c.builder.CreateCall, instead of calling a function it can start a new
232
+ // goroutine for example.
233
+ func (c * Compiler ) addFuncLoweringSwitch (funcID , call llvm.Value , createCall func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value , functions funcWithUsesList ) {
234
+ // The block that cannot be reached with correct funcValues (to help the
235
+ // optimizer).
236
+ c .builder .SetInsertPointBefore (call )
237
+ defaultBlock := llvm .AddBasicBlock (call .InstructionParent ().Parent (), "func.default" )
238
+ c .builder .SetInsertPointAtEnd (defaultBlock )
239
+ c .builder .CreateUnreachable ()
217
240
218
- // Split right after the switch. We will need to insert a few
219
- // basic blocks in this gap.
220
- nextBlock := c .splitBasicBlock ( sw , llvm . NextBasicBlock ( sw . InstructionParent ()), "func.next" )
241
+ // Create the switch.
242
+ c . builder . SetInsertPointBefore ( call )
243
+ sw := c .builder . CreateSwitch ( funcID , defaultBlock , len ( functions ) + 1 )
221
244
222
- // The 0 case, which is actually a nil check.
223
- nilBlock := llvm .InsertBasicBlock (nextBlock , "func.nil" )
224
- c .builder .SetInsertPointAtEnd (nilBlock )
225
- c .createRuntimeCall ("nilPanic" , nil , "" )
226
- c .builder .CreateUnreachable ()
227
- sw .AddCase (llvm .ConstInt (c .uintptrType , 0 , false ), nilBlock )
245
+ // Split right after the switch. We will need to insert a few basic blocks
246
+ // in this gap.
247
+ nextBlock := c .splitBasicBlock (sw , llvm .NextBasicBlock (sw .InstructionParent ()), "func.next" )
228
248
229
- // Gather the list of parameters for every call we're going to
230
- // make.
231
- callParams := make ([]llvm. Value , funcCall . OperandsCount () - 1 )
232
- for i := range callParams {
233
- callParams [ i ] = funcCall . Operand ( i )
234
- }
249
+ // The 0 case, which is actually a nil check.
250
+ nilBlock := llvm . InsertBasicBlock ( nextBlock , "func.nil" )
251
+ c . builder . SetInsertPointAtEnd ( nilBlock )
252
+ c . createRuntimeCall ( "nilPanic" , nil , "" )
253
+ c . builder . CreateUnreachable ( )
254
+ sw . AddCase ( llvm . ConstInt ( c . uintptrType , 0 , false ), nilBlock )
235
255
236
- // If the call produces a value, we need to get it using a PHI
237
- // node.
238
- phiBlocks := make ([]llvm.BasicBlock , len (functions ))
239
- phiValues := make ([]llvm.Value , len (functions ))
240
- for i , fn := range functions {
241
- // Insert a switch case.
242
- bb := llvm .InsertBasicBlock (nextBlock , "func.call" + strconv .Itoa (fn .id ))
243
- c .builder .SetInsertPointAtEnd (bb )
244
- result := c .builder .CreateCall (fn .funcPtr , callParams , "" )
245
- c .builder .CreateBr (nextBlock )
246
- sw .AddCase (llvm .ConstInt (c .uintptrType , uint64 (fn .id ), false ), bb )
247
- phiBlocks [i ] = bb
248
- phiValues [i ] = result
249
- }
250
- // Create the PHI node so that the call result flows into the
251
- // next block (after the split). This is only necessary when the
252
- // call produced a value.
253
- if funcCall .Type ().TypeKind () != llvm .VoidTypeKind {
254
- c .builder .SetInsertPointBefore (nextBlock .FirstInstruction ())
255
- phi := c .builder .CreatePHI (funcCall .Type (), "" )
256
- phi .AddIncoming (phiValues , phiBlocks )
257
- funcCall .ReplaceAllUsesWith (phi )
258
- }
256
+ // Gather the list of parameters for every call we're going to make.
257
+ callParams := make ([]llvm.Value , call .OperandsCount ()- 1 )
258
+ for i := range callParams {
259
+ callParams [i ] = call .Operand (i )
260
+ }
259
261
260
- // Finally, remove the old instructions.
261
- funcCall .EraseFromParentAsInstruction ()
262
- for _ , inttoptr := range getUses (getFuncPtrCall ) {
263
- inttoptr .EraseFromParentAsInstruction ()
264
- }
265
- getFuncPtrCall .EraseFromParentAsInstruction ()
266
- }
267
- }
262
+ // If the call produces a value, we need to get it using a PHI
263
+ // node.
264
+ phiBlocks := make ([]llvm.BasicBlock , len (functions ))
265
+ phiValues := make ([]llvm.Value , len (functions ))
266
+ for i , fn := range functions {
267
+ // Insert a switch case.
268
+ bb := llvm .InsertBasicBlock (nextBlock , "func.call" + strconv .Itoa (fn .id ))
269
+ c .builder .SetInsertPointAtEnd (bb )
270
+ result := createCall (fn .funcPtr , callParams )
271
+ c .builder .CreateBr (nextBlock )
272
+ sw .AddCase (llvm .ConstInt (c .uintptrType , uint64 (fn .id ), false ), bb )
273
+ phiBlocks [i ] = bb
274
+ phiValues [i ] = result
275
+ }
276
+ // Create the PHI node so that the call result flows into the
277
+ // next block (after the split). This is only necessary when the
278
+ // call produced a value.
279
+ if call .Type ().TypeKind () != llvm .VoidTypeKind {
280
+ c .builder .SetInsertPointBefore (nextBlock .FirstInstruction ())
281
+ phi := c .builder .CreatePHI (call .Type (), "" )
282
+ phi .AddIncoming (phiValues , phiBlocks )
283
+ call .ReplaceAllUsesWith (phi )
268
284
}
269
285
}
0 commit comments