Commit 2e20386f authored by Ian Lance Taylor's avatar Ian Lance Taylor

Library support for cgo export.

These functions are used to call from a C function back to a
Go function.  This only includes 386 support.

R=rsc
CC=golang-dev
https://golang.org/cl/834045
parent 2d0ff3f1
...@@ -35,3 +35,27 @@ EXT(crosscall_386): ...@@ -35,3 +35,27 @@ EXT(crosscall_386):
popl %ebp popl %ebp
ret ret
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
*
* Save registers and call fn with two arguments.
*/
.globl EXT(crosscall2)
EXT(crosscall2):
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %esi
pushl %edi
pushl 16(%ebp)
pushl 12(%ebp)
mov 8(%ebp), %eax
call *%eax
addl $8,%esp
popl %edi
popl %esi
popl %ebx
popl %ebp
ret
...@@ -351,16 +351,53 @@ TEXT runcgo(SB),7,$16 ...@@ -351,16 +351,53 @@ TEXT runcgo(SB),7,$16
// Now on a scheduling stack (a pthread-created stack). // Now on a scheduling stack (a pthread-created stack).
SUBL $16, SP SUBL $16, SP
ANDL $~15, SP // alignment for gcc ABI ANDL $~15, SP // alignment for gcc ABI
MOVL g(DI), BP
MOVL BP, 8(SP)
MOVL SI, g(DI)
MOVL CX, 4(SP) MOVL CX, 4(SP)
MOVL BX, 0(SP) MOVL BX, 0(SP)
CALL AX CALL AX
// Back; switch to original stack, re-establish // Back; switch to original g and stack, re-establish
// "DF is clear" invariant. // "DF is clear" invariant.
CLD CLD
get_tls(DI)
MOVL 8(SP), SI
MOVL SI, g(DI)
MOVL 4(SP), SP MOVL 4(SP), SP
RET RET
// runcgocallback(G *g1, void* sp, void (*fn)(void))
// Switch to g1 and sp, call fn, switch back. fn's arguments are on
// the new stack.
TEXT runcgocallback(SB),7,$32
MOVL g1+0(FP), DX
MOVL sp+4(FP), AX
MOVL fn+8(FP), BX
// We are running on m's scheduler stack. Save current SP
// into m->sched.sp so that a recursive call to runcgo doesn't
// clobber our stack, and also so that we can restore
// the SP when the call finishes. Reusing m->sched.sp
// for this purpose depends on the fact that there is only
// one possible gosave of m->sched.
get_tls(CX)
MOVL DX, g(CX)
MOVL m(CX), CX
MOVL SP, (m_sched+gobuf_sp)(CX)
// Set new SP, call fn
MOVL AX, SP
CALL BX
// Restore old g and SP, return
get_tls(CX)
MOVL m(CX), DX
MOVL m_g0(DX), BX
MOVL BX, g(CX)
MOVL (m_sched+gobuf_sp)(DX), SP
RET
// check that SP is in range [g->stackbase, g->stackguard) // check that SP is in range [g->stackbase, g->stackguard)
TEXT stackcheck(SB), 7, $0 TEXT stackcheck(SB), 7, $0
get_tls(CX) get_tls(CX)
......
...@@ -13,11 +13,21 @@ void ·exitsyscall(void); ...@@ -13,11 +13,21 @@ void ·exitsyscall(void);
void void
cgocall(void (*fn)(void*), void *arg) cgocall(void (*fn)(void*), void *arg)
{ {
G *oldlock;
if(initcgo == nil) if(initcgo == nil)
throw("cgocall unavailable"); throw("cgocall unavailable");
ncgocall++; ncgocall++;
/*
* Lock g to m to ensure we stay on the same stack if we do a
* cgo callback.
*/
oldlock = m->lockedg;
m->lockedg = g;
g->lockedm = m;
/* /*
* Announce we are entering a system call * Announce we are entering a system call
* so that the scheduler knows to create another * so that the scheduler knows to create another
...@@ -27,9 +37,49 @@ cgocall(void (*fn)(void*), void *arg) ...@@ -27,9 +37,49 @@ cgocall(void (*fn)(void*), void *arg)
·entersyscall(); ·entersyscall();
runcgo(fn, arg); runcgo(fn, arg);
·exitsyscall(); ·exitsyscall();
m->lockedg = oldlock;
if(oldlock == nil)
g->lockedm = nil;
return; return;
} }
// When a C function calls back into Go, the wrapper function will
// call this. This switches to a Go stack, copies the arguments
// (arg/argsize) on to the stack, calls the function, copies the
// arguments back where they came from, and finally returns to the old
// stack.
void
cgocallback(void (*fn)(void), void *arg, int32 argsize)
{
Gobuf oldsched;
G *g1;
void *sp;
if(g != m->g0)
throw("bad g in cgocallback");
oldsched = m->sched;
g1 = m->curg;
startcgocallback(g1);
sp = g1->sched.sp - argsize;
if(sp < g1->stackguard)
throw("g stack overflow in cgocallback");
mcpy(sp, arg, argsize);
runcgocallback(g1, sp, fn);
mcpy(arg, sp, argsize);
endcgocallback(g1);
m->sched = oldsched;
}
void void
·Cgocalls(int64 ret) ·Cgocalls(int64 ret)
{ {
......
...@@ -7,5 +7,6 @@ ...@@ -7,5 +7,6 @@
*/ */
void cgocall(void (*fn)(void*), void*); void cgocall(void (*fn)(void*), void*);
void cgocallback(void (*fn)(void), void*, int32);
void *cmalloc(uintptr); void *cmalloc(uintptr);
void cfree(void*); void cfree(void*);
...@@ -544,8 +544,8 @@ gosched(void) ...@@ -544,8 +544,8 @@ gosched(void)
// The goroutine g is about to enter a system call. // The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore. // Record that it's not using the cpu anymore.
// This is called only from the go syscall library, not // This is called only from the go syscall library and cgocall,
// from the low-level system calls used by the runtime. // not from the low-level system calls used by the runtime.
void void
·entersyscall(void) ·entersyscall(void)
{ {
...@@ -604,6 +604,28 @@ void ...@@ -604,6 +604,28 @@ void
gosched(); gosched();
} }
// Start scheduling g1 again for a cgo callback.
void
startcgocallback(G* g1)
{
lock(&sched);
g1->status = Grunning;
sched.msyscall--;
sched.mcpu++;
unlock(&sched);
}
// Stop scheduling g1 after a cgo callback.
void
endcgocallback(G* g1)
{
lock(&sched);
g1->status = Gsyscall;
sched.mcpu--;
sched.msyscall++;
unlock(&sched);
}
/* /*
* stack layout parameters. * stack layout parameters.
* known to linkers. * known to linkers.
......
...@@ -196,8 +196,6 @@ struct G ...@@ -196,8 +196,6 @@ struct G
bool ispanic; bool ispanic;
M* m; // for debuggers, but offset not hard-coded M* m; // for debuggers, but offset not hard-coded
M* lockedm; M* lockedm;
void (*cgofn)(void*); // for cgo/ffi
void *cgoarg;
int32 sig; int32 sig;
uintptr sigcode0; uintptr sigcode0;
uintptr sigcode1; uintptr sigcode1;
...@@ -432,8 +430,11 @@ void breakpoint(void); ...@@ -432,8 +430,11 @@ void breakpoint(void);
void gosched(void); void gosched(void);
void goexit(void); void goexit(void);
void runcgo(void (*fn)(void*), void*); void runcgo(void (*fn)(void*), void*);
void runcgocallback(G*, void*, void (*fn)());
void ·entersyscall(void); void ·entersyscall(void);
void ·exitsyscall(void); void ·exitsyscall(void);
void startcgocallback(G*);
void endcgocallback(G*);
G* newproc1(byte*, byte*, int32, int32); G* newproc1(byte*, byte*, int32, int32);
void siginit(void); void siginit(void);
bool sigsend(int32 sig); bool sigsend(int32 sig);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment