// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "textflag.h"
#include "abi_ppc64x.h"

// Called by C code generated by cmd/cgo.
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
// Saves C callee-saved registers and calls cgocallback with three arguments.
// fn is the PC of a func(a unsafe.Pointer) function.
//
// This is a simplified version that only saves GPR and FPR registers,
// not vector registers. This keeps the stack frame smaller to avoid
// exceeding the nosplit stack limit.
//
// On PPC64LE ELFv2, callee-save registers are:
//   R14-R31 (18 GPRs = 144 bytes)
//   F14-F31 (18 FPRs = 144 bytes)
//   CR2-CR4 (saved in CR field)
//
// Stack layout (must be 16-byte aligned):
//   32 (FIXED_FRAME) + 24 (args) + 144 (GPR) + 144 (FPR) = 344
//   Rounded to 352 for 16-byte alignment.

#define FIXED_FRAME 32
#define SAVE_SIZE    352
#define GPR_OFFSET   (FIXED_FRAME+24)
#define FPR_OFFSET   (GPR_OFFSET+SAVE_GPR_SIZE)

TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0
	// Save LR and CR in caller's frame per ELFv2 ABI
	MOVD LR, R0
	MOVD R0, 16(R1)
	MOVW CR, R0
	MOVD R0, 8(R1)

	// Allocate our stack frame
	MOVDU R1, -SAVE_SIZE(R1)

	// Save TOC (R2) in case needed
	MOVD R2, 24(R1)

	// Save callee-save GPRs
	SAVE_GPR(GPR_OFFSET)

	// Save callee-save FPRs
	SAVE_FPR(FPR_OFFSET)

	// Initialize R0 to 0 as expected by Go
	MOVD $0, R0

	// Load the current g.
	BL runtime·load_g(SB)

	// Set up arguments for cgocallback
	MOVD R3, FIXED_FRAME+0(R1) // fn unsafe.Pointer
	MOVD R4, FIXED_FRAME+8(R1) // a unsafe.Pointer

	// Skip R5 = n uint32
	MOVD R6, FIXED_FRAME+16(R1)  // ctxt uintptr
	BL   runtime·cgocallback(SB)

	// Restore callee-save FPRs
	RESTORE_FPR(FPR_OFFSET)

	// Restore callee-save GPRs
	RESTORE_GPR(GPR_OFFSET)

	// Restore TOC
	MOVD 24(R1), R2

	// Deallocate stack frame
	ADD $SAVE_SIZE, R1

	// Restore LR and CR from caller's frame
	MOVD 16(R1), R0
	MOVD R0, LR
	MOVD 8(R1), R0
	MOVW R0, CR

	RET
