aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2009-02-06 13:46:56 -0800
committerRuss Cox <rsc@golang.org>2009-02-06 13:46:56 -0800
commit0f4f2a61836bba7dadb0cbdd00dfa53ba549555e (patch)
treeb8e48ca2a3b0f4983102041168ebc81244a713e8 /src/runtime
parent187cf78a7c2fff4c1f606dacc412d5dda84f45b2 (diff)
downloadgo-0f4f2a61836bba7dadb0cbdd00dfa53ba549555e.tar.xz
closures - runtime and debugger support, test case
R=r DELTA=257 (250 added, 1 deleted, 6 changed) OCL=24509 CL=24565
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/malloc.c2
-rw-r--r--src/runtime/mem.c4
-rw-r--r--src/runtime/rt2_amd64.c140
3 files changed, 142 insertions, 4 deletions
diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c
index 74354357c5..ac5de61a5a 100644
--- a/src/runtime/malloc.c
+++ b/src/runtime/malloc.c
@@ -203,7 +203,7 @@ void*
SysAlloc(uintptr n)
{
mstats.sys += n;
- return sys_mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
+ return sys_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0);
}
void
diff --git a/src/runtime/mem.c b/src/runtime/mem.c
index 9d6a3969b5..e2208d7bd3 100644
--- a/src/runtime/mem.c
+++ b/src/runtime/mem.c
@@ -29,7 +29,7 @@ brk(uint32 n)
{
byte *v;
- v = sys_mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
+ v = sys_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0);
m->mem.nmmap += n;
return v;
}
@@ -64,7 +64,7 @@ oldmal(uint32 n)
// so we have to call sys_mmap directly - it is written
// in assembly and tagged not to grow the stack.
m->mem.hunk =
- sys_mmap(nil, NHUNK, PROT_READ|PROT_WRITE,
+ sys_mmap(nil, NHUNK, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_ANON|MAP_PRIVATE, 0, 0);
m->mem.nhunk = NHUNK;
m->mem.nmmap += NHUNK;
diff --git a/src/runtime/rt2_amd64.c b/src/runtime/rt2_amd64.c
index 762eaad581..84ae0b1ba8 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");
+}
+
+