package ssa

import (
	
	
	

	
)

// Builder is used to builds SSA consisting of Basic Blocks per function.
type Builder interface {
	// Init must be called to reuse this builder for the next function.
	Init(typ *Signature)

	// Signature returns the Signature of the currently-compiled function.
	Signature() *Signature

	// BlockIDMax returns the maximum value of BasicBlocksID existing in the currently-compiled function.
	BlockIDMax() BasicBlockID

	// AllocateBasicBlock creates a basic block in SSA function.
	AllocateBasicBlock() BasicBlock

	// CurrentBlock returns the currently handled BasicBlock which is set by the latest call to SetCurrentBlock.
	CurrentBlock() BasicBlock

	// EntryBlock returns the entry BasicBlock of the currently-compiled function.
	EntryBlock() BasicBlock

	// SetCurrentBlock sets the instruction insertion target to the BasicBlock `b`.
	SetCurrentBlock(b BasicBlock)

	// DeclareVariable declares a Variable of the given Type.
	DeclareVariable(Type) Variable

	// DefineVariable defines a variable in the `block` with value.
	// The defining instruction will be inserted into the `block`.
	DefineVariable(variable Variable, value Value, block BasicBlock)

	// DefineVariableInCurrentBB is the same as DefineVariable except the definition is
	// inserted into the current BasicBlock. Alias to DefineVariable(x, y, CurrentBlock()).
	DefineVariableInCurrentBB(variable Variable, value Value)

	// AllocateInstruction returns a new Instruction.
	AllocateInstruction() *Instruction

	// InsertInstruction executes BasicBlock.InsertInstruction for the currently handled basic block.
	InsertInstruction(raw *Instruction)

	// allocateValue allocates an unused Value.
	allocateValue(typ Type) Value

	// MustFindValue searches the latest definition of the given Variable and returns the result.
	MustFindValue(variable Variable) Value

	// FindValueInLinearPath tries to find the latest definition of the given Variable in the linear path to the current BasicBlock.
	// If it cannot find the definition, or it's not sealed yet, it returns ValueInvalid.
	FindValueInLinearPath(variable Variable) Value

	// Seal declares that we've known all the predecessors to this block and were added via AddPred.
	// After calling this, AddPred will be forbidden.
	Seal(blk BasicBlock)

	// AnnotateValue is for debugging purpose.
	AnnotateValue(value Value, annotation string)

	// DeclareSignature appends the *Signature to be referenced by various instructions (e.g. OpcodeCall).
	DeclareSignature(signature *Signature)

	// Signatures returns the slice of declared Signatures.
	Signatures() []*Signature

	// ResolveSignature returns the Signature which corresponds to SignatureID.
	ResolveSignature(id SignatureID) *Signature

	// RunPasses runs various passes on the constructed SSA function.
	RunPasses()

	// Format returns the debugging string of the SSA function.
	Format() string

	// BlockIteratorBegin initializes the state to iterate over all the valid BasicBlock(s) compiled.
	// Combined with BlockIteratorNext, we can use this like:
	//
	// 	for blk := builder.BlockIteratorBegin(); blk != nil; blk = builder.BlockIteratorNext() {
	// 		// ...
	//	}
	//
	// The returned blocks are ordered in the order of AllocateBasicBlock being called.
	BlockIteratorBegin() BasicBlock

	// BlockIteratorNext advances the state for iteration initialized by BlockIteratorBegin.
	// Returns nil if there's no unseen BasicBlock.
	BlockIteratorNext() BasicBlock

	// ValuesInfo returns the data per Value used to lower the SSA in backend.
	// This is indexed by ValueID.
	ValuesInfo() []ValueInfo

	// BlockIteratorReversePostOrderBegin is almost the same as BlockIteratorBegin except it returns the BasicBlock in the reverse post-order.
	// This is available after RunPasses is run.
	BlockIteratorReversePostOrderBegin() BasicBlock

	// BlockIteratorReversePostOrderNext is almost the same as BlockIteratorPostOrderNext except it returns the BasicBlock in the reverse post-order.
	// This is available after RunPasses is run.
	BlockIteratorReversePostOrderNext() BasicBlock

	// ReturnBlock returns the BasicBlock which is used to return from the function.
	ReturnBlock() BasicBlock

	// InsertUndefined inserts an undefined instruction at the current position.
	InsertUndefined()

	// SetCurrentSourceOffset sets the current source offset. The incoming instruction will be annotated with this offset.
	SetCurrentSourceOffset(line SourceOffset)

	// LoopNestingForestRoots returns the roots of the loop nesting forest.
	LoopNestingForestRoots() []BasicBlock

	// LowestCommonAncestor returns the lowest common ancestor in the dominator tree of the given BasicBlock(s).
	LowestCommonAncestor(blk1, blk2 BasicBlock) BasicBlock

	// Idom returns the immediate dominator of the given BasicBlock.
	Idom(blk BasicBlock) BasicBlock

	// VarLengthPool returns the VarLengthPool of Value.
	VarLengthPool() *wazevoapi.VarLengthPool[Value]

	// InsertZeroValue inserts a zero value constant instruction of the given type.
	InsertZeroValue(t Type)

	// BasicBlock returns the BasicBlock of the given ID.
	BasicBlock(id BasicBlockID) BasicBlock

	// InstructionOfValue returns the Instruction that produces the given Value or nil if the Value is not produced by any Instruction.
	InstructionOfValue(v Value) *Instruction
}

// NewBuilder returns a new Builder implementation.
func () Builder {
	return &builder{
		instructionsPool:        wazevoapi.NewPool[Instruction](resetInstruction),
		basicBlocksPool:         wazevoapi.NewPool[basicBlock](resetBasicBlock),
		varLengthBasicBlockPool: wazevoapi.NewVarLengthPool[BasicBlock](),
		varLengthPool:           wazevoapi.NewVarLengthPool[Value](),
		valueAnnotations:        make(map[ValueID]string),
		signatures:              make(map[SignatureID]*Signature),
		returnBlk:               &basicBlock{id: basicBlockIDReturnBlock},
	}
}

// builder implements Builder interface.
type builder struct {
	basicBlocksPool  wazevoapi.Pool[basicBlock]
	instructionsPool wazevoapi.Pool[Instruction]
	varLengthPool    wazevoapi.VarLengthPool[Value]
	signatures       map[SignatureID]*Signature
	currentSignature *Signature

	// reversePostOrderedBasicBlocks are the BasicBlock(s) ordered in the reverse post-order after passCalculateImmediateDominators.
	reversePostOrderedBasicBlocks []*basicBlock
	currentBB                     *basicBlock
	returnBlk                     *basicBlock

	// nextValueID is used by builder.AllocateValue.
	nextValueID ValueID
	// nextVariable is used by builder.AllocateVariable.
	nextVariable Variable

	// valueAnnotations contains the annotations for each Value, only used for debugging.
	valueAnnotations map[ValueID]string

	// valuesInfo contains the data per Value used to lower the SSA in backend. This is indexed by ValueID.
	valuesInfo []ValueInfo

	// dominators stores the immediate dominator of each BasicBlock.
	// The index is blockID of the BasicBlock.
	dominators []*basicBlock
	sparseTree dominatorSparseTree

	varLengthBasicBlockPool wazevoapi.VarLengthPool[BasicBlock]

	// loopNestingForestRoots are the roots of the loop nesting forest.
	loopNestingForestRoots []BasicBlock

	// The followings are used for optimization passes/deterministic compilation.
	instStack       []*Instruction
	blkStack        []*basicBlock
	blkStack2       []*basicBlock
	redundantParams []redundantParam

	// blockIterCur is used to implement blockIteratorBegin and blockIteratorNext.
	blockIterCur int

	// donePreBlockLayoutPasses is true if all the passes before LayoutBlocks are called.
	donePreBlockLayoutPasses bool
	// doneBlockLayout is true if LayoutBlocks is called.
	doneBlockLayout bool
	// donePostBlockLayoutPasses is true if all the passes after LayoutBlocks are called.
	donePostBlockLayoutPasses bool

	currentSourceOffset SourceOffset

	// zeros are the zero value constants for each type.
	zeros [typeEnd]Value
}

// ValueInfo contains the data per Value used to lower the SSA in backend.
type ValueInfo struct {
	// RefCount is the reference count of the Value.
	RefCount uint32
	alias    Value
}

// redundantParam is a pair of the index of the redundant parameter and the Value.
// This is used to eliminate the redundant parameters in the optimization pass.
type redundantParam struct {
	// index is the index of the redundant parameter in the basicBlock.
	index int
	// uniqueValue is the Value which is passed to the redundant parameter.
	uniqueValue Value
}

// BasicBlock implements Builder.BasicBlock.
func ( *builder) ( BasicBlockID) BasicBlock {
	return .basicBlock()
}

func ( *builder) ( BasicBlockID) *basicBlock {
	if  == basicBlockIDReturnBlock {
		return .returnBlk
	}
	return .basicBlocksPool.View(int())
}

// InsertZeroValue implements Builder.InsertZeroValue.
func ( *builder) ( Type) {
	if .zeros[].Valid() {
		return
	}
	 := .AllocateInstruction()
	switch  {
	case TypeI32:
		.AsIconst32(0)
	case TypeI64:
		.AsIconst64(0)
	case TypeF32:
		.AsF32const(0)
	case TypeF64:
		.AsF64const(0)
	case TypeV128:
		.AsVconst(0, 0)
	default:
		panic("TODO: " + .String())
	}
	.zeros[] = .Insert().Return()
}

func ( *builder) () *wazevoapi.VarLengthPool[Value] {
	return &.varLengthPool
}

// ReturnBlock implements Builder.ReturnBlock.
func ( *builder) () BasicBlock {
	return .returnBlk
}

// Init implements Builder.Reset.
func ( *builder) ( *Signature) {
	.nextVariable = 0
	.currentSignature = 
	.zeros = [typeEnd]Value{ValueInvalid, ValueInvalid, ValueInvalid, ValueInvalid, ValueInvalid, ValueInvalid}
	resetBasicBlock(.returnBlk)
	.instructionsPool.Reset()
	.basicBlocksPool.Reset()
	.varLengthPool.Reset()
	.varLengthBasicBlockPool.Reset()
	.donePreBlockLayoutPasses = false
	.doneBlockLayout = false
	.donePostBlockLayoutPasses = false
	for ,  := range .signatures {
		.used = false
	}

	.redundantParams = .redundantParams[:0]
	.blkStack = .blkStack[:0]
	.blkStack2 = .blkStack2[:0]
	.dominators = .dominators[:0]
	.loopNestingForestRoots = .loopNestingForestRoots[:0]
	.basicBlocksPool.Reset()

	for  := ValueID(0);  < .nextValueID; ++ {
		delete(.valueAnnotations, )
		.valuesInfo[] = ValueInfo{alias: ValueInvalid}
	}
	.nextValueID = 0
	.reversePostOrderedBasicBlocks = .reversePostOrderedBasicBlocks[:0]
	.doneBlockLayout = false
	.currentSourceOffset = sourceOffsetUnknown
}

// Signature implements Builder.Signature.
func ( *builder) () *Signature {
	return .currentSignature
}

// AnnotateValue implements Builder.AnnotateValue.
func ( *builder) ( Value,  string) {
	.valueAnnotations[.ID()] = 
}

// AllocateInstruction implements Builder.AllocateInstruction.
func ( *builder) () *Instruction {
	 := .instructionsPool.Allocate()
	.id = .instructionsPool.Allocated()
	return 
}

// DeclareSignature implements Builder.AnnotateValue.
func ( *builder) ( *Signature) {
	.signatures[.ID] = 
	.used = false
}

// Signatures implements Builder.Signatures.
func ( *builder) () ( []*Signature) {
	for ,  := range .signatures {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		return [].ID < [].ID
	})
	return
}

// SetCurrentSourceOffset implements Builder.SetCurrentSourceOffset.
func ( *builder) ( SourceOffset) {
	.currentSourceOffset = 
}

func ( *builder) () ( []*Signature) {
	for ,  := range .signatures {
		if .used {
			 = append(, )
		}
	}
	sort.Slice(, func(,  int) bool {
		return [].ID < [].ID
	})
	return
}

// ResolveSignature implements Builder.ResolveSignature.
func ( *builder) ( SignatureID) *Signature {
	return .signatures[]
}

// AllocateBasicBlock implements Builder.AllocateBasicBlock.
func ( *builder) () BasicBlock {
	return .allocateBasicBlock()
}

