mirror of
https://github.com/golang/go
synced 2024-11-18 23:34:45 -07:00
cmd/cgo, runtime/cgo: support ppc64
This implements support for calls to and from C in the ppc64 C ABI, as well as supporting functionality such as an entry point from the dynamic linker. Change-Id: I68da6df50d5638cb1a3d3fef773fb412d7bf631a Reviewed-on: https://go-review.googlesource.com/2009 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
f1c4444dfc
commit
af7ca8dce4
@ -60,6 +60,7 @@ func (p *Package) writeDefs() {
|
|||||||
}
|
}
|
||||||
fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
|
fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
|
||||||
fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
|
fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
|
||||||
|
fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n")
|
||||||
|
|
||||||
// Write second Go output: definitions of _C_xxx.
|
// Write second Go output: definitions of _C_xxx.
|
||||||
// In a separate file so that the import of "unsafe" does not
|
// In a separate file so that the import of "unsafe" does not
|
||||||
|
@ -267,6 +267,7 @@ var cgoEnabled = map[string]bool{
|
|||||||
"linux/386": true,
|
"linux/386": true,
|
||||||
"linux/amd64": true,
|
"linux/amd64": true,
|
||||||
"linux/arm": true,
|
"linux/arm": true,
|
||||||
|
"linux/ppc64le": true,
|
||||||
"android/386": true,
|
"android/386": true,
|
||||||
"android/amd64": true,
|
"android/amd64": true,
|
||||||
"android/arm": true,
|
"android/arm": true,
|
||||||
|
@ -118,6 +118,7 @@ export GOTRACEBACK=2
|
|||||||
go test -ldflags '-linkmode=auto' || exit 1
|
go test -ldflags '-linkmode=auto' || exit 1
|
||||||
# linkmode=internal fails on dragonfly since errno is a TLS relocation.
|
# linkmode=internal fails on dragonfly since errno is a TLS relocation.
|
||||||
[ "$GOHOSTOS" == dragonfly ] || go test -ldflags '-linkmode=internal' || exit 1
|
[ "$GOHOSTOS" == dragonfly ] || go test -ldflags '-linkmode=internal' || exit 1
|
||||||
|
# TODO(austin): Add linux-ppc64(le) once external linking works (issue #8912)
|
||||||
case "$GOHOSTOS-$GOARCH" in
|
case "$GOHOSTOS-$GOARCH" in
|
||||||
openbsd-386 | openbsd-amd64)
|
openbsd-386 | openbsd-amd64)
|
||||||
# test linkmode=external, but __thread not supported, so skip testtls.
|
# test linkmode=external, but __thread not supported, so skip testtls.
|
||||||
|
@ -29,8 +29,29 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
|
|||||||
MOVD R3, (g_stack+stack_lo)(g)
|
MOVD R3, (g_stack+stack_lo)(g)
|
||||||
MOVD R1, (g_stack+stack_hi)(g)
|
MOVD R1, (g_stack+stack_hi)(g)
|
||||||
|
|
||||||
// TODO: if there is a _cgo_init, call it.
|
// if there is a _cgo_init, call it using the gcc ABI.
|
||||||
// TODO: add TLS
|
MOVD _cgo_init(SB), R12
|
||||||
|
CMP R0, R12
|
||||||
|
BEQ nocgo
|
||||||
|
MOVD R12, CTR // r12 = "global function entry point"
|
||||||
|
MOVD R13, R5 // arg 2: TLS base pointer
|
||||||
|
MOVD $setg_gcc<>(SB), R4 // arg 1: setg
|
||||||
|
MOVD g, R3 // arg 0: G
|
||||||
|
// C functions expect 32 bytes of space on caller stack frame
|
||||||
|
// and a 16-byte aligned R1
|
||||||
|
MOVD R1, R14 // save current stack
|
||||||
|
SUB $32, R1 // reserve 32 bytes
|
||||||
|
RLDCR $0, R1, $~15, R1 // 16-byte align
|
||||||
|
BL (CTR) // may clobber R0, R3-R12
|
||||||
|
MOVD R14, R1 // restore stack
|
||||||
|
XOR R0, R0 // fix R0
|
||||||
|
|
||||||
|
nocgo:
|
||||||
|
// update stackguard after _cgo_init
|
||||||
|
MOVD (g_stack+stack_lo)(g), R3
|
||||||
|
ADD $const__StackGuard, R3
|
||||||
|
MOVD R3, g_stackguard0(g)
|
||||||
|
MOVD R3, g_stackguard1(g)
|
||||||
|
|
||||||
// set the per-goroutine and per-mach "registers"
|
// set the per-goroutine and per-mach "registers"
|
||||||
MOVD $runtime·m0(SB), R3
|
MOVD $runtime·m0(SB), R3
|
||||||
@ -71,6 +92,11 @@ TEXT runtime·breakpoint(SB),NOSPLIT,$-8-0
|
|||||||
TEXT runtime·asminit(SB),NOSPLIT,$-8-0
|
TEXT runtime·asminit(SB),NOSPLIT,$-8-0
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
|
TEXT _cgo_reginit(SB),NOSPLIT,$-8-0
|
||||||
|
// crosscall_ppc64 and crosscall2 need to reginit, but can't
|
||||||
|
// get at the 'runtime.reginit' symbol.
|
||||||
|
BR runtime·reginit(SB)
|
||||||
|
|
||||||
TEXT runtime·reginit(SB),NOSPLIT,$-8-0
|
TEXT runtime·reginit(SB),NOSPLIT,$-8-0
|
||||||
// set R0 to zero, it's expected by the toolchain
|
// set R0 to zero, it's expected by the toolchain
|
||||||
XOR R0, R0
|
XOR R0, R0
|
||||||
@ -625,26 +651,207 @@ TEXT gosave<>(SB),NOSPLIT,$-8
|
|||||||
// aligned appropriately for the gcc ABI.
|
// aligned appropriately for the gcc ABI.
|
||||||
// See cgocall.c for more details.
|
// See cgocall.c for more details.
|
||||||
TEXT ·asmcgocall(SB),NOSPLIT,$0-16
|
TEXT ·asmcgocall(SB),NOSPLIT,$0-16
|
||||||
MOVD R0, 21(R0)
|
MOVD fn+0(FP), R3
|
||||||
|
MOVD arg+8(FP), R4
|
||||||
|
BL asmcgocall<>(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·asmcgocall_errno(SB),NOSPLIT,$0-24
|
||||||
|
MOVD fn+0(FP), R3
|
||||||
|
MOVD arg+8(FP), R4
|
||||||
|
BL asmcgocall<>(SB)
|
||||||
|
MOVD R3, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// asmcgocall common code. fn in R3, arg in R4. returns errno in R3.
|
||||||
|
TEXT asmcgocall<>(SB),NOSPLIT,$0-0
|
||||||
|
MOVD R1, R2 // save original stack pointer
|
||||||
|
MOVD g, R5
|
||||||
|
|
||||||
|
// Figure out if we need to switch to m->g0 stack.
|
||||||
|
// We get called to create new OS threads too, and those
|
||||||
|
// come in on the m->g0 stack already.
|
||||||
|
MOVD g_m(g), R6
|
||||||
|
MOVD m_g0(R6), R6
|
||||||
|
CMP R6, g
|
||||||
|
BEQ g0
|
||||||
|
BL gosave<>(SB)
|
||||||
|
MOVD R6, g
|
||||||
|
BL runtime·save_g(SB)
|
||||||
|
MOVD (g_sched+gobuf_sp)(g), R1
|
||||||
|
|
||||||
|
// Now on a scheduling stack (a pthread-created stack).
|
||||||
|
g0:
|
||||||
|
// Save room for two of our pointers, plus 32 bytes of callee
|
||||||
|
// save area that lives on the caller stack.
|
||||||
|
SUB $48, R1
|
||||||
|
RLDCR $0, R1, $~15, R1 // 16-byte alignment for gcc ABI
|
||||||
|
MOVD R5, 40(R1) // save old g on stack
|
||||||
|
MOVD (g_stack+stack_hi)(R5), R5
|
||||||
|
SUB R2, R5
|
||||||
|
MOVD R5, 32(R1) // save depth in old g stack (can't just save SP, as stack might be copied during a callback)
|
||||||
|
MOVD R0, 0(R1) // clear back chain pointer (TODO can we give it real back trace information?)
|
||||||
|
// This is a "global call", so put the global entry point in r12
|
||||||
|
MOVD R3, R12
|
||||||
|
MOVD R12, CTR
|
||||||
|
MOVD R4, R3 // arg in r3
|
||||||
|
BL (CTR)
|
||||||
|
|
||||||
|
// C code can clobber R0, so set it back to 0. F27-F31 are
|
||||||
|
// callee save, so we don't need to recover those.
|
||||||
|
XOR R0, R0
|
||||||
|
// Restore g, stack pointer. R3 is errno, so don't touch it
|
||||||
|
MOVD 40(R1), g
|
||||||
|
BL runtime·save_g(SB)
|
||||||
|
MOVD (g_stack+stack_hi)(g), R5
|
||||||
|
MOVD 32(R1), R6
|
||||||
|
SUB R6, R5
|
||||||
|
MOVD R5, R1
|
||||||
|
RET
|
||||||
|
|
||||||
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
|
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
|
||||||
// Turn the fn into a Go func (by taking its address) and call
|
// Turn the fn into a Go func (by taking its address) and call
|
||||||
// cgocallback_gofunc.
|
// cgocallback_gofunc.
|
||||||
TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
|
TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
|
||||||
MOVD R0, 22(R0)
|
MOVD $fn+0(FP), R3
|
||||||
|
MOVD R3, 8(R1)
|
||||||
|
MOVD frame+8(FP), R3
|
||||||
|
MOVD R3, 16(R1)
|
||||||
|
MOVD framesize+16(FP), R3
|
||||||
|
MOVD R3, 24(R1)
|
||||||
|
MOVD $runtime·cgocallback_gofunc(SB), R3
|
||||||
|
MOVD R3, CTR
|
||||||
|
BL (CTR)
|
||||||
|
RET
|
||||||
|
|
||||||
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
|
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
|
||||||
// See cgocall.c for more details.
|
// See cgocall.c for more details.
|
||||||
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-24
|
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24
|
||||||
MOVD R0, 23(R0)
|
NO_LOCAL_POINTERS
|
||||||
|
|
||||||
|
// Load m and g from thread-local storage.
|
||||||
|
MOVB runtime·iscgo(SB), R3
|
||||||
|
CMP R3, $0
|
||||||
|
BEQ nocgo
|
||||||
|
BL runtime·load_g(SB)
|
||||||
|
nocgo:
|
||||||
|
|
||||||
|
// If g is nil, Go did not create the current thread.
|
||||||
|
// Call needm to obtain one for temporary use.
|
||||||
|
// In this case, we're running on the thread stack, so there's
|
||||||
|
// lots of space, but the linker doesn't know. Hide the call from
|
||||||
|
// the linker analysis by using an indirect call.
|
||||||
|
CMP g, $0
|
||||||
|
BNE havem
|
||||||
|
MOVD g, savedm-8(SP) // g is zero, so is m.
|
||||||
|
MOVD $runtime·needm(SB), R3
|
||||||
|
MOVD R3, CTR
|
||||||
|
BL (CTR)
|
||||||
|
|
||||||
|
// Set m->sched.sp = SP, so that if a panic happens
|
||||||
|
// during the function we are about to execute, it will
|
||||||
|
// have a valid SP to run on the g0 stack.
|
||||||
|
// The next few lines (after the havem label)
|
||||||
|
// will save this SP onto the stack and then write
|
||||||
|
// the same SP back to m->sched.sp. That seems redundant,
|
||||||
|
// but if an unrecovered panic happens, unwindm will
|
||||||
|
// restore the g->sched.sp from the stack location
|
||||||
|
// and then systemstack will try to use it. If we don't set it here,
|
||||||
|
// that restored SP will be uninitialized (typically 0) and
|
||||||
|
// will not be usable.
|
||||||
|
MOVD g_m(g), R3
|
||||||
|
MOVD m_g0(R3), R3
|
||||||
|
MOVD R1, (g_sched+gobuf_sp)(R3)
|
||||||
|
|
||||||
|
havem:
|
||||||
|
MOVD g_m(g), R8
|
||||||
|
MOVD R8, savedm-8(SP)
|
||||||
|
// Now there's a valid m, and we're running on its m->g0.
|
||||||
|
// Save current m->g0->sched.sp on stack and then set it to SP.
|
||||||
|
// Save current sp in m->g0->sched.sp in preparation for
|
||||||
|
// switch back to m->curg stack.
|
||||||
|
// NOTE: unwindm knows that the saved g->sched.sp is at 8(R1) aka savedsp-16(SP).
|
||||||
|
MOVD m_g0(R8), R3
|
||||||
|
MOVD (g_sched+gobuf_sp)(R3), R4
|
||||||
|
MOVD R4, savedsp-16(SP)
|
||||||
|
MOVD R1, (g_sched+gobuf_sp)(R3)
|
||||||
|
|
||||||
|
// Switch to m->curg stack and call runtime.cgocallbackg.
|
||||||
|
// Because we are taking over the execution of m->curg
|
||||||
|
// but *not* resuming what had been running, we need to
|
||||||
|
// save that information (m->curg->sched) so we can restore it.
|
||||||
|
// We can restore m->curg->sched.sp easily, because calling
|
||||||
|
// runtime.cgocallbackg leaves SP unchanged upon return.
|
||||||
|
// To save m->curg->sched.pc, we push it onto the stack.
|
||||||
|
// This has the added benefit that it looks to the traceback
|
||||||
|
// routine like cgocallbackg is going to return to that
|
||||||
|
// PC (because the frame we allocate below has the same
|
||||||
|
// size as cgocallback_gofunc's frame declared above)
|
||||||
|
// so that the traceback will seamlessly trace back into
|
||||||
|
// the earlier calls.
|
||||||
|
//
|
||||||
|
// In the new goroutine, -16(SP) and -8(SP) are unused.
|
||||||
|
MOVD m_curg(R8), g
|
||||||
|
BL runtime·save_g(SB)
|
||||||
|
MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
|
||||||
|
MOVD (g_sched+gobuf_pc)(g), R5
|
||||||
|
MOVD R5, -24(R4)
|
||||||
|
MOVD $-24(R4), R1
|
||||||
|
BL runtime·cgocallbackg(SB)
|
||||||
|
|
||||||
|
// Restore g->sched (== m->curg->sched) from saved values.
|
||||||
|
MOVD 0(R1), R5
|
||||||
|
MOVD R5, (g_sched+gobuf_pc)(g)
|
||||||
|
MOVD $24(R1), R4
|
||||||
|
MOVD R4, (g_sched+gobuf_sp)(g)
|
||||||
|
|
||||||
|
// Switch back to m->g0's stack and restore m->g0->sched.sp.
|
||||||
|
// (Unlike m->curg, the g0 goroutine never uses sched.pc,
|
||||||
|
// so we do not have to restore it.)
|
||||||
|
MOVD g_m(g), R8
|
||||||
|
MOVD m_g0(R8), g
|
||||||
|
BL runtime·save_g(SB)
|
||||||
|
MOVD (g_sched+gobuf_sp)(g), R1
|
||||||
|
MOVD savedsp-16(SP), R4
|
||||||
|
MOVD R4, (g_sched+gobuf_sp)(g)
|
||||||
|
|
||||||
|
// If the m on entry was nil, we called needm above to borrow an m
|
||||||
|
// for the duration of the call. Since the call is over, return it with dropm.
|
||||||
|
MOVD savedm-8(SP), R6
|
||||||
|
CMP R6, $0
|
||||||
|
BNE droppedm
|
||||||
|
MOVD $runtime·dropm(SB), R3
|
||||||
|
MOVD R3, CTR
|
||||||
|
BL (CTR)
|
||||||
|
droppedm:
|
||||||
|
|
||||||
|
// Done!
|
||||||
|
RET
|
||||||
|
|
||||||
// void setg(G*); set g. for use by needm.
|
// void setg(G*); set g. for use by needm.
|
||||||
TEXT runtime·setg(SB), NOSPLIT, $0-8
|
TEXT runtime·setg(SB), NOSPLIT, $0-8
|
||||||
MOVD R0, 24(R0)
|
MOVD gg+0(FP), g
|
||||||
|
// This only happens if iscgo, so jump straight to save_g
|
||||||
|
BL runtime·save_g(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
// void setg_gcc(G*); set g called from gcc.
|
// void setg_gcc(G*); set g in C TLS.
|
||||||
TEXT setg_gcc<>(SB),NOSPLIT,$0
|
// Must obey the gcc calling convention.
|
||||||
MOVD R0, 25(R0)
|
TEXT setg_gcc<>(SB),NOSPLIT,$-8-0
|
||||||
|
// The standard prologue clobbers R31, which is callee-save in
|
||||||
|
// the C ABI, so we have to use $-8-0 and save LR ourselves.
|
||||||
|
MOVD LR, R4
|
||||||
|
// Also save g and R31, since they're callee-save in C ABI
|
||||||
|
MOVD R31, R5
|
||||||
|
MOVD g, R6
|
||||||
|
|
||||||
|
MOVD R3, g
|
||||||
|
BL runtime·save_g(SB)
|
||||||
|
|
||||||
|
MOVD R6, g
|
||||||
|
MOVD R5, R31
|
||||||
|
MOVD R4, LR
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT runtime·getcallerpc(SB),NOSPLIT,$-8-16
|
TEXT runtime·getcallerpc(SB),NOSPLIT,$-8-16
|
||||||
MOVD 0(R1), R3
|
MOVD 0(R1), R3
|
||||||
@ -989,8 +1196,21 @@ TEXT runtime·return0(SB), NOSPLIT, $0
|
|||||||
|
|
||||||
// Called from cgo wrappers, this function returns g->m->curg.stack.hi.
|
// Called from cgo wrappers, this function returns g->m->curg.stack.hi.
|
||||||
// Must obey the gcc calling convention.
|
// Must obey the gcc calling convention.
|
||||||
TEXT _cgo_topofstack(SB),NOSPLIT,$0
|
TEXT _cgo_topofstack(SB),NOSPLIT,$-8
|
||||||
MOVD R0, 26(R0)
|
// g (R30) and R31 are callee-save in the C ABI, so save them
|
||||||
|
MOVD g, R4
|
||||||
|
MOVD R31, R5
|
||||||
|
MOVD LR, R6
|
||||||
|
|
||||||
|
BL runtime·load_g(SB) // clobbers g (R30), R31
|
||||||
|
MOVD g_m(g), R3
|
||||||
|
MOVD m_curg(R3), R3
|
||||||
|
MOVD (g_stack+stack_hi)(R3), R3
|
||||||
|
|
||||||
|
MOVD R4, g
|
||||||
|
MOVD R5, R31
|
||||||
|
MOVD R6, LR
|
||||||
|
RET
|
||||||
|
|
||||||
// The top-most function running on a goroutine
|
// The top-most function running on a goroutine
|
||||||
// returns to goexit+PCQuantum.
|
// returns to goexit+PCQuantum.
|
||||||
|
124
src/runtime/cgo/asm_ppc64x.s
Normal file
124
src/runtime/cgo/asm_ppc64x.s
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void crosscall2(void (*fn)(void*, int32), void*, int32)
|
||||||
|
* Save registers and call fn with two arguments.
|
||||||
|
* crosscall2 obeys the C ABI; fn obeys the Go ABI.
|
||||||
|
*/
|
||||||
|
TEXT crosscall2(SB),NOSPLIT,$-8
|
||||||
|
// TODO(austin): ABI v1 (fn is probably a function descriptor)
|
||||||
|
|
||||||
|
// Start with standard C stack frame layout and linkage
|
||||||
|
MOVD LR, R0
|
||||||
|
MOVD R0, 16(R1) // Save LR in caller's frame
|
||||||
|
MOVD R2, 24(R1) // Save TOC in caller's frame
|
||||||
|
|
||||||
|
BL saveregs2<>(SB)
|
||||||
|
|
||||||
|
MOVDU R1, (-288-3*8)(R1)
|
||||||
|
|
||||||
|
// Initialize Go ABI environment
|
||||||
|
BL runtime·reginit(SB)
|
||||||
|
BL runtime·load_g(SB)
|
||||||
|
|
||||||
|
MOVD R3, CTR
|
||||||
|
MOVD R4, 8(R1)
|
||||||
|
MOVD R5, 16(R1)
|
||||||
|
BL (CTR)
|
||||||
|
|
||||||
|
ADD $(288+3*8), R1
|
||||||
|
|
||||||
|
BL restoreregs2<>(SB)
|
||||||
|
|
||||||
|
MOVD 24(R1), R2
|
||||||
|
MOVD 16(R1), R0
|
||||||
|
MOVD R0, LR
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT saveregs2<>(SB),NOSPLIT,$-8
|
||||||
|
// O=-288; for R in R{14..31}; do echo "\tMOVD\t$R, $O(R1)"|sed s/R30/g/; ((O+=8)); done; for F in F{14..31}; do echo "\tFMOVD\t$F, $O(R1)"; ((O+=8)); done
|
||||||
|
MOVD R14, -288(R1)
|
||||||
|
MOVD R15, -280(R1)
|
||||||
|
MOVD R16, -272(R1)
|
||||||
|
MOVD R17, -264(R1)
|
||||||
|
MOVD R18, -256(R1)
|
||||||
|
MOVD R19, -248(R1)
|
||||||
|
MOVD R20, -240(R1)
|
||||||
|
MOVD R21, -232(R1)
|
||||||
|
MOVD R22, -224(R1)
|
||||||
|
MOVD R23, -216(R1)
|
||||||
|
MOVD R24, -208(R1)
|
||||||
|
MOVD R25, -200(R1)
|
||||||
|
MOVD R26, -192(R1)
|
||||||
|
MOVD R27, -184(R1)
|
||||||
|
MOVD R28, -176(R1)
|
||||||
|
MOVD R29, -168(R1)
|
||||||
|
MOVD g, -160(R1)
|
||||||
|
MOVD R31, -152(R1)
|
||||||
|
FMOVD F14, -144(R1)
|
||||||
|
FMOVD F15, -136(R1)
|
||||||
|
FMOVD F16, -128(R1)
|
||||||
|
FMOVD F17, -120(R1)
|
||||||
|
FMOVD F18, -112(R1)
|
||||||
|
FMOVD F19, -104(R1)
|
||||||
|
FMOVD F20, -96(R1)
|
||||||
|
FMOVD F21, -88(R1)
|
||||||
|
FMOVD F22, -80(R1)
|
||||||
|
FMOVD F23, -72(R1)
|
||||||
|
FMOVD F24, -64(R1)
|
||||||
|
FMOVD F25, -56(R1)
|
||||||
|
FMOVD F26, -48(R1)
|
||||||
|
FMOVD F27, -40(R1)
|
||||||
|
FMOVD F28, -32(R1)
|
||||||
|
FMOVD F29, -24(R1)
|
||||||
|
FMOVD F30, -16(R1)
|
||||||
|
FMOVD F31, -8(R1)
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT restoreregs2<>(SB),NOSPLIT,$-8
|
||||||
|
// O=-288; for R in R{14..31}; do echo "\tMOVD\t$O(R1), $R"|sed s/R30/g/; ((O+=8)); done; for F in F{14..31}; do echo "\tFMOVD\t$O(R1), $F"; ((O+=8)); done
|
||||||
|
MOVD -288(R1), R14
|
||||||
|
MOVD -280(R1), R15
|
||||||
|
MOVD -272(R1), R16
|
||||||
|
MOVD -264(R1), R17
|
||||||
|
MOVD -256(R1), R18
|
||||||
|
MOVD -248(R1), R19
|
||||||
|
MOVD -240(R1), R20
|
||||||
|
MOVD -232(R1), R21
|
||||||
|
MOVD -224(R1), R22
|
||||||
|
MOVD -216(R1), R23
|
||||||
|
MOVD -208(R1), R24
|
||||||
|
MOVD -200(R1), R25
|
||||||
|
MOVD -192(R1), R26
|
||||||
|
MOVD -184(R1), R27
|
||||||
|
MOVD -176(R1), R28
|
||||||
|
MOVD -168(R1), R29
|
||||||
|
MOVD -160(R1), g
|
||||||
|
MOVD -152(R1), R31
|
||||||
|
FMOVD -144(R1), F14
|
||||||
|
FMOVD -136(R1), F15
|
||||||
|
FMOVD -128(R1), F16
|
||||||
|
FMOVD -120(R1), F17
|
||||||
|
FMOVD -112(R1), F18
|
||||||
|
FMOVD -104(R1), F19
|
||||||
|
FMOVD -96(R1), F20
|
||||||
|
FMOVD -88(R1), F21
|
||||||
|
FMOVD -80(R1), F22
|
||||||
|
FMOVD -72(R1), F23
|
||||||
|
FMOVD -64(R1), F24
|
||||||
|
FMOVD -56(R1), F25
|
||||||
|
FMOVD -48(R1), F26
|
||||||
|
FMOVD -40(R1), F27
|
||||||
|
FMOVD -32(R1), F28
|
||||||
|
FMOVD -24(R1), F29
|
||||||
|
FMOVD -16(R1), F30
|
||||||
|
FMOVD -8(R1), F31
|
||||||
|
|
||||||
|
RET
|
70
src/runtime/cgo/gcc_linux_ppc64x.c
Normal file
70
src/runtime/cgo/gcc_linux_ppc64x.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "libcgo.h"
|
||||||
|
|
||||||
|
static void *threadentry(void*);
|
||||||
|
|
||||||
|
void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
|
||||||
|
static void (*setg_gcc)(void*);
|
||||||
|
|
||||||
|
void
|
||||||
|
x_cgo_init(G *g, void (*setg)(void*), void **tlsbase)
|
||||||
|
{
|
||||||
|
pthread_attr_t attr;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
setg_gcc = setg;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_getstacksize(&attr, &size);
|
||||||
|
g->stacklo = (uintptr)&attr - size + 4096;
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_cgo_sys_thread_start(ThreadStart *ts)
|
||||||
|
{
|
||||||
|
pthread_attr_t attr;
|
||||||
|
sigset_t ign, oset;
|
||||||
|
pthread_t p;
|
||||||
|
size_t size;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
sigfillset(&ign);
|
||||||
|
pthread_sigmask(SIG_SETMASK, &ign, &oset);
|
||||||
|
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_getstacksize(&attr, &size);
|
||||||
|
// Leave stacklo=0 and set stackhi=size; mstack will do the rest.
|
||||||
|
ts->g->stackhi = size;
|
||||||
|
err = pthread_create(&p, &attr, threadentry, ts);
|
||||||
|
|
||||||
|
pthread_sigmask(SIG_SETMASK, &oset, nil);
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
fatalf("pthread_create failed: %s", strerror(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void crosscall_ppc64(void (*fn)(void), void *g);
|
||||||
|
|
||||||
|
static void*
|
||||||
|
threadentry(void *v)
|
||||||
|
{
|
||||||
|
ThreadStart ts;
|
||||||
|
|
||||||
|
ts = *(ThreadStart*)v;
|
||||||
|
free(v);
|
||||||
|
|
||||||
|
// Save g for this thread in C TLS
|
||||||
|
setg_gcc((void*)ts.g);
|
||||||
|
|
||||||
|
crosscall_ppc64(ts.fn, (void*)ts.g);
|
||||||
|
return nil;
|
||||||
|
}
|
140
src/runtime/cgo/gcc_ppc64x.S
Normal file
140
src/runtime/cgo/gcc_ppc64x.S
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apple still insists on underscore prefixes for C function names.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#define EXT(s) _##s
|
||||||
|
#else
|
||||||
|
#define EXT(s) s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void crosscall_ppc64(void (*fn)(void), void *g)
|
||||||
|
*
|
||||||
|
* Calling into the 9g 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.
|
||||||
|
*/
|
||||||
|
.globl EXT(crosscall_ppc64)
|
||||||
|
EXT(crosscall_ppc64):
|
||||||
|
// Start with standard C stack frame layout and linkage
|
||||||
|
mflr %r0
|
||||||
|
std %r0, 16(%r1) // Save LR in caller's frame
|
||||||
|
std %r2, 24(%r1) // Save TOC in caller's frame
|
||||||
|
bl saveregs
|
||||||
|
stdu %r1, -296(%r1)
|
||||||
|
|
||||||
|
// Set up Go ABI constant registers
|
||||||
|
bl _cgo_reginit
|
||||||
|
|
||||||
|
// Restore g pointer (r30 in Go ABI, which may have been clobbered by C)
|
||||||
|
mr %r30, %r4
|
||||||
|
|
||||||
|
// Call fn
|
||||||
|
mtctr %r3
|
||||||
|
bctrl
|
||||||
|
|
||||||
|
addi %r1, %r1, 296
|
||||||
|
bl restoreregs
|
||||||
|
ld %r2, 24(%r1)
|
||||||
|
ld %r0, 16(%r1)
|
||||||
|
mtlr %r0
|
||||||
|
blr
|
||||||
|
|
||||||
|
saveregs:
|
||||||
|
// Save callee-save registers
|
||||||
|
// O=-288; for R in %r{14..31}; do echo "\tstd\t$R, $O(%r1)"; ((O+=8)); done; for F in f{14..31}; do echo "\tstfd\t$F, $O(%r1)"; ((O+=8)); done
|
||||||
|
std %r14, -288(%r1)
|
||||||
|
std %r15, -280(%r1)
|
||||||
|
std %r16, -272(%r1)
|
||||||
|
std %r17, -264(%r1)
|
||||||
|
std %r18, -256(%r1)
|
||||||
|
std %r19, -248(%r1)
|
||||||
|
std %r20, -240(%r1)
|
||||||
|
std %r21, -232(%r1)
|
||||||
|
std %r22, -224(%r1)
|
||||||
|
std %r23, -216(%r1)
|
||||||
|
std %r24, -208(%r1)
|
||||||
|
std %r25, -200(%r1)
|
||||||
|
std %r26, -192(%r1)
|
||||||
|
std %r27, -184(%r1)
|
||||||
|
std %r28, -176(%r1)
|
||||||
|
std %r29, -168(%r1)
|
||||||
|
std %r30, -160(%r1)
|
||||||
|
std %r31, -152(%r1)
|
||||||
|
stfd %f14, -144(%r1)
|
||||||
|
stfd %f15, -136(%r1)
|
||||||
|
stfd %f16, -128(%r1)
|
||||||
|
stfd %f17, -120(%r1)
|
||||||
|
stfd %f18, -112(%r1)
|
||||||
|
stfd %f19, -104(%r1)
|
||||||
|
stfd %f20, -96(%r1)
|
||||||
|
stfd %f21, -88(%r1)
|
||||||
|
stfd %f22, -80(%r1)
|
||||||
|
stfd %f23, -72(%r1)
|
||||||
|
stfd %f24, -64(%r1)
|
||||||
|
stfd %f25, -56(%r1)
|
||||||
|
stfd %f26, -48(%r1)
|
||||||
|
stfd %f27, -40(%r1)
|
||||||
|
stfd %f28, -32(%r1)
|
||||||
|
stfd %f29, -24(%r1)
|
||||||
|
stfd %f30, -16(%r1)
|
||||||
|
stfd %f31, -8(%r1)
|
||||||
|
|
||||||
|
blr
|
||||||
|
|
||||||
|
restoreregs:
|
||||||
|
// O=-288; for R in %r{14..31}; do echo "\tld\t$R, $O(%r1)"; ((O+=8)); done; for F in %f{14..31}; do echo "\tlfd\t$F, $O(%r1)"; ((O+=8)); done
|
||||||
|
ld %r14, -288(%r1)
|
||||||
|
ld %r15, -280(%r1)
|
||||||
|
ld %r16, -272(%r1)
|
||||||
|
ld %r17, -264(%r1)
|
||||||
|
ld %r18, -256(%r1)
|
||||||
|
ld %r19, -248(%r1)
|
||||||
|
ld %r20, -240(%r1)
|
||||||
|
ld %r21, -232(%r1)
|
||||||
|
ld %r22, -224(%r1)
|
||||||
|
ld %r23, -216(%r1)
|
||||||
|
ld %r24, -208(%r1)
|
||||||
|
ld %r25, -200(%r1)
|
||||||
|
ld %r26, -192(%r1)
|
||||||
|
ld %r27, -184(%r1)
|
||||||
|
ld %r28, -176(%r1)
|
||||||
|
ld %r29, -168(%r1)
|
||||||
|
ld %r30, -160(%r1)
|
||||||
|
ld %r31, -152(%r1)
|
||||||
|
lfd %f14, -144(%r1)
|
||||||
|
lfd %f15, -136(%r1)
|
||||||
|
lfd %f16, -128(%r1)
|
||||||
|
lfd %f17, -120(%r1)
|
||||||
|
lfd %f18, -112(%r1)
|
||||||
|
lfd %f19, -104(%r1)
|
||||||
|
lfd %f20, -96(%r1)
|
||||||
|
lfd %f21, -88(%r1)
|
||||||
|
lfd %f22, -80(%r1)
|
||||||
|
lfd %f23, -72(%r1)
|
||||||
|
lfd %f24, -64(%r1)
|
||||||
|
lfd %f25, -56(%r1)
|
||||||
|
lfd %f26, -48(%r1)
|
||||||
|
lfd %f27, -40(%r1)
|
||||||
|
lfd %f28, -32(%r1)
|
||||||
|
lfd %f29, -24(%r1)
|
||||||
|
lfd %f30, -16(%r1)
|
||||||
|
lfd %f31, -8(%r1)
|
||||||
|
|
||||||
|
blr
|
||||||
|
|
||||||
|
.globl EXT(__stack_chk_fail_local)
|
||||||
|
EXT(__stack_chk_fail_local):
|
||||||
|
1:
|
||||||
|
// TODO(austin)
|
||||||
|
b 1b
|
||||||
|
|
||||||
|
#ifdef __ELF__
|
||||||
|
.section .note.GNU-stack,"",%progbits
|
||||||
|
#endif
|
@ -229,6 +229,11 @@ func cgocallbackg1() {
|
|||||||
case "386":
|
case "386":
|
||||||
// On 386, stack frame is three words, plus caller PC.
|
// On 386, stack frame is three words, plus caller PC.
|
||||||
cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
|
cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
|
||||||
|
case "ppc64", "ppc64le":
|
||||||
|
// On ppc64, stack frame is two words and there's a
|
||||||
|
// saved LR between SP and the stack frame and between
|
||||||
|
// the stack frame and the arguments.
|
||||||
|
cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke callback.
|
// Invoke callback.
|
||||||
@ -263,6 +268,8 @@ func unwindm(restore *bool) {
|
|||||||
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp))
|
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp))
|
||||||
case "arm":
|
case "arm":
|
||||||
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 4))
|
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 4))
|
||||||
|
case "ppc64", "ppc64le":
|
||||||
|
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 8))
|
||||||
}
|
}
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,11 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
|
|||||||
t.Skipf("no external linking on OS X 10.6")
|
t.Skipf("no external linking on OS X 10.6")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
|
||||||
|
// TODO(austin) External linking not implemented on
|
||||||
|
// ppc64 (issue #8912)
|
||||||
|
t.Skipf("no external linking on ppc64")
|
||||||
|
}
|
||||||
got := executeTest(t, cgoExternalThreadSIGPROFSource, nil)
|
got := executeTest(t, cgoExternalThreadSIGPROFSource, nil)
|
||||||
want := "OK\n"
|
want := "OK\n"
|
||||||
if got != want {
|
if got != want {
|
||||||
|
@ -7,6 +7,12 @@ TEXT _rt0_ppc64_linux(SB),NOSPLIT,$0
|
|||||||
DWORD $0
|
DWORD $0
|
||||||
|
|
||||||
TEXT _main<>(SB),NOSPLIT,$-8
|
TEXT _main<>(SB),NOSPLIT,$-8
|
||||||
|
// In a statically linked binary, the stack contains argc,
|
||||||
|
// argv as argc string pointers followed by a NULL, envv as a
|
||||||
|
// sequence of string pointers followed by a NULL, and auxv.
|
||||||
|
// There is no TLS base pointer.
|
||||||
|
//
|
||||||
|
// TODO(austin): Support ABI v1 dynamic linking entry point
|
||||||
MOVD 0(R1), R3 // argc
|
MOVD 0(R1), R3 // argc
|
||||||
ADD $8, R1, R4 // argv
|
ADD $8, R1, R4 // argv
|
||||||
BR main(SB)
|
BR main(SB)
|
||||||
|
@ -4,11 +4,29 @@ TEXT _rt0_ppc64le_linux(SB),NOSPLIT,$0
|
|||||||
BR _main<>(SB)
|
BR _main<>(SB)
|
||||||
|
|
||||||
TEXT _main<>(SB),NOSPLIT,$-8
|
TEXT _main<>(SB),NOSPLIT,$-8
|
||||||
MOVD 0(R1), R3 // argc
|
// In a statically linked binary, the stack contains argc,
|
||||||
ADD $8, R1, R4 // argv
|
// argv as argc string pointers followed by a NULL, envv as a
|
||||||
|
// sequence of string pointers followed by a NULL, and auxv.
|
||||||
|
// There is no TLS base pointer.
|
||||||
|
//
|
||||||
|
// In a dynamically linked binary, r3 contains argc, r4
|
||||||
|
// contains argv, r5 contains envp, r6 contains auxv, and r13
|
||||||
|
// contains the TLS pointer.
|
||||||
|
//
|
||||||
|
// Figure out which case this is by looking at r4: if it's 0,
|
||||||
|
// we're statically linked; otherwise we're dynamically
|
||||||
|
// linked.
|
||||||
|
CMP R0, R4
|
||||||
|
BNE dlink
|
||||||
|
|
||||||
|
// Statically linked
|
||||||
|
MOVD 0(R1), R3 // argc
|
||||||
|
ADD $8, R1, R4 // argv
|
||||||
MOVD $runtime·tls0(SB), R13 // TLS
|
MOVD $runtime·tls0(SB), R13 // TLS
|
||||||
ADD $0x7000, R13
|
ADD $0x7000, R13
|
||||||
BR main(SB)
|
|
||||||
|
dlink:
|
||||||
|
BR main(SB)
|
||||||
|
|
||||||
TEXT main(SB),NOSPLIT,$-8
|
TEXT main(SB),NOSPLIT,$-8
|
||||||
MOVD $runtime·rt0_go(SB), R31
|
MOVD $runtime·rt0_go(SB), R31
|
||||||
|
@ -77,6 +77,9 @@ func goargs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func goenvs_unix() {
|
func goenvs_unix() {
|
||||||
|
// TODO(austin): ppc64 in dynamic linking mode doesn't
|
||||||
|
// guarantee env[] will immediately follow argv. Might cause
|
||||||
|
// problems.
|
||||||
n := int32(0)
|
n := int32(0)
|
||||||
for argv_index(argv, argc+1+n) != nil {
|
for argv_index(argv, argc+1+n) != nil {
|
||||||
n++
|
n++
|
||||||
|
@ -193,6 +193,13 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
|
|||||||
// initialize essential registers (just in case)
|
// initialize essential registers (just in case)
|
||||||
BL runtime·reginit(SB)
|
BL runtime·reginit(SB)
|
||||||
|
|
||||||
|
// this might be called in external code context,
|
||||||
|
// where g is not set.
|
||||||
|
MOVB runtime·iscgo(SB), R6
|
||||||
|
CMP R6, $0
|
||||||
|
BEQ 2(PC)
|
||||||
|
BL runtime·load_g(SB)
|
||||||
|
|
||||||
// check that g exists
|
// check that g exists
|
||||||
CMP g, $0
|
CMP g, $0
|
||||||
BNE 6(PC)
|
BNE 6(PC)
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
// ppc64 code that will overwrite this register.
|
// ppc64 code that will overwrite this register.
|
||||||
//
|
//
|
||||||
// If !iscgo, this is a no-op.
|
// If !iscgo, this is a no-op.
|
||||||
|
//
|
||||||
|
// NOTE: setg_gcc<> assume this clobbers only R31.
|
||||||
TEXT runtime·save_g(SB),NOSPLIT,$-8-0
|
TEXT runtime·save_g(SB),NOSPLIT,$-8-0
|
||||||
MOVB runtime·iscgo(SB), R31
|
MOVB runtime·iscgo(SB), R31
|
||||||
CMP R31, $0
|
CMP R31, $0
|
||||||
@ -46,6 +48,8 @@ nocgo:
|
|||||||
// This is never called directly from C code (it doesn't have to
|
// This is never called directly from C code (it doesn't have to
|
||||||
// follow the C ABI), but it may be called from a C context, where the
|
// follow the C ABI), but it may be called from a C context, where the
|
||||||
// usual Go registers aren't set up.
|
// usual Go registers aren't set up.
|
||||||
|
//
|
||||||
|
// NOTE: _cgo_topofstack assumes this only clobbers g (R30), and R31.
|
||||||
TEXT runtime·load_g(SB),NOSPLIT,$-8-0
|
TEXT runtime·load_g(SB),NOSPLIT,$-8-0
|
||||||
MOVD $runtime·tlsg(SB), R31
|
MOVD $runtime·tlsg(SB), R31
|
||||||
// R13 is the C ABI TLS base pointer + 0x7000
|
// R13 is the C ABI TLS base pointer + 0x7000
|
||||||
|
Loading…
Reference in New Issue
Block a user