summaryrefslogtreecommitdiff
path: root/src/runtime/rt2_amd64.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/rt2_amd64.c')
-rw-r--r--src/runtime/rt2_amd64.c140
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");
+}
+
+