@@ -100,7 +100,7 @@ func (b *builder) createLandingPad() {
100100
101101 // Continue at the 'recover' block, which returns to the parent in an
102102 // appropriate way.
103- b .CreateBr (b .blockEntries [b .fn .Recover ] )
103+ b .CreateBr (b .blockInfo [b .fn .Recover . Index ]. entry )
104104}
105105
106106// Create a checkpoint (similar to setjmp). This emits inline assembly that
@@ -234,41 +234,88 @@ func (b *builder) createInvokeCheckpoint() {
234234 continueBB := b .insertBasicBlock ("" )
235235 b .CreateCondBr (isZero , continueBB , b .landingpad )
236236 b .SetInsertPointAtEnd (continueBB )
237- b .blockExits [ b . currentBlock ] = continueBB
237+ b .currentBlockInfo . exit = continueBB
238238}
239239
240- // isInLoop checks if there is a path from a basic block to itself.
241- func isInLoop (start * ssa.BasicBlock ) bool {
242- // Use a breadth-first search to scan backwards through the block graph.
243- queue := []* ssa.BasicBlock {start }
244- checked := map [* ssa.BasicBlock ]struct {}{}
245-
246- for len (queue ) > 0 {
247- // pop a block off of the queue
248- block := queue [len (queue )- 1 ]
249- queue = queue [:len (queue )- 1 ]
250-
251- // Search through predecessors.
252- // Searching backwards means that this is pretty fast when the block is close to the start of the function.
253- // Defers are often placed near the start of the function.
254- for _ , pred := range block .Preds {
255- if pred == start {
256- // cycle found
257- return true
258- }
240+ // isInLoop checks if there is a path from the current block to itself.
241+ // Use Tarjan's strongly connected components algorithm to search for cycles.
242+ // A one-node SCC is a cycle iff there is an edge from the node to itself.
243+ // A multi-node SCC is always a cycle.
244+ func (b * builder ) isInLoop () bool {
245+ if b .currentBlockInfo .tarjan .index == 0 {
246+ b .strongConnect (b .currentBlock )
247+ }
248+ return b .currentBlockInfo .tarjan .cyclic
249+ }
259250
260- if _ , ok := checked [pred ]; ok {
261- // block already checked
262- continue
263- }
251+ func (b * builder ) strongConnect (block * ssa.BasicBlock ) {
252+ // Assign a new index.
253+ assignedIndex := b .tarjanIndex + 1
254+ b .tarjanIndex = assignedIndex
255+
256+ // Apply the new index.
257+ blockIndex := block .Index
258+ node := & b .blockInfo [blockIndex ].tarjan
259+ node .index = assignedIndex
260+ node .lowLink = assignedIndex
261+
262+ // Push the node onto the stack.
263+ node .onStack = true
264+ b .tarjanStack = append (b .tarjanStack , uint (blockIndex ))
265+
266+ // Process the successors.
267+ for _ , successor := range block .Succs {
268+ // Look up the successor's state.
269+ successorIndex := successor .Index
270+ if successorIndex == blockIndex {
271+ // Handle a self-cycle specially.
272+ node .cyclic = true
273+ continue
274+ }
275+ successorNode := & b .blockInfo [successorIndex ].tarjan
276+
277+ if successorNode .index == 0 {
278+ // This node has not yet been visisted.
279+ b .strongConnect (successor )
280+ } else if ! successorNode .onStack {
281+ // This node is has been visited, but is in a different SCC.
282+ // Ignore it.
283+ continue
284+ }
285+ if successorNode .lowLink < node .lowLink {
286+ node .lowLink = successorNode .lowLink
287+ }
288+ }
264289
265- // add to queue and checked map
266- queue = append (queue , pred )
267- checked [pred ] = struct {}{}
290+ if node .lowLink == node .index {
291+ // This is a root node.
292+ // Pop the SCC off the stack.
293+ stack := b .tarjanStack
294+ top := stack [len (stack )- 1 ]
295+ stack = stack [:len (stack )- 1 ]
296+ blocks := b .blockInfo
297+ topNode := & blocks [top ].tarjan
298+ topNode .onStack = false
299+ if top != uint (blockIndex ) {
300+ // Mark all nodes in this SCC as cyclic.
301+ topNode .cyclic = true
302+ for top != uint (blockIndex ) {
303+ top = stack [len (stack )- 1 ]
304+ stack = stack [:len (stack )- 1 ]
305+ topNode := & blocks [top ].tarjan
306+ topNode .onStack = false
307+ topNode .cyclic = true
308+ }
268309 }
310+ b .tarjanStack = stack
269311 }
312+ }
270313
271- return false
314+ type tarjanNode struct {
315+ index uint
316+ lowLink uint
317+ onStack bool
318+ cyclic bool
272319}
273320
274321// createDefer emits a single defer instruction, to be run when this function
@@ -410,7 +457,10 @@ func (b *builder) createDefer(instr *ssa.Defer) {
410457
411458 // Put this struct in an allocation.
412459 var alloca llvm.Value
413- if ! isInLoop (instr .Block ()) {
460+ if instr .Block () != b .currentBlock {
461+ panic ("block mismatch" )
462+ }
463+ if ! b .isInLoop () {
414464 // This can safely use a stack allocation.
415465 alloca = llvmutil .CreateEntryBlockAlloca (b .Builder , deferredCallType , "defer.alloca" )
416466 } else {
0 commit comments