package amd64

import (
	
	
)

// PostRegAlloc implements backend.Machine.
func ( *machine) () {
	.setupPrologue()
	.postRegAlloc()
}

func ( *machine) () {
	 := .rootInstr
	 := .next

	// At this point, we have the stack layout as follows:
	//
	//                   (high address)
	//                 +-----------------+ <----- RBP (somewhere in the middle of the stack)
	//                 |     .......     |
	//                 |      ret Y      |
	//                 |     .......     |
	//                 |      ret 0      |
	//                 |      arg X      |
	//                 |     .......     |
	//                 |      arg 1      |
	//                 |      arg 0      |
	//                 |   Return Addr   |
	//       RSP ----> +-----------------+
	//                    (low address)

	// First, we push the RBP, and update the RBP to the current RSP.
	//
	//                   (high address)                     (high address)
	//       RBP ----> +-----------------+                +-----------------+
	//                 |     .......     |                |     .......     |
	//                 |      ret Y      |                |      ret Y      |
	//                 |     .......     |                |     .......     |
	//                 |      ret 0      |                |      ret 0      |
	//                 |      arg X      |                |      arg X      |
	//                 |     .......     |     ====>      |     .......     |
	//                 |      arg 1      |                |      arg 1      |
	//                 |      arg 0      |                |      arg 0      |
	//                 |   Return Addr   |                |   Return Addr   |
	//       RSP ----> +-----------------+                |    Caller_RBP   |
	//                    (low address)                   +-----------------+ <----- RSP, RBP
	//
	 = .setupRBPRSP()

	if !.stackBoundsCheckDisabled {
		 = .insertStackBoundsCheck(.requiredStackSize(), )
	}

	//
	//            (high address)
	//          +-----------------+                  +-----------------+
	//          |     .......     |                  |     .......     |
	//          |      ret Y      |                  |      ret Y      |
	//          |     .......     |                  |     .......     |
	//          |      ret 0      |                  |      ret 0      |
	//          |      arg X      |                  |      arg X      |
	//          |     .......     |                  |     .......     |
	//          |      arg 1      |                  |      arg 1      |
	//          |      arg 0      |                  |      arg 0      |
	//          |      xxxxx      |                  |      xxxxx      |
	//          |   Return Addr   |                  |   Return Addr   |
	//          |    Caller_RBP   |      ====>       |    Caller_RBP   |
	// RBP,RSP->+-----------------+                  +-----------------+ <----- RBP
	//             (low address)                     |   clobbered M   |
	//                                               |   clobbered 1   |
	//                                               |   ...........   |
	//                                               |   clobbered 0   |
	//                                               +-----------------+ <----- RSP
	//
	if  := .clobberedRegs; len() > 0 {
		for  := range  {
			 := [len()-1-] // Reverse order.
			if .RegType() == regalloc.RegTypeInt {
				 = linkInstr(, .allocateInstr().asPush64(newOperandReg()))
			} else {
				// Push the XMM register is not supported by the PUSH instruction.
				 = .addRSP(-16, )
				 := .allocateInstr().asXmmMovRM(
					sseOpcodeMovdqu, , newOperandMem(.newAmodeImmReg(0, rspVReg)),
				)
				 = linkInstr(, )
			}
		}
	}

	if  := .spillSlotSize;  > 0 {
		// Simply decrease the RSP to allocate the spill slots.
		// 		sub $size, %rsp
		 = linkInstr(, .allocateInstr().asAluRmiR(aluRmiROpcodeSub, newOperandImm32(uint32()), rspVReg, true))

		// At this point, we have the stack layout as follows:
		//
		//            (high address)
		//          +-----------------+
		//          |     .......     |
		//          |      ret Y      |
		//          |     .......     |
		//          |      ret 0      |
		//          |      arg X      |
		//          |     .......     |
		//          |      arg 1      |
		//          |      arg 0      |
		//          |   ReturnAddress |
		//          |   Caller_RBP    |
		//          +-----------------+ <--- RBP
		//          |    clobbered M  |
		//          |   ............  |
		//          |    clobbered 1  |
		//          |    clobbered 0  |
		//          |   spill slot N  |
		//          |   ............  |
		//          |   spill slot 0  |
		//          +-----------------+ <--- RSP
		//             (low address)
	}

	linkInstr(, )
}

