package amd64

import (
	
	
)

// regAllocFn implements regalloc.Function.
type regAllocFn struct {
	ssaB                   ssa.Builder
	m                      *machine
	loopNestingForestRoots []ssa.BasicBlock
	blockIter              int
}

// PostOrderBlockIteratorBegin implements regalloc.Function.
func ( *regAllocFn) () *labelPosition {
	.blockIter = len(.m.orderedSSABlockLabelPos) - 1
	return .PostOrderBlockIteratorNext()
}

// PostOrderBlockIteratorNext implements regalloc.Function.
func ( *regAllocFn) () *labelPosition {
	if .blockIter < 0 {
		return nil
	}
	 := .m.orderedSSABlockLabelPos[.blockIter]
	.blockIter--
	return 
}

// ReversePostOrderBlockIteratorBegin implements regalloc.Function.
func ( *regAllocFn) () *labelPosition {
	.blockIter = 0
	return .ReversePostOrderBlockIteratorNext()
}

// ReversePostOrderBlockIteratorNext implements regalloc.Function.
func ( *regAllocFn) () *labelPosition {
	if .blockIter >= len(.m.orderedSSABlockLabelPos) {
		return nil
	}
	 := .m.orderedSSABlockLabelPos[.blockIter]
	.blockIter++
	return 
}

// ClobberedRegisters implements regalloc.Function.
func ( *regAllocFn) ( []regalloc.VReg) {
	.m.clobberedRegs = append(.m.clobberedRegs[:0], ...)
}

// LoopNestingForestRoots implements regalloc.Function.
func ( *regAllocFn) () int {
	.loopNestingForestRoots = .ssaB.LoopNestingForestRoots()
	return len(.loopNestingForestRoots)
}

// LoopNestingForestRoot implements regalloc.Function.
func ( *regAllocFn) ( int) *labelPosition {
	 := .loopNestingForestRoots[]
	 := .m.getOrAllocateSSABlockLabelPosition()
	return 
}

// LowestCommonAncestor implements regalloc.Function.
func ( *regAllocFn) (,  *labelPosition) *labelPosition {
	 := .ssaB.LowestCommonAncestor(.sb, .sb)
	 := .m.getOrAllocateSSABlockLabelPosition()
	return 
}

// Idom implements regalloc.Function.
func ( *regAllocFn) ( *labelPosition) *labelPosition {
	 := .ssaB.Idom(.sb)
	 := .m.getOrAllocateSSABlockLabelPosition()
	return 
}

// SwapBefore implements regalloc.Function.
func ( *regAllocFn) (, ,  regalloc.VReg,  *instruction) {
	.m.swap(.prev, , , )
}

// StoreRegisterBefore implements regalloc.Function.
func ( *regAllocFn) ( regalloc.VReg,  *instruction) {
	 := .m
	.insertStoreRegisterAt(, , false)
}

// StoreRegisterAfter implements regalloc.Function.
func ( *regAllocFn) ( regalloc.VReg,  *instruction) {
	 := .m
	.insertStoreRegisterAt(, , true)
}

// ReloadRegisterBefore implements regalloc.Function.
func ( *regAllocFn) ( regalloc.VReg,  *instruction) {
	 := .m
	.insertReloadRegisterAt(, , false)
}

// ReloadRegisterAfter implements regalloc.Function.
func ( *regAllocFn) ( regalloc.VReg,  *instruction) {
	 := .m
	.insertReloadRegisterAt(, , true)
}

// InsertMoveBefore implements regalloc.Function.
func ( *regAllocFn) (,  regalloc.VReg,  *instruction) {
	.m.insertMoveBefore(, , )
}

// LoopNestingForestChild implements regalloc.Function.
func ( *regAllocFn) ( *labelPosition,  int) *labelPosition {
	 := .sb.LoopNestingForestChildren()[]
	return .m.getOrAllocateSSABlockLabelPosition()
}

// Succ implements regalloc.Block.
func ( *regAllocFn) ( *labelPosition,  int) *labelPosition {
	 := .sb.Succ()
	if .ReturnBlock() {
		return nil
	}
	return .m.getOrAllocateSSABlockLabelPosition()
}

// Pred implements regalloc.Block.
func ( *regAllocFn) ( *labelPosition,  int) *labelPosition {
	 := .sb.Pred()
	return .m.getOrAllocateSSABlockLabelPosition()
}

// BlockParams implements regalloc.Function.
func ( *regAllocFn) ( *labelPosition,  *[]regalloc.VReg) []regalloc.VReg {
	 := .m.c
	* = (*)[:0]
	for  := 0;  < .sb.Params(); ++ {
		 := .VRegOf(.sb.Param())
		* = append(*, )
	}
	return *
}

// ID implements regalloc.Block.
func ( *labelPosition) () int32 {
	return int32(.sb.ID())
}

// InstrIteratorBegin implements regalloc.Block.
func ( *labelPosition) () *instruction {
	 := .begin
	.cur = 
	return 
}

// InstrIteratorNext implements regalloc.Block.
func ( *labelPosition) () *instruction {
	for {
		if .cur == .end {
			return nil
		}
		 := .cur.next
		.cur = 
		if  == nil {
			return nil
		} else if .addedBeforeRegAlloc {
			// Only concerned about the instruction added before regalloc.
			return 
		}
	}
}

