package ssa

import (
	
	
	

	
)

// passCalculateImmediateDominators calculates immediate dominators for each basic block.
// The result is stored in b.dominators. This make it possible for the following passes to
// use builder.isDominatedBy to check if a block is dominated by another block.
//
// At the last of pass, this function also does the loop detection and sets the basicBlock.loop flag.
func passCalculateImmediateDominators( *builder) {
	 := .reversePostOrderedBasicBlocks[:0]

	// Store the reverse postorder from the entrypoint into reversePostOrder slice.
	// This calculation of reverse postorder is not described in the paper,
	// so we use heuristic to calculate it so that we could potentially handle arbitrary
	// complex CFGs under the assumption that success is sorted in program's natural order.
	// That means blk.success[i] always appears before blk.success[i+1] in the source program,
	// which is a reasonable assumption as long as SSA Builder is properly used.
	//
	// First we push blocks in postorder iteratively visit successors of the entry block.
	 := .entryBlk()
	 := append(.blkStack[:0], )
	// These flags are used to track the state of the block in the DFS traversal.
	// We temporarily use the reversePostOrder field to store the state.
	const , ,  = 0, 1, 2
	.visited = 
	for len() > 0 {
		 := len() - 1
		 := []
		 = [:]
		switch .visited {
		case :
			// This is likely a bug in the frontend.
			panic("BUG: unsupported CFG")
		case :
			// This is the first time to pop this block, and we have to see the successors first.
			// So push this block again to the stack.
			 = append(, )
			// And push the successors to the stack if necessary.
			for ,  := range .success {
				if .ReturnBlock() || .invalid {
					continue
				}
				if .visited ==  {
					.visited = 
					 = append(, )
				}
			}
			// Finally, we could pop this block once we pop all of its successors.
			.visited = 
		case :
			// Note: at this point we push blk in postorder despite its name.
			 = append(, )
		default:
			panic("BUG")
		}
	}
	// At this point, reversePostOrder has postorder actually, so we reverse it.
	for  := len()/2 - 1;  >= 0; -- {
		 := len() - 1 - 
		[], [] = [], []
	}

	for ,  := range  {
		.reversePostOrder = int32()
	}

	// Reuse the dominators slice if possible from the previous computation of function.
	.dominators = .dominators[:cap(.dominators)]
	if len(.dominators) < .basicBlocksPool.Allocated() {
		// Generously reserve space in the slice because the slice will be reused future allocation.
		.dominators = append(.dominators, make([]*basicBlock, .basicBlocksPool.Allocated())...)
	}
	calculateDominators(, .dominators)

	// Reuse the slices for the future use.
	.blkStack = 

	// For the following passes.
	.reversePostOrderedBasicBlocks = 

	// Ready to detect loops!
	subPassLoopDetection()
}

// calculateDominators calculates the immediate dominator of each node in the CFG, and store the result in `doms`.
// The algorithm is based on the one described in the paper "A Simple, Fast Dominance Algorithm"
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf which is a faster/simple alternative to the well known Lengauer-Tarjan algorithm.
//
// The following code almost matches the pseudocode in the paper with one exception (see the code comment below).
//
// The result slice `doms` must be pre-allocated with the size larger than the size of dfsBlocks.
func calculateDominators( []*basicBlock,  []*basicBlock) {
	,  := [0], [1: /* skips entry point */]
	for ,  := range  {
		[.id] = nil
	}
	[.id] = 

	 := true
	for  {
		 = false
		for ,  := range  {
			var  *basicBlock
			for  := range .preds {
				 := .preds[].blk
				// Skip if this pred is not reachable yet. Note that this is not described in the paper,
				// but it is necessary to handle nested loops etc.
				if [.id] == nil {
					continue
				}

				if  == nil {
					 = 
					continue
				} else {
					 = intersect(, , )
				}
			}
			if [.id] !=  {
				[.id] = 
				 = true
			}
		}
	}
}

// intersect returns the common dominator of blk1 and blk2.
//
// This is the `intersect` function in the paper.
func intersect( []*basicBlock,  *basicBlock,  *basicBlock) *basicBlock {
	,  := , 
	for  !=  {
		// Move the 'finger1' upwards to its immediate dominator.
		for .reversePostOrder > .reversePostOrder {
			 = [.id]
		}
		// Move the 'finger2' upwards to its immediate dominator.
		for .reversePostOrder > .reversePostOrder {
			 = [.id]
		}
	}
	return 
}