// allocateBasicBlock allocates a new basicBlock.
func ( *builder) () *basicBlock {
	 := BasicBlockID(.basicBlocksPool.Allocated())
	 := .basicBlocksPool.Allocate()
	.id = 
	return 
}

// Idom implements Builder.Idom.
func ( *builder) ( BasicBlock) BasicBlock {
	return .dominators[.ID()]
}

// InsertInstruction implements Builder.InsertInstruction.
func ( *builder) ( *Instruction) {
	.currentBB.insertInstruction(, )

	if  := .currentSourceOffset; .Valid() {
		// Emit the source offset info only when the instruction has side effect because
		// these are the only instructions that are accessed by stack unwinding.
		// This reduces the significant amount of the offset info in the binary.
		if .sideEffect() != sideEffectNone {
			.annotateSourceOffset()
		}
	}

	 := instructionReturnTypes[.opcode]
	if  == nil {
		panic("TODO: " + .Format())
	}

	,  := (, )
	if .invalid() {
		return
	}

	 := .allocateValue()
	.rValue = .setInstructionID(.id)

	 := len()
	if  == 0 {
		return
	}

	 := .varLengthPool.Allocate()
	for  := 0;  < ; ++ {
		 := .allocateValue([])
		 = .Append(&.varLengthPool, .setInstructionID(.id))
	}
	.rValues = 
}

// DefineVariable implements Builder.DefineVariable.
func ( *builder) ( Variable,  Value,  BasicBlock) {
	 := .(*basicBlock)
	.lastDefinitions[] = 
}

// DefineVariableInCurrentBB implements Builder.DefineVariableInCurrentBB.
func ( *builder) ( Variable,  Value) {
	.DefineVariable(, , .currentBB)
}

// SetCurrentBlock implements Builder.SetCurrentBlock.
func ( *builder) ( BasicBlock) {
	.currentBB = .(*basicBlock)
}

// CurrentBlock implements Builder.CurrentBlock.
func ( *builder) () BasicBlock {
	return .currentBB
}

// EntryBlock implements Builder.EntryBlock.
func ( *builder) () BasicBlock {
	return .entryBlk()
}

// DeclareVariable implements Builder.DeclareVariable.
func ( *builder) ( Type) Variable {
	 := .nextVariable
	.nextVariable++
	return .setType()
}

// allocateValue implements Builder.AllocateValue.
func ( *builder) ( Type) ( Value) {
	 = Value(.nextValueID)
	 = .setType()
	.nextValueID++
	return
}

// FindValueInLinearPath implements Builder.FindValueInLinearPath.
func ( *builder) ( Variable) Value {
	return .findValueInLinearPath(, .currentBB)
}

func ( *builder) ( Variable,  *basicBlock) Value {
	if ,  := .lastDefinitions[];  {
		return 
	} else if !.sealed {
		return ValueInvalid
	}

	if  := .singlePred;  != nil {
		// If this block is sealed and have only one predecessor,
		// we can use the value in that block without ambiguity on definition.
		return .(, )
	}
	if len(.preds) == 1 {
		panic("BUG")
	}
	return ValueInvalid
}

// MustFindValue implements Builder.MustFindValue.
func ( *builder) ( Variable) Value {
	return .findValue(.getType(), , .currentBB)
}