// InstrRevIteratorBegin implements regalloc.Block.
func ( *labelPosition) () *instruction {
	.cur = .end
	return .cur
}

// InstrRevIteratorNext implements regalloc.Block.
func ( *labelPosition) () *instruction {
	for {
		if .cur == .begin {
			return nil
		}
		 := .cur.prev
		.cur = 
		if  == nil {
			return nil
		} else if .addedBeforeRegAlloc {
			// Only concerned about the instruction added before regalloc.
			return 
		}
	}
}

// FirstInstr implements regalloc.Block.
func ( *labelPosition) () *instruction { return .begin }

// LastInstrForInsertion implements regalloc.Block.
func ( *labelPosition) () *instruction {
	return lastInstrForInsertion(.begin, .end)
}

// Preds implements regalloc.Block.
func ( *labelPosition) () int { return .sb.Preds() }

// Entry implements regalloc.Block.
func ( *labelPosition) () bool { return .sb.EntryBlock() }

// Succs implements regalloc.Block.
func ( *labelPosition) () int { return .sb.Succs() }

// LoopHeader implements regalloc.Block.
func ( *labelPosition) () bool { return .sb.LoopHeader() }

// LoopNestingForestChildren implements regalloc.Block.
func ( *labelPosition) () int {
	return len(.sb.LoopNestingForestChildren())
}

func ( *machine) (,  regalloc.VReg,  *instruction) {
	 := .RegType()
	if  != .RegType() {
		panic("BUG: src and dst must have the same type")
	}

	 := .allocateInstr()
	if  == regalloc.RegTypeInt {
		.asMovRR(, , true)
	} else {
		.asXmmUnaryRmR(sseOpcodeMovdqu, newOperandReg(), )
	}

	 := .prev
	 := .next
	 = linkInstr(, )
	linkInstr(, )
}

func ( *machine) ( regalloc.VReg,  *instruction,  bool) *instruction {
	if !.IsRealReg() {
		panic("BUG: VReg must be backed by real reg to be stored")
	}

	 := .c.TypeOf()

	var ,  *instruction
	if  {
		,  = , .next
	} else {
		,  = .prev, 
	}

	 := .getVRegSpillSlotOffsetFromSP(.ID(), .Size())
	 := .allocateInstr()
	 := newOperandMem(.newAmodeImmReg(uint32(), rspVReg))
	switch  {
	case ssa.TypeI32:
		.asMovRM(, , 4)
	case ssa.TypeI64:
		.asMovRM(, , 8)
	case ssa.TypeF32:
		.asXmmMovRM(sseOpcodeMovss, , )
	case ssa.TypeF64:
		.asXmmMovRM(sseOpcodeMovsd, , )
	case ssa.TypeV128:
		.asXmmMovRM(sseOpcodeMovdqu, , )
	}

	 = linkInstr(, )
	return linkInstr(, )
}

func ( *machine) ( regalloc.VReg,  *instruction,  bool) *instruction {
	if !.IsRealReg() {
		panic("BUG: VReg must be backed by real reg to be stored")
	}

	 := .c.TypeOf()
	var ,  *instruction
	if  {
		,  = , .next
	} else {
		,  = .prev, 
	}

	// Load the value to the temporary.
	 := .allocateInstr()
	 := .getVRegSpillSlotOffsetFromSP(.ID(), .Size())
	 := newOperandMem(.newAmodeImmReg(uint32(), rspVReg))
	switch  {
	case ssa.TypeI32:
		.asMovzxRmR(extModeLQ, , )
	case ssa.TypeI64:
		.asMov64MR(, )
	case ssa.TypeF32:
		.asXmmUnaryRmR(sseOpcodeMovss, , )
	case ssa.TypeF64:
		.asXmmUnaryRmR(sseOpcodeMovsd, , )
	case ssa.TypeV128:
		.asXmmUnaryRmR(sseOpcodeMovdqu, , )
	default:
		panic("BUG")
	}

	 = linkInstr(, )
	return linkInstr(, )
}

func ( *machine) ( *instruction, , ,  regalloc.VReg) {
	if .RegType() == regalloc.RegTypeInt {
		 := .next
		 := .allocateInstr().asXCHG(, newOperandReg(), 8)
		 = linkInstr(, )
		linkInstr(, )
	} else {
		if .Valid() {
			 := .next
			.insertMoveBefore(, , )
			.insertMoveBefore(, , )
			.insertMoveBefore(, , )
		} else {
			 := .next
			 := .RealReg()
			// Temporarily spill x1 to stack.
			 = .insertStoreRegisterAt(, , true).prev
			// Then move x2 to x1.
			 = linkInstr(, .allocateInstr().asXmmUnaryRmR(sseOpcodeMovdqa, newOperandReg(), ))
			linkInstr(, )
			// Then reload the original value on x1 from stack to r2.
			.insertReloadRegisterAt(.SetRealReg(), , true)
		}
	}
}

func lastInstrForInsertion(,  *instruction) *instruction {
	 := 
	for .kind == nop0 {
		 = .prev
		if  ==  {
			return 
		}
	}
	switch .kind {
	case jmp:
		return 
	default:
		return 
	}
}