diff options
Diffstat (limited to 'src/runtime/rt2_amd64.c')
-rw-r--r-- | src/runtime/rt2_amd64.c | 140 |
1 files changed, 139 insertions, 1 deletions
diff --git a/src/runtime/rt2_amd64.c b/src/runtime/rt2_amd64.c index 762eaad58..84ae0b1ba 100644 --- a/src/runtime/rt2_amd64.c +++ b/src/runtime/rt2_amd64.c @@ -15,6 +15,7 @@ traceback(byte *pc0, byte *sp, G *g) uint64 pc; int32 i, n; Func *f; + byte *p; pc = (uint64)pc0; @@ -36,6 +37,16 @@ traceback(byte *pc0, byte *sp, G *g) } f = findfunc(pc); if(f == nil) { + // dangerous, but poke around to see if it is a closure + p = (byte*)pc; + // ADDQ $xxx, SP; RET + if(p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) { + sp += *(uint32*)(p+3) + 8; + pc = *(uint64*)(sp - 8); + if(pc <= 0x1000) + return; + continue; + } printf("%p unknown pc\n", pc); return; } @@ -76,6 +87,7 @@ sys·Caller(int32 n, uint64 retpc, string retfile, int32 retline, bool retbool) { uint64 pc; byte *sp; + byte *p; Stktop *stk; Func *f; @@ -110,9 +122,19 @@ sys·Caller(int32 n, uint64 retpc, string retfile, int32 retline, bool retbool) else sp += f->frame; + loop: pc = *(uint64*)(sp-8); - if(pc <= 0x1000 || (f = findfunc(pc)) == nil) + if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { + // dangerous, but let's try this. + // see if it is a closure. + p = (byte*)pc; + // ADDQ $xxx, SP; RET + if(p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) { + sp += *(uint32*)(p+3) + 8; + goto loop; + } goto error; + } } retpc = pc; @@ -124,3 +146,119 @@ sys·Caller(int32 n, uint64 retpc, string retfile, int32 retline, bool retbool) FLUSH(&retline); FLUSH(&retbool); } + +#pragma textflag 7 +// func closure(siz int32, +// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, +// arg0, arg1, arg2 *ptr) (func(xxx) yyy) +void +sys·closure(int32 siz, byte *fn, byte *arg0) +{ + byte *p, *q, **ret; + int32 i, n; + int64 pcrel; + + if(siz < 0 || siz%8 != 0) + throw("bad closure size"); + + ret = (byte**)((byte*)&arg0 + siz); + + if(siz > 100) { + // TODO(rsc): implement stack growth preamble? + throw("closure too big"); + } + + // compute size of new fn. + // must match code laid out below. + n = 7+10+3; // SUBQ MOVQ MOVQ + if(siz <= 4*8) + n += 2*siz/8; // MOVSQ MOVSQ... + else + n += 7+3; // MOVQ REP MOVSQ + n += 12; // CALL worst case; sometimes only 5 + n += 7+1; // ADDQ RET + + // store args aligned after code, so gc can find them. + n += siz; + if(n%8) + n += 8 - n%8; + + p = mal(n); + *ret = p; + q = p + n - siz; + mcpy(q, (byte*)&arg0, siz); + + // SUBQ $siz, SP + *p++ = 0x48; + *p++ = 0x81; + *p++ = 0xec; + *(uint32*)p = siz; + p += 4; + + // MOVQ $q, SI + *p++ = 0x48; + *p++ = 0xbe; + *(byte**)p = q; + p += 8; + + // MOVQ SP, DI + *p++ = 0x48; + *p++ = 0x89; + *p++ = 0xe7; + + if(siz <= 4*8) { + for(i=0; i<siz; i+=8) { + // MOVSQ + *p++ = 0x48; + *p++ = 0xa5; + } + } else { + // MOVQ $(siz/8), CX [32-bit immediate siz/8] + *p++ = 0x48; + *p++ = 0xc7; + *p++ = 0xc1; + *(uint32*)p = siz/8; + p += 4; + + // REP; MOVSQ + *p++ = 0xf3; + *p++ = 0x48; + *p++ = 0xa5; + } + + + // call fn + pcrel = fn - (p+5); + if((int32)pcrel == pcrel) { + // can use direct call with pc-relative offset + // CALL fn + *p++ = 0xe8; + *(int32*)p = pcrel; + p += 4; + } else { + // MOVQ $fn, CX [64-bit immediate fn] + *p++ = 0x48; + *p++ = 0xb9; + *(byte**)p = fn; + p += 8; + + // CALL *CX + *p++ = 0xff; + *p++ = 0xd1; + } + + // ADDQ $siz, SP + *p++ = 0x48; + *p++ = 0x81; + *p++ = 0xc4; + *(uint32*)p = siz; + p += 4; + + // RET + *p++ = 0xc3; + + if(p > q) + throw("bad math in sys.closure"); +} + + |