// Copyright 2019 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.

.file "gcc_aix_ppc64.S"

/*
 * void crosscall_ppc64(void (*fn)(void), void *g)
 *
 * Calling into the gc tool chain, where all registers are caller save.
 * Called from standard ppc64 C ABI, where r2, r14-r31, f14-f31 are
 * callee-save, so they must be saved explicitly.
 * AIX has a special assembly syntax and keywords that can be mixed with
 * Linux assembly.
 */
  .toc
  .csect .text[PR]
  .globl crosscall_ppc64
  .globl .crosscall_ppc64
  .csect crosscall_ppc64[DS]
crosscall_ppc64:
  .llong .crosscall_ppc64, TOC[tc0], 0
  .csect .text[PR]
.crosscall_ppc64:
	// Start with standard C stack frame layout and linkage
	mflr	0
	std	0, 16(1)	// Save LR in caller's frame
	std	2, 40(1)	// Save TOC in caller's frame
	bl	saveregs
	stdu	1, -296(1)

	// Set up Go ABI constant registers
	// Must match _cgo_reginit in runtime package.
	xor 0, 0, 0

	// Restore g pointer (r30 in Go ABI, which may have been clobbered by C)
	mr	30, 4

	// Call fn
	mr	12, 3
	mtctr	12
	bctrl

	addi	1, 1, 296
	bl	restoreregs
	ld	2, 40(1)
	ld	0, 16(1)
	mtlr	0
	blr

saveregs:
	// Save callee-save registers
	// O=-288; for R in {14..31}; do echo "\tstd\t$R, $O(1)"; ((O+=8)); done; for F in f{14..31}; do echo "\tstfd\t$F, $O(1)"; ((O+=8)); done
	std	14, -288(1)
	std	15, -280(1)
	std	16, -272(1)
	std	17, -264(1)
	std	18, -256(1)
	std	19, -248(1)
	std	20, -240(1)
	std	21, -232(1)
	std	22, -224(1)
	std	23, -216(1)
	std	24, -208(1)
	std	25, -200(1)
	std	26, -192(1)
	std	27, -184(1)
	std	28, -176(1)
	std	29, -168(1)
	std	30, -160(1)
	std	31, -152(1)
	stfd	14, -144(1)
	stfd	15, -136(1)
	stfd	16, -128(1)
	stfd	17, -120(1)
	stfd	18, -112(1)
	stfd	19, -104(1)
	stfd	20, -96(1)
	stfd	21, -88(1)
	stfd	22, -80(1)
	stfd	23, -72(1)
	stfd	24, -64(1)
	stfd	25, -56(1)
	stfd	26, -48(1)
	stfd	27, -40(1)
	stfd	28, -32(1)
	stfd	29, -24(1)
	stfd	30, -16(1)
	stfd	31, -8(1)

	blr

restoreregs:
	// O=-288; for R in {14..31}; do echo "\tld\t$R, $O(1)"; ((O+=8)); done; for F in {14..31}; do echo "\tlfd\t$F, $O(1)"; ((O+=8)); done
	ld	14, -288(1)
	ld	15, -280(1)
	ld	16, -272(1)
	ld	17, -264(1)
	ld	18, -256(1)
	ld	19, -248(1)
	ld	20, -240(1)
	ld	21, -232(1)
	ld	22, -224(1)
	ld	23, -216(1)
	ld	24, -208(1)
	ld	25, -200(1)
	ld	26, -192(1)
	ld	27, -184(1)
	ld	28, -176(1)
	ld	29, -168(1)
	ld	30, -160(1)
	ld	31, -152(1)
	lfd	14, -144(1)
	lfd	15, -136(1)
	lfd	16, -128(1)
	lfd	17, -120(1)
	lfd	18, -112(1)
	lfd	19, -104(1)
	lfd	20, -96(1)
	lfd	21, -88(1)
	lfd	22, -80(1)
	lfd	23, -72(1)
	lfd	24, -64(1)
	lfd	25, -56(1)
	lfd	26, -48(1)
	lfd	27, -40(1)
	lfd	28, -32(1)
	lfd	29, -24(1)
	lfd	30, -16(1)
	lfd	31, -8(1)

	blr