// findValue recursively tries to find the latest definition of a `variable`. The algorithm is described in
// the section 2 of the paper https://link.springer.com/content/pdf/10.1007/978-3-642-37051-9_6.pdf.
//
// TODO: reimplement this in iterative, not recursive, to avoid stack overflow.
func ( *builder) ( Type,  Variable,  *basicBlock) Value {
	if ,  := .lastDefinitions[];  {
		// The value is already defined in this block!
		return 
	} else if !.sealed { // Incomplete CFG as in the paper.
		// If this is not sealed, that means it might have additional unknown predecessor later on.
		// So we temporarily define the placeholder value here (not add as a parameter yet!),
		// and record it as unknown.
		// The unknown values are resolved when we call seal this block via BasicBlock.Seal().
		 := .allocateValue()
		if wazevoapi.SSALoggingEnabled {
			fmt.Printf("adding unknown value placeholder for %s at %d\n", , .id)
		}
		.lastDefinitions[] = 
		.unknownValues = append(.unknownValues, unknownValue{
			variable: ,
			value:    ,
		})
		return 
	} else if .EntryBlock() {
		// If this is the entry block, we reach the uninitialized variable which has zero value.
		return .zeros[.getType()]
	}

	if  := .singlePred;  != nil {
		// If this block is sealed and have only one predecessor,
		// we can use the value in that block without ambiguity on definition.
		return .(, , )
	} else if len(.preds) == 0 {
		panic("BUG: value is not defined for " + .String())
	}

	// If this block has multiple predecessors, we have to gather the definitions,
	// and treat them as an argument to this block.
	//
	// But before that, we have to check if the possible definitions are the same Value.
	 := .allocateValue()
	// Break the cycle by defining the variable with the tmpValue.
	.DefineVariable(, , )
	// Check all the predecessors if they have the same definition.
	 := ValueInvalid
	for  := range .preds {
		 := .(, , .preds[].blk)
		if  == ValueInvalid {
			 = 
		} else if  !=  {
			 = ValueInvalid
			break
		}
	}

	if  != ValueInvalid {
		// If all the predecessors have the same definition, we can use that value.
		.alias(, )
		return 
	} else {
		// Otherwise, add the tmpValue to this block as a parameter which may or may not be redundant, but
		// later we eliminate trivial params in an optimization pass. This must be done before finding the
		// definitions in the predecessors so that we can break the cycle.
		.addParamOn(, )
		// After the new param is added, we have to manipulate the original branching instructions
		// in predecessors so that they would pass the definition of `variable` as the argument to
		// the newly added PHI.
		for  := range .preds {
			 := &.preds[]
			 := .(, , .blk)
			.branch.addArgumentBranchInst(, )
		}
		return 
	}
}

// Seal implements Builder.Seal.
func ( *builder) ( BasicBlock) {
	 := .(*basicBlock)
	if len(.preds) == 1 {
		.singlePred = .preds[0].blk
	}
	.sealed = true

	for ,  := range .unknownValues {
		,  := .variable, .value
		 := .getType()
		.addParamOn(, )
		for  := range .preds {
			 := &.preds[]
			 := .findValue(, , .blk)
			if !.Valid() {
				panic("BUG: value is not defined anywhere in the predecessors in the CFG")
			}
			.branch.addArgumentBranchInst(, )
		}
	}
}

// Format implements Builder.Format.
func ( *builder) () string {
	 := strings.Builder{}
	 := .usedSignatures()
	if len() > 0 {
		.WriteByte('\n')
		.WriteString("signatures:\n")
		for ,  := range  {
			.WriteByte('\t')
			.WriteString(.String())
			.WriteByte('\n')
		}
	}

	var ,  func() *basicBlock
	if .doneBlockLayout {
		,  = .blockIteratorReversePostOrderBegin, .blockIteratorReversePostOrderNext
	} else {
		,  = .blockIteratorBegin, .blockIteratorNext
	}
	for  := ();  != nil;  = () {
		.WriteByte('\n')
		.WriteString(.formatHeader())
		.WriteByte('\n')

		for  := .Root();  != nil;  = .Next() {
			.WriteByte('\t')
			.WriteString(.Format())
			.WriteByte('\n')
		}
	}
	return .String()
}

// BlockIteratorNext implements Builder.BlockIteratorNext.
func ( *builder) () BasicBlock {
	if  := .blockIteratorNext();  == nil {
		return nil // BasicBlock((*basicBlock)(nil)) != BasicBlock(nil)
	} else {
		return 
	}
}

// BlockIteratorNext implements Builder.BlockIteratorNext.
func ( *builder) () *basicBlock {
	 := .blockIterCur
	for {
		if  == .basicBlocksPool.Allocated() {
			return nil
		}
		 := .basicBlocksPool.View()
		++
		if !.invalid {
			.blockIterCur = 
			return 
		}
	}
}

// BlockIteratorBegin implements Builder.BlockIteratorBegin.
func ( *builder) () BasicBlock {
	return .blockIteratorBegin()
}

// BlockIteratorBegin implements Builder.BlockIteratorBegin.
func ( *builder) () *basicBlock {
	.blockIterCur = 0
	return .blockIteratorNext()
}

