package amd64

import (
	
	
	
)

// For the details of the ABI, see:
// https://github.com/golang/go/blob/go1.24.0/src/cmd/compile/abi-internal.md#amd64-architecture

var (
	intArgResultRegs   = []regalloc.RealReg{rax, rbx, rcx, rdi, rsi, r8, r9, r10, r11}
	floatArgResultRegs = []regalloc.RealReg{xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7}
)

var regInfo = &regalloc.RegisterInfo{
	AllocatableRegisters: [regalloc.NumRegType][]regalloc.RealReg{
		regalloc.RegTypeInt: {
			rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15,
		},
		regalloc.RegTypeFloat: {
			xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
		},
	},
	CalleeSavedRegisters: regalloc.NewRegSet(
		rdx, r12, r13, r14, r15,
		xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
	),
	CallerSavedRegisters: regalloc.NewRegSet(
		rax, rcx, rbx, rsi, rdi, r8, r9, r10, r11,
		xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
	),
	RealRegToVReg: []regalloc.VReg{
		rax: raxVReg, rcx: rcxVReg, rdx: rdxVReg, rbx: rbxVReg, rsp: rspVReg, rbp: rbpVReg, rsi: rsiVReg, rdi: rdiVReg,
		r8: r8VReg, r9: r9VReg, r10: r10VReg, r11: r11VReg, r12: r12VReg, r13: r13VReg, r14: r14VReg, r15: r15VReg,
		xmm0: xmm0VReg, xmm1: xmm1VReg, xmm2: xmm2VReg, xmm3: xmm3VReg, xmm4: xmm4VReg, xmm5: xmm5VReg, xmm6: xmm6VReg,
		xmm7: xmm7VReg, xmm8: xmm8VReg, xmm9: xmm9VReg, xmm10: xmm10VReg, xmm11: xmm11VReg, xmm12: xmm12VReg,
		xmm13: xmm13VReg, xmm14: xmm14VReg, xmm15: xmm15VReg,
	},
	RealRegName: func( regalloc.RealReg) string { return regNames[] },
	RealRegType: func( regalloc.RealReg) regalloc.RegType {
		if  < xmm0 {
			return regalloc.RegTypeInt
		}
		return regalloc.RegTypeFloat
	},
}

// ArgsResultsRegs implements backend.Machine.
func ( *machine) () (,  []regalloc.RealReg) {
	return intArgResultRegs, floatArgResultRegs
}

// LowerParams implements backend.Machine.
func ( *machine) ( []ssa.Value) {
	 := .currentABI

	for ,  := range  {
		if !.Valid() {
			continue
		}
		 := .c.VRegOf()
		 := &.Args[]
		if .Kind == backend.ABIArgKindReg {
			.InsertMove(, .Reg, .Type)
		} else {
			//
			//            (high address)
			//          +-----------------+
			//          |     .......     |
			//          |      ret Y      |
			//          |     .......     |
			//          |      ret 0      |
			//          |      arg X      |
			//          |     .......     |
			//          |      arg 1      |
			//          |      arg 0      |
			//          |   ReturnAddress |
			//          |    Caller_RBP   |
			//          +-----------------+ <-- RBP
			//          |   ...........   |
			//          |   clobbered  M  |
			//          |   ............  |
			//          |   clobbered  0  |
			//          |   spill slot N  |
			//          |   ...........   |
			//          |   spill slot 0  |
			//   RSP--> +-----------------+
			//             (low address)

			// Load the value from the arg stack slot above the current RBP.
			 := .allocateInstr()
			 := newOperandMem(.newAmodeImmRBPReg(uint32(.Offset + 16)))
			switch .Type {
			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")
			}
			.insert()
		}
	}
}

// LowerReturns implements backend.Machine.
func ( *machine) ( []ssa.Value) {
	// Load the XMM registers first as it might need a temporary register to inline
	// constant return.
	 := .currentABI
	for ,  := range  {
		 := &.Rets[]
		if !.Type.IsInt() {
			.LowerReturn(, )
		}
	}
	// Then load the GPR registers.
	for ,  := range  {
		 := &.Rets[]
		if .Type.IsInt() {
			.LowerReturn(, )
		}
	}
}

func ( *machine) ( ssa.Value,  *backend.ABIArg) {
	 := .c.VRegOf()
	if  := .c.ValueDefinition(); .IsFromInstr() {
		// Constant instructions are inlined.
		if  := .Instr; .Constant() {
			.insertLoadConstant(, )
		}
	}
	if .Kind == backend.ABIArgKindReg {
		.InsertMove(.Reg, , .Type())
	} else {
		//
		//            (high address)
		//          +-----------------+
		//          |     .......     |
		//          |      ret Y      |
		//          |     .......     |
		//          |      ret 0      |
		//          |      arg X      |
		//          |     .......     |
		//          |      arg 1      |
		//          |      arg 0      |
		//          |   ReturnAddress |
		//          |    Caller_RBP   |
		//          +-----------------+ <-- RBP
		//          |   ...........   |
		//          |   clobbered  M  |
		//          |   ............  |
		//          |   clobbered  0  |
		//          |   spill slot N  |
		//          |   ...........   |
		//          |   spill slot 0  |
		//   RSP--> +-----------------+
		//             (low address)

		// Store the value to the return stack slot above the current RBP.
		 := .allocateInstr()
		 := newOperandMem(.newAmodeImmRBPReg(uint32(.currentABI.ArgStackSize + 16 + .Offset)))
		switch .Type {
		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, , )
		}
		.insert()
	}
}