// subPassLoopDetection detects loops in the function using the immediate dominators.
//
// This is run at the last of passCalculateImmediateDominators.
func subPassLoopDetection( *builder) {
	for  := .blockIteratorBegin();  != nil;  = .blockIteratorNext() {
		for  := range .preds {
			 := .preds[].blk
			if .invalid {
				continue
			}
			if .isDominatedBy(, ) {
				.loopHeader = true
			}
		}
	}
}

// buildLoopNestingForest builds the loop nesting forest for the function.
// This must be called after branch splitting since it relies on the CFG.
func passBuildLoopNestingForest( *builder) {
	 := .entryBlk()
	 := .dominators
	for ,  := range .reversePostOrderedBasicBlocks {
		 := [.id]
		for !.loopHeader &&  !=  {
			 = [.id]
		}

		if  ==  && .loopHeader {
			.loopNestingForestRoots = append(.loopNestingForestRoots, )
		} else if  ==  {
		} else if .loopHeader {
			.loopNestingForestChildren = .loopNestingForestChildren.Append(&.varLengthBasicBlockPool, )
		}
	}

	if wazevoapi.SSALoggingEnabled {
		for ,  := range .loopNestingForestRoots {
			printLoopNestingForest(.(*basicBlock), 0)
		}
	}
}

func printLoopNestingForest( *basicBlock,  int) {
	fmt.Println(strings.Repeat("\t", ), "loop nesting forest root:", .ID())
	for ,  := range .loopNestingForestChildren.View() {
		fmt.Println(strings.Repeat("\t", +1), "child:", .ID())
		if .LoopHeader() {
			(.(*basicBlock), +2)
		}
	}
}

type dominatorSparseTree struct {
	time         int32
	euler        []*basicBlock
	first, depth []int32
	table        [][]int32
}

// passBuildDominatorTree builds the dominator tree for the function, and constructs builder.sparseTree.
func passBuildDominatorTree( *builder) {
	// First we materialize the children of each node in the dominator tree.
	 := .dominators
	for ,  := range .reversePostOrderedBasicBlocks {
		 := [.id]
		if  == nil {
			panic("BUG")
		} else if  ==  {
			// This is the entry block.
			continue
		}
		if  := .child;  == nil {
			.child = 
		} else {
			.child = 
			.sibling = 
		}
	}

	// Reset the state from the previous computation.
	 := .basicBlocksPool.Allocated()
	 := &.sparseTree
	.euler = append(.euler[:0], make([]*basicBlock, 2*-1)...)
	.first = append(.first[:0], make([]int32, )...)
	for  := range .first {
		.first[] = -1
	}
	.depth = append(.depth[:0], make([]int32, 2*-1)...)
	.time = 0

	// Start building the sparse tree.
	.eulerTour(.entryBlk(), 0)
	.buildSparseTable()
}

func ( *dominatorSparseTree) ( *basicBlock,  int32) {
	if wazevoapi.SSALoggingEnabled {
		fmt.Println(strings.Repeat("\t", int()), "euler tour:", .ID())
	}
	.euler[.time] = 
	.depth[.time] = 
	if .first[.id] == -1 {
		.first[.id] = .time
	}
	.time++

	for  := .child;  != nil;  = .sibling {
		.(, +1)
		.euler[.time] =  // add the current node again after visiting a child
		.depth[.time] = 
		.time++
	}
}

// buildSparseTable builds a sparse table for RMQ queries.
func ( *dominatorSparseTree) () {
	 := len(.depth)
	 := int(math.Log2(float64())) + 1
	 := .table

	if  >= len() {
		 = append(, make([][]int32, -len()+1)...)
	}
	for  := range  {
		if len([]) <  {
			[] = append([], make([]int32, -len([]))...)
		}
		[][0] = int32()
	}

	for  := 1; 1<< <= ; ++ {
		for  := 0; +(1<<)-1 < ; ++ {
			if .depth[[][-1]] < .depth[[+(1<<(-1))][-1]] {
				[][] = [][-1]
			} else {
				[][] = [+(1<<(-1))][-1]
			}
		}
	}
	.table = 
}

// rmq performs a range minimum query on the sparse table.
func ( *dominatorSparseTree) (,  int32) int32 {
	 := .table
	 := .depth
	 := int(math.Log2(float64( -  + 1)))
	if [[][]] <= [[-(1<<)+1][]] {
		return [][]
	}
	return [-(1<<)+1][]
}

// findLCA finds the LCA using the Euler tour and RMQ.
func ( *dominatorSparseTree) (,  BasicBlockID) *basicBlock {
	 := .first
	if [] > [] {
		,  = , 
	}
	return .euler[.rmq([], [])]
}