// postRegAlloc does multiple things while walking through the instructions:
// 1. Inserts the epilogue code.
// 2. Removes the redundant copy instruction.
// 3. Inserts the dec/inc RSP instruction right before/after the call instruction.
// 4. Lowering that is supposed to be done after regalloc.
func ( *machine) () {
	for  := .rootInstr;  != nil;  = .next {
		switch  := .kind;  {
		case ret:
			.setupEpilogueAfter(.prev)
			continue
		case fcvtToSintSequence, fcvtToUintSequence:
			.pendingInstructions = .pendingInstructions[:0]
			if  == fcvtToSintSequence {
				.lowerFcvtToSintSequenceAfterRegalloc()
			} else {
				.lowerFcvtToUintSequenceAfterRegalloc()
			}
			 := .prev
			 := .next
			 := 
			for ,  := range .pendingInstructions {
				 = linkInstr(, )
			}
			linkInstr(, )
			continue
		case xmmCMov:
			.pendingInstructions = .pendingInstructions[:0]
			.lowerXmmCmovAfterRegAlloc()
			 := .prev
			 := .next
			 := 
			for ,  := range .pendingInstructions {
				 = linkInstr(, )
			}
			linkInstr(, )
			continue
		case idivRemSequence:
			.pendingInstructions = .pendingInstructions[:0]
			.lowerIDivRemSequenceAfterRegAlloc()
			 := .prev
			 := .next
			 := 
			for ,  := range .pendingInstructions {
				 = linkInstr(, )
			}
			linkInstr(, )
			continue
		case call, callIndirect:
			// At this point, reg alloc is done, therefore we can safely insert dec/inc RPS instruction
			// right before/after the call instruction. If this is done before reg alloc, the stack slot
			// can point to the wrong location and therefore results in a wrong value.
			 := 
			 := .next
			, , , ,  := backend.ABIInfoFromUint64(.u2)
			if  > 0 {
				 := .allocateInstr().asAluRmiR(aluRmiROpcodeSub, newOperandImm32(), rspVReg, true)
				linkInstr(.prev, )
				linkInstr(, )
				 := .allocateInstr().asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(), rspVReg, true)
				linkInstr(, )
				linkInstr(, )
			}
			continue
		case tailCall, tailCallIndirect:
			// At this point, reg alloc is done, therefore we can safely insert dec RPS instruction
			// right before the tail call (jump) instruction. If this is done before reg alloc, the stack slot
			// can point to the wrong location and therefore results in a wrong value.
			 := 
			, , , ,  := backend.ABIInfoFromUint64(.u2)
			if  > 0 {
				 := .allocateInstr().asAluRmiR(aluRmiROpcodeSub, newOperandImm32(), rspVReg, true)
				linkInstr(.prev, )
				linkInstr(, )
			}
			// In a tail call, we insert the epilogue before the jump instruction.
			.setupEpilogueAfter(.prev)
			// If this has been encoded as a proper tail call, we can remove the trailing instructions
			// For details, see internal/engine/RATIONALE.md
			.removeUntilRet(.next)
			continue
		}

		// Removes the redundant copy instruction.
		if .IsCopy() && .op1.reg().RealReg() == .op2.reg().RealReg() {
			,  := .prev, .next
			// Remove the copy instruction.
			.next = 
			if  != nil {
				.prev = 
			}
		}
	}
}

func ( *machine) ( *instruction) {
	 := .next

	// At this point, we have the stack layout as follows:
	//
	//            (high address)
	//          +-----------------+
	//          |     .......     |
	//          |      ret Y      |
	//          |     .......     |
	//          |      ret 0      |
	//          |      arg X      |
	//          |     .......     |
	//          |      arg 1      |
	//          |      arg 0      |
	//          |   ReturnAddress |
	//          |   Caller_RBP    |
	//          +-----------------+ <--- RBP
	//          |    clobbered M  |
	//          |   ............  |
	//          |    clobbered 1  |
	//          |    clobbered 0  |
	//          |   spill slot N  |
	//          |   ............  |
	//          |   spill slot 0  |
	//          +-----------------+ <--- RSP
	//             (low address)

	if  := .spillSlotSize;  > 0 {
		// Simply increase the RSP to free the spill slots.
		// 		add $size, %rsp
		 = linkInstr(, .allocateInstr().asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(uint32()), rspVReg, true))
	}

	//
	//             (high address)
	//            +-----------------+                     +-----------------+
	//            |     .......     |                     |     .......     |
	//            |      ret Y      |                     |      ret Y      |
	//            |     .......     |                     |     .......     |
	//            |      ret 0      |                     |      ret 0      |
	//            |      arg X      |                     |      arg X      |
	//            |     .......     |                     |     .......     |
	//            |      arg 1      |                     |      arg 1      |
	//            |      arg 0      |                     |      arg 0      |
	//            |   ReturnAddress |                     |   ReturnAddress |
	//            |    Caller_RBP   |                     |    Caller_RBP   |
	//   RBP ---> +-----------------+      ========>      +-----------------+ <---- RSP, RBP
	//            |    clobbered M  |
	//            |   ............  |
	//            |    clobbered 1  |
	//            |    clobbered 0  |
	//   RSP ---> +-----------------+
	//               (low address)
	//
	if  := .clobberedRegs; len() > 0 {
		for ,  := range  {
			if .RegType() == regalloc.RegTypeInt {
				 = linkInstr(, .allocateInstr().asPop64())
			} else {
				// Pop the XMM register is not supported by the POP instruction.
				 := .allocateInstr().asXmmUnaryRmR(
					sseOpcodeMovdqu, newOperandMem(.newAmodeImmReg(0, rspVReg)), ,
				)
				 = linkInstr(, )
				 = .addRSP(16, )
			}
		}
	}

	// Now roll back the RSP to RBP, and pop the caller's RBP.
	 = .revertRBPRSP()

	linkInstr(, )
}

// removeUntilRet removes the instructions starting from `cur` until the first `ret` instruction.
func ( *machine) ( *instruction) {
	for ;  != nil;  = .next {
		,  := .prev, .next
		.next = 
		if  != nil {
			.prev = 
		}
		if .kind == ret {
			return
		}
	}
}

func ( *machine) ( int32,  *instruction) *instruction {
	if  == 0 {
		return 
	}
	 := aluRmiROpcodeAdd
	if  < 0 {
		 = aluRmiROpcodeSub
		 = -
	}
	return linkInstr(, .allocateInstr().asAluRmiR(, newOperandImm32(uint32()), rspVReg, true))
}

func ( *machine) ( *instruction) *instruction {
	 = linkInstr(, .allocateInstr().asPush64(newOperandReg(rbpVReg)))
	 = linkInstr(, .allocateInstr().asMovRR(rspVReg, rbpVReg, true))
	return 
}

func ( *machine) ( *instruction) *instruction {
	 = linkInstr(, .allocateInstr().asMovRR(rbpVReg, rspVReg, true))
	 = linkInstr(, .allocateInstr().asPop64(rbpVReg))
	return 
}