// BlockIteratorReversePostOrderBegin implements Builder.BlockIteratorReversePostOrderBegin.
func ( *builder) () BasicBlock {
	return .blockIteratorReversePostOrderBegin()
}

// BlockIteratorBegin implements Builder.BlockIteratorBegin.
func ( *builder) () *basicBlock {
	.blockIterCur = 0
	return .blockIteratorReversePostOrderNext()
}

// BlockIteratorReversePostOrderNext implements Builder.BlockIteratorReversePostOrderNext.
func ( *builder) () BasicBlock {
	if  := .blockIteratorReversePostOrderNext();  == nil {
		return nil // BasicBlock((*basicBlock)(nil)) != BasicBlock(nil)
	} else {
		return 
	}
}

// BlockIteratorNext implements Builder.BlockIteratorNext.
func ( *builder) () *basicBlock {
	if .blockIterCur >= len(.reversePostOrderedBasicBlocks) {
		return nil
	} else {
		 := .reversePostOrderedBasicBlocks[.blockIterCur]
		.blockIterCur++
		return 
	}
}

// ValuesInfo implements Builder.ValuesInfo.
func ( *builder) () []ValueInfo {
	return .valuesInfo
}

// alias records the alias of the given values. The alias(es) will be
// eliminated in the optimization pass via resolveArgumentAlias.
func ( *builder) (,  Value) {
	 := int(.ID())
	if  >= len(.valuesInfo) {
		 :=  + 1 - len(.valuesInfo)
		.valuesInfo = append(.valuesInfo, make([]ValueInfo, )...)
		 := .valuesInfo[len(.valuesInfo)-:]
		for  := range  {
			[].alias = ValueInvalid
		}
	}
	.valuesInfo[].alias = 
}

// resolveArgumentAlias resolves the alias of the arguments of the given instruction.
func ( *builder) ( *Instruction) {
	if .v.Valid() {
		.v = .resolveAlias(.v)
	}

	if .v2.Valid() {
		.v2 = .resolveAlias(.v2)
	}

	if .v3.Valid() {
		.v3 = .resolveAlias(.v3)
	}

	 := .vs.View()
	for ,  := range  {
		[] = .resolveAlias()
	}
}

// resolveAlias resolves the alias of the given value.
func ( *builder) ( Value) Value {
	 := .valuesInfo
	 := ValueID(len())
	// Some aliases are chained, so we need to resolve them recursively.
	for {
		 := .ID()
		if  <  && [].alias.Valid() {
			 = [].alias
		} else {
			break
		}
	}
	return 
}

// entryBlk returns the entry block of the function.
func ( *builder) () *basicBlock {
	return .basicBlocksPool.View(0)
}

// isDominatedBy returns true if the given block `n` is dominated by the given block `d`.
// Before calling this, the builder must pass by passCalculateImmediateDominators.
func ( *builder) ( *basicBlock,  *basicBlock) bool {
	if len(.dominators) == 0 {
		panic("BUG: passCalculateImmediateDominators must be called before calling isDominatedBy")
	}
	 := .entryBlk()
	 := .dominators
	for  !=  &&  !=  {
		 = [.id]
	}
	return  == 
}

// BlockIDMax implements Builder.BlockIDMax.
func ( *builder) () BasicBlockID {
	return BasicBlockID(.basicBlocksPool.Allocated())
}

// InsertUndefined implements Builder.InsertUndefined.
func ( *builder) () {
	 := .AllocateInstruction()
	.opcode = OpcodeUndefined
	.InsertInstruction()
}

// LoopNestingForestRoots implements Builder.LoopNestingForestRoots.
func ( *builder) () []BasicBlock {
	return .loopNestingForestRoots
}

// LowestCommonAncestor implements Builder.LowestCommonAncestor.
func ( *builder) (,  BasicBlock) BasicBlock {
	return .sparseTree.findLCA(.ID(), .ID())
}

// InstructionOfValue returns the instruction that produces the given Value, or nil
// if the Value is not produced by any instruction.
func ( *builder) ( Value) *Instruction {
	 := .instructionID()
	if  <= 0 {
		return nil
	}
	return .instructionsPool.View( - 1)
}