runtime: reimplement reflect.call to not use stack splitting.

R=golang-dev, r, khr, rsc
CC=golang-dev
https://golang.org/cl/12053043
This commit is contained in:
Keith Randall 2013-08-02 13:03:14 -07:00
parent b8c8cb8509
commit 9cd570680b
10 changed files with 321 additions and 24 deletions

View file

@ -151,6 +151,7 @@ func testCallbackCallers(t *testing.T) {
n := 0
name := []string{
"test.goCallback",
"runtime.call16",
"runtime.cgocallbackg1",
"runtime.cgocallbackg",
"runtime.cgocallback_gofunc",

View file

@ -1522,7 +1522,7 @@ pctospadj(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg)
oldval = 0;
if(phase == 0)
return oldval;
if(oldval + p->spadj < -10000 || oldval + p->spadj > 1000000000) {
if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) {
diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj);
errorexit();
}

View file

@ -3509,3 +3509,14 @@ func (x *exhaustive) Choose(max int) int {
func (x *exhaustive) Maybe() bool {
return x.Choose(2) == 1
}
func bigArgFunc(v [(1<<30)+64]byte) {
}
func TestBigArgs(t *testing.T) {
if !testing.Short() && ^uint(0)>>32 != 0 { // test on 64-bit only
v := new([(1<<30)+64]byte)
bigArgFunc(*v) // regular calls are ok
shouldPanic(func() {ValueOf(bigArgFunc).Call([]Value{ValueOf(*v)})}) // ... just not reflect calls
}
}

View file

@ -244,12 +244,12 @@ TEXT runtime·morestack(SB),7,$0-0
MOVL $0, 0x1003 // crash if newstack returns
RET
// Called from reflection library. Mimics morestack,
// Called from panic. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
TEXT reflect·call(SB), 7, $0-12
TEXT runtime·newstackcall(SB), 7, $0-12
get_tls(CX)
MOVL m(CX), BX
@ -264,12 +264,12 @@ TEXT reflect·call(SB), 7, $0-12
// Save our own state as the PC and SP to restore
// if this goroutine needs to be restarted.
MOVL $reflect·call(SB), (g_sched+gobuf_pc)(AX)
MOVL $runtime·newstackcall(SB), (g_sched+gobuf_pc)(AX)
MOVL SP, (g_sched+gobuf_sp)(AX)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to 1, as a hint to newstack
// that this is a call from reflect·call.
// that this is a call from runtime·newstackcall.
// If it turns out that f needs a larger frame than
// the default stack, f's usual stack growth prolog will
// allocate a new segment (and recopy the arguments).
@ -291,6 +291,95 @@ TEXT reflect·call(SB), 7, $0-12
MOVL $0, 0x1103 // crash if newstack returns
RET
// reflect·call: call a function with the given argument list
// func call(f *FuncVal, arg *byte, argsize uint32).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
#define DISPATCH(NAME,MAXSIZE) \
CMPL CX, $MAXSIZE; \
JA 3(PC); \
MOVL $runtime·NAME(SB), AX; \
JMP AX
// Note: can't just "JMP runtime·NAME(SB)" - bad inlining results.
TEXT reflect·call(SB), 7, $0-12
MOVL argsize+8(FP), CX
DISPATCH(call16, 16)
DISPATCH(call32, 32)
DISPATCH(call64, 64)
DISPATCH(call128, 128)
DISPATCH(call256, 256)
DISPATCH(call512, 512)
DISPATCH(call1024, 1024)
DISPATCH(call2048, 2048)
DISPATCH(call4096, 4096)
DISPATCH(call8192, 8192)
DISPATCH(call16384, 16384)
DISPATCH(call32768, 32768)
DISPATCH(call65536, 65536)
DISPATCH(call131072, 131072)
DISPATCH(call262144, 262144)
DISPATCH(call524288, 524288)
DISPATCH(call1048576, 1048576)
DISPATCH(call2097152, 2097152)
DISPATCH(call4194304, 4194304)
DISPATCH(call8388608, 8388608)
DISPATCH(call16777216, 16777216)
DISPATCH(call33554432, 33554432)
DISPATCH(call67108864, 67108864)
DISPATCH(call134217728, 134217728)
DISPATCH(call268435456, 268435456)
DISPATCH(call536870912, 536870912)
DISPATCH(call1073741824, 1073741824)
MOVL $runtime·badreflectcall(SB), AX
JMP AX
#define CALLFN(NAME,MAXSIZE,FLAGS) \
TEXT runtime·NAME(SB), FLAGS, $MAXSIZE-12; \
/* copy arguments to stack */ \
MOVL argptr+4(FP), SI; \
MOVL argsize+8(FP), CX; \
MOVL SP, DI; \
REP;MOVSB; \
/* call function */ \
MOVL f+0(FP), DX; \
CALL (DX); \
/* copy return values back */ \
MOVL argptr+4(FP), DI; \
MOVL argsize+8(FP), CX; \
MOVL SP, SI; \
REP;MOVSB; \
RET
CALLFN(call16, 16, 7)
CALLFN(call32, 32, 7)
CALLFN(call64, 64, 7)
CALLFN(call128, 128, 0)
CALLFN(call256, 256, 0)
CALLFN(call512, 512, 0)
CALLFN(call1024, 1024, 0)
CALLFN(call2048, 2048, 0)
CALLFN(call4096, 4096, 0)
CALLFN(call8192, 8192, 0)
CALLFN(call16384, 16384, 0)
CALLFN(call32768, 32768, 0)
CALLFN(call65536, 65536, 0)
CALLFN(call131072, 131072, 0)
CALLFN(call262144, 262144, 0)
CALLFN(call524288, 524288, 0)
CALLFN(call1048576, 1048576, 0)
CALLFN(call2097152, 2097152, 0)
CALLFN(call4194304, 4194304, 0)
CALLFN(call8388608, 8388608, 0)
CALLFN(call16777216, 16777216, 0)
CALLFN(call33554432, 33554432, 0)
CALLFN(call67108864, 67108864, 0)
CALLFN(call134217728, 134217728, 0)
CALLFN(call268435456, 268435456, 0)
CALLFN(call536870912, 536870912, 0)
CALLFN(call1073741824, 1073741824, 0)
// Return point when leaving stack.
//

View file

@ -225,12 +225,12 @@ TEXT runtime·morestack(SB),7,$0-0
MOVQ $0, 0x1003 // crash if newstack returns
RET
// Called from reflection library. Mimics morestack,
// Called from panic. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
TEXT reflect·call(SB), 7, $0-20
TEXT runtime·newstackcall(SB), 7, $0-20
get_tls(CX)
MOVQ m(CX), BX
@ -245,12 +245,12 @@ TEXT reflect·call(SB), 7, $0-20
// Save our own state as the PC and SP to restore
// if this goroutine needs to be restarted.
MOVQ $reflect·call(SB), (g_sched+gobuf_pc)(AX)
MOVQ $runtime·newstackcall(SB), (g_sched+gobuf_pc)(AX)
MOVQ SP, (g_sched+gobuf_sp)(AX)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to 1, as a hint to newstack
// that this is a call from reflect·call.
// that this is a call from runtime·newstackcall.
// If it turns out that f needs a larger frame than
// the default stack, f's usual stack growth prolog will
// allocate a new segment (and recopy the arguments).
@ -272,6 +272,96 @@ TEXT reflect·call(SB), 7, $0-20
MOVQ $0, 0x1103 // crash if newstack returns
RET
// reflect·call: call a function with the given argument list
// func call(f *FuncVal, arg *byte, argsize uint32).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
#define DISPATCH(NAME,MAXSIZE) \
CMPQ CX, $MAXSIZE; \
JA 3(PC); \
MOVQ $runtime·NAME(SB), AX; \
JMP AX
// Note: can't just "JMP runtime·NAME(SB)" - bad inlining results.
TEXT reflect·call(SB), 7, $0-20
MOVLQZX argsize+16(FP), CX
DISPATCH(call16, 16)
DISPATCH(call32, 32)
DISPATCH(call64, 64)
DISPATCH(call128, 128)
DISPATCH(call256, 256)
DISPATCH(call512, 512)
DISPATCH(call1024, 1024)
DISPATCH(call2048, 2048)
DISPATCH(call4096, 4096)
DISPATCH(call8192, 8192)
DISPATCH(call16384, 16384)
DISPATCH(call32768, 32768)
DISPATCH(call65536, 65536)
DISPATCH(call131072, 131072)
DISPATCH(call262144, 262144)
DISPATCH(call524288, 524288)
DISPATCH(call1048576, 1048576)
DISPATCH(call2097152, 2097152)
DISPATCH(call4194304, 4194304)
DISPATCH(call8388608, 8388608)
DISPATCH(call16777216, 16777216)
DISPATCH(call33554432, 33554432)
DISPATCH(call67108864, 67108864)
DISPATCH(call134217728, 134217728)
DISPATCH(call268435456, 268435456)
DISPATCH(call536870912, 536870912)
DISPATCH(call1073741824, 1073741824)
MOVQ $runtime·badreflectcall(SB), AX
JMP AX
#define CALLFN(NAME,MAXSIZE,FLAGS) \
TEXT runtime·NAME(SB), FLAGS, $MAXSIZE-20; \
/* copy arguments to stack */ \
MOVQ argptr+8(FP), SI; \
MOVLQZX argsize+16(FP), CX; \
MOVQ SP, DI; \
REP;MOVSB; \
/* call function */ \
MOVQ f+0(FP), DX; \
CALL (DX); \
/* copy return values back */ \
MOVQ argptr+8(FP), DI; \
MOVLQZX argsize+16(FP), CX; \
MOVQ SP, SI; \
REP;MOVSB; \
RET
CALLFN(call16, 16, 7)
CALLFN(call32, 32, 7)
CALLFN(call64, 64, 7)
CALLFN(call128, 128, 0)
CALLFN(call256, 256, 0)
CALLFN(call512, 512, 0)
CALLFN(call1024, 1024, 0)
CALLFN(call2048, 2048, 0)
CALLFN(call4096, 4096, 0)
CALLFN(call8192, 8192, 0)
CALLFN(call16384, 16384, 0)
CALLFN(call32768, 32768, 0)
CALLFN(call65536, 65536, 0)
CALLFN(call131072, 131072, 0)
CALLFN(call262144, 262144, 0)
CALLFN(call524288, 524288, 0)
CALLFN(call1048576, 1048576, 0)
CALLFN(call2097152, 2097152, 0)
CALLFN(call4194304, 4194304, 0)
CALLFN(call8388608, 8388608, 0)
CALLFN(call16777216, 16777216, 0)
CALLFN(call33554432, 33554432, 0)
CALLFN(call67108864, 67108864, 0)
CALLFN(call134217728, 134217728, 0)
CALLFN(call268435456, 268435456, 0)
CALLFN(call536870912, 536870912, 0)
CALLFN(call1073741824, 1073741824, 0)
// Return point when leaving stack.
//
// Lessstack can appear in stack traces for the same reason

View file

@ -208,12 +208,12 @@ TEXT runtime·morestack(SB),7,$-4-0
// is still in this function, and not the beginning of the next.
RET
// Called from reflection library. Mimics morestack,
// Called from panic. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
TEXT reflect·call(SB), 7, $-4-12
TEXT runtime·newstackcall(SB), 7, $-4-12
// Save our caller's state as the PC and SP to
// restore when returning from f.
MOVW LR, (m_morebuf+gobuf_pc)(m) // our caller's PC
@ -222,14 +222,14 @@ TEXT reflect·call(SB), 7, $-4-12
// Save our own state as the PC and SP to restore
// if this goroutine needs to be restarted.
MOVW $reflect·call(SB), R11
MOVW $runtime·newstackcall(SB), R11
MOVW R11, (g_sched+gobuf_pc)(g)
MOVW LR, (g_sched+gobuf_lr)(g)
MOVW SP, (g_sched+gobuf_sp)(g)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to 1, as a hint to newstack
// that this is a call from reflect·call.
// that this is a call from runtime·newstackcall.
// If it turns out that f needs a larger frame than
// the default stack, f's usual stack growth prolog will
// allocate a new segment (and recopy the arguments).
@ -248,6 +248,105 @@ TEXT reflect·call(SB), 7, $-4-12
MOVW (g_sched+gobuf_sp)(g), SP
B runtime·newstack(SB)
// reflect·call: call a function with the given argument list
// func call(f *FuncVal, arg *byte, argsize uint32).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
#define DISPATCH(NAME,MAXSIZE) \
CMP $MAXSIZE, R0; \
B.HI 3(PC); \
MOVW $runtime·NAME(SB), R1; \
B (R1)
TEXT reflect·call(SB), 7, $-4-12
MOVW argsize+8(FP), R0
DISPATCH(call16, 16)
DISPATCH(call32, 32)
DISPATCH(call64, 64)
DISPATCH(call128, 128)
DISPATCH(call256, 256)
DISPATCH(call512, 512)
DISPATCH(call1024, 1024)
DISPATCH(call2048, 2048)
DISPATCH(call4096, 4096)
DISPATCH(call8192, 8192)
DISPATCH(call16384, 16384)
DISPATCH(call32768, 32768)
DISPATCH(call65536, 65536)
DISPATCH(call131072, 131072)
DISPATCH(call262144, 262144)
DISPATCH(call524288, 524288)
DISPATCH(call1048576, 1048576)
DISPATCH(call2097152, 2097152)
DISPATCH(call4194304, 4194304)
DISPATCH(call8388608, 8388608)
DISPATCH(call16777216, 16777216)
DISPATCH(call33554432, 33554432)
DISPATCH(call67108864, 67108864)
DISPATCH(call134217728, 134217728)
DISPATCH(call268435456, 268435456)
DISPATCH(call536870912, 536870912)
DISPATCH(call1073741824, 1073741824)
MOVW $runtime·badreflectcall(SB), R1
B (R1)
#define CALLFN(NAME,MAXSIZE,FLAGS) \
TEXT runtime·NAME(SB), FLAGS, $MAXSIZE-12; \
/* copy arguments to stack */ \
MOVW argptr+4(FP), R0; \
MOVW argsize+8(FP), R2; \
ADD $4, SP, R1; \
CMP $0, R2; \
B.EQ 5(PC); \
MOVBU.P 1(R0), R5; \
MOVBU.P R5, 1(R1); \
SUB $1, R2, R2; \
B -5(PC); \
/* call function */ \
MOVW f+0(FP), R7; \
MOVW (R7), R0; \
BL (R0); \
/* copy return values back */ \
MOVW argptr+4(FP), R0; \
MOVW argsize+8(FP), R2; \
ADD $4, SP, R1; \
CMP $0, R2; \
RET.EQ ; \
MOVBU.P 1(R1), R5; \
MOVBU.P R5, 1(R0); \
SUB $1, R2, R2; \
B -5(PC) \
CALLFN(call16, 16, 7)
CALLFN(call32, 32, 7)
CALLFN(call64, 64, 7)
CALLFN(call128, 128, 0)
CALLFN(call256, 256, 0)
CALLFN(call512, 512, 0)
CALLFN(call1024, 1024, 0)
CALLFN(call2048, 2048, 0)
CALLFN(call4096, 4096, 0)
CALLFN(call8192, 8192, 0)
CALLFN(call16384, 16384, 0)
CALLFN(call32768, 32768, 0)
CALLFN(call65536, 65536, 0)
CALLFN(call131072, 131072, 0)
CALLFN(call262144, 262144, 0)
CALLFN(call524288, 524288, 0)
CALLFN(call1048576, 1048576, 0)
CALLFN(call2097152, 2097152, 0)
CALLFN(call4194304, 4194304, 0)
CALLFN(call8388608, 8388608, 0)
CALLFN(call16777216, 16777216, 0)
CALLFN(call33554432, 33554432, 0)
CALLFN(call67108864, 67108864, 0)
CALLFN(call134217728, 134217728, 0)
CALLFN(call268435456, 268435456, 0)
CALLFN(call536870912, 536870912, 0)
CALLFN(call1073741824, 1073741824, 0)
// Return point when leaving stack.
// using frame size $-4 means do not save LR on stack.
//

View file

@ -241,10 +241,10 @@ runtime·panic(Eface e)
break;
// take defer off list in case of recursive panic
popdefer();
g->ispanic = true; // rock for newstack, where reflect.call ends up
g->ispanic = true; // rock for newstack, where reflect.newstackcall ends up
argp = d->argp;
pc = d->pc;
reflect·call(d->fn, (byte*)d->args, d->siz);
runtime·newstackcall(d->fn, (byte*)d->args, d->siz);
freedefer(d);
if(p->recovered) {
g->panic = p->link;

View file

@ -1926,6 +1926,12 @@ runtime·badmcall2(void) // called from assembly
runtime·throw("runtime: mcall function returned");
}
void
runtime·badreflectcall(void) // called from assembly
{
runtime·panicstring("runtime: arg size to reflect.call more than 1GB");
}
static struct {
Lock;
void (*fn)(uintptr*, int32);

View file

@ -985,6 +985,7 @@ void runtime·printuint(uint64);
void runtime·printhex(uint64);
void runtime·printslice(Slice);
void runtime·printcomplex(Complex128);
void runtime·newstackcall(FuncVal*, byte*, uint32);
void reflect·call(FuncVal*, byte*, uint32);
void runtime·panic(Eface);
void runtime·panicindex(void);

View file

@ -183,7 +183,7 @@ runtime·oldstack(void)
runtime·gogo(&gp->sched);
}
// Called from reflect·call or from runtime·morestack when a new
// Called from runtime·newstackcall or from runtime·morestack when a new
// stack segment is needed. Allocate a new stack big enough for
// m->moreframesize bytes, copy m->moreargsize bytes to the new frame,
// and then act as though runtime·lessstack called the function at
@ -198,7 +198,7 @@ runtime·newstack(void)
uintptr *src, *dst, *dstend;
G *gp;
Gobuf label;
bool reflectcall;
bool newstackcall;
uintptr free;
if(m->morebuf.g != m->curg) {
@ -217,12 +217,12 @@ runtime·newstack(void)
argsize = m->moreargsize;
gp->status = Gwaiting;
gp->waitreason = "stack split";
reflectcall = framesize==1;
if(reflectcall)
newstackcall = framesize==1;
if(newstackcall)
framesize = 0;
// For reflectcall the context already points to beginning of reflect·call.
if(!reflectcall)
// For newstackcall the context already points to beginning of runtime·newstackcall.
if(!newstackcall)
runtime·rewindmorestack(&gp->sched);
sp = gp->sched.sp;
@ -269,8 +269,8 @@ runtime·newstack(void)
runtime·gosched0(gp); // never return
}
if(reflectcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
// special case: called from reflect.call (framesize==1)
if(newstackcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
// special case: called from runtime.newstackcall (framesize==1)
// to call code with an arbitrary argument size,
// and we have enough space on the current stack.
// the new Stktop* is necessary to unwind, but
@ -334,7 +334,7 @@ runtime·newstack(void)
label.sp = sp;
label.pc = (uintptr)runtime·lessstack;
label.g = m->curg;
if(reflectcall)
if(newstackcall)
runtime·gostartcallfn(&label, (FuncVal*)m->cret);
else {
runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt);