diff options
| author | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
|---|---|---|
| committer | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
| commit | d90e7cbac65c5792ce312ee82fbe03a5dfc98c6f (patch) | |
| tree | 7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/pkg/runtime/amd64 | |
| parent | bf5c0c957c3c3ea9add6cfd51b90c463cb4814b5 (diff) | |
| download | go-d90e7cbac65c5792ce312ee82fbe03a5dfc98c6f.tar.xz | |
mv src/lib to src/pkg
tests: all.bash passes, gobuild still works, godoc still works.
R=rsc
OCL=30096
CL=30102
Diffstat (limited to 'src/pkg/runtime/amd64')
| -rw-r--r-- | src/pkg/runtime/amd64/asm.s | 207 | ||||
| -rw-r--r-- | src/pkg/runtime/amd64/closure.c | 121 | ||||
| -rw-r--r-- | src/pkg/runtime/amd64/traceback.c | 146 |
3 files changed, 474 insertions, 0 deletions
diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s new file mode 100644 index 0000000000..6fc01bbc98 --- /dev/null +++ b/src/pkg/runtime/amd64/asm.s @@ -0,0 +1,207 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + + +TEXT _rt0_amd64(SB),7,$-8 + + // copy arguments forward on an even stack + + MOVQ 0(SP), AX // argc + LEAQ 8(SP), BX // argv + SUBQ $(4*8+7), SP // 2args 2auto + ANDQ $~7, SP + MOVQ AX, 16(SP) + MOVQ BX, 24(SP) + + // set the per-goroutine and per-mach registers + + LEAQ m0(SB), R14 // dedicated m. register + LEAQ g0(SB), R15 // dedicated g. register + MOVQ R15, 0(R14) // m has pointer to its g0 + + // create istack out of the given (operating system) stack + + LEAQ (-8192+104)(SP), AX + MOVQ AX, 0(R15) // 0(R15) is stack limit (w 104b guard) + MOVQ SP, 8(R15) // 8(R15) is base + + CLD // convention is D is always left cleared + CALL check(SB) + + MOVL 16(SP), AX // copy argc + MOVL AX, 0(SP) + MOVQ 24(SP), AX // copy argv + MOVQ AX, 8(SP) + CALL args(SB) + CALL osinit(SB) + CALL schedinit(SB) + + // create a new goroutine to start program + PUSHQ $mainstart(SB) // entry + PUSHQ $16 // arg size + CALL sys·newproc(SB) + POPQ AX + POPQ AX + + // start this M + CALL mstart(SB) + + CALL notok(SB) // never returns + RET + +TEXT mainstart(SB),7,$0 + CALL main·init(SB) + CALL initdone(SB) + CALL main·main(SB) + PUSHQ $0 + CALL exit(SB) + POPQ AX + CALL notok(SB) + RET + +TEXT breakpoint(SB),7,$0 + BYTE $0xcc + RET + +/* + * go-routine + */ +TEXT gogo(SB), 7, $0 + MOVQ 8(SP), AX // gobuf + MOVQ 0(AX), SP // restore SP + MOVQ 8(AX), AX + MOVQ AX, 0(SP) // put PC on the stack + MOVL $1, AX // return 1 + RET + +TEXT gosave(SB), 7, $0 + MOVQ 8(SP), AX // gobuf + MOVQ SP, 0(AX) // save SP + MOVQ 0(SP), BX + MOVQ BX, 8(AX) // save PC + MOVL $0, AX // return 0 + RET + +/* + * support for morestack + */ + +// morestack trampolines +TEXT sys·morestack00+0(SB),7,$0 + MOVQ $0, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestack01+0(SB),7,$0 + SHLQ $32, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestack10+0(SB),7,$0 + MOVLQZX AX, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestack11+0(SB),7,$0 + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestackx(SB),7,$0 + POPQ AX + SHLQ $35, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack(SB), AX + JMP AX + +// subcases of morestack01 +// with const of 8,16,...48 +TEXT sys·morestack8(SB),7,$0 + PUSHQ $1 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack16(SB),7,$0 + PUSHQ $2 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack24(SB),7,$0 + PUSHQ $3 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack32(SB),7,$0 + PUSHQ $4 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack40(SB),7,$0 + PUSHQ $5 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack48(SB),7,$0 + PUSHQ $6 + MOVQ $sys·morestackx(SB), AX + JMP AX + +// return point when leaving new stack. save AX, jmp to lessstack to switch back +TEXT retfromnewstack(SB), 7, $0 + MOVQ AX, 16(R14) // save AX in m->cret + MOVQ $lessstack(SB), AX + JMP AX + +// gogo, returning 2nd arg instead of 1 +TEXT gogoret(SB), 7, $0 + MOVQ 16(SP), AX // return 2nd arg + MOVQ 8(SP), BX // gobuf + MOVQ 0(BX), SP // restore SP + MOVQ 8(BX), BX + MOVQ BX, 0(SP) // put PC on the stack + RET + +TEXT setspgoto(SB), 7, $0 + MOVQ 8(SP), AX // SP + MOVQ 16(SP), BX // fn to call + MOVQ 24(SP), CX // fn to return + MOVQ AX, SP + PUSHQ CX + JMP BX + POPQ AX // not reached + RET + +// bool cas(int32 *val, int32 old, int32 new) +// Atomically: +// if(*val == old){ +// *val = new; +// return 1; +// } else +// return 0; +TEXT cas(SB), 7, $0 + MOVQ 8(SP), BX + MOVL 16(SP), AX + MOVL 20(SP), CX + LOCK + CMPXCHGL CX, 0(BX) + JZ 3(PC) + MOVL $0, AX + RET + MOVL $1, AX + RET + +// void jmpdefer(fn, sp); +// called from deferreturn. +// 1. pop the caller +// 2. sub 5 bytes from the callers return +// 3. jmp to the argument +TEXT jmpdefer(SB), 7, $0 + MOVQ 8(SP), AX // fn + MOVQ 16(SP), BX // caller sp + LEAQ -8(BX), SP // caller sp after CALL + SUBQ $5, (SP) // return to CALL again + JMP AX // but first run the deferred function diff --git a/src/pkg/runtime/amd64/closure.c b/src/pkg/runtime/amd64/closure.c new file mode 100644 index 0000000000..5717d3c5e8 --- /dev/null +++ b/src/pkg/runtime/amd64/closure.c @@ -0,0 +1,121 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" + +#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"); +} + + diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c new file mode 100644 index 0000000000..16d7bed72e --- /dev/null +++ b/src/pkg/runtime/amd64/traceback.c @@ -0,0 +1,146 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" + +void +traceback(byte *pc0, byte *sp, G *g) +{ + Stktop *stk; + uint64 pc; + int32 i, n; + Func *f; + byte *p; + + pc = (uint64)pc0; + + // If the PC is zero, it's likely a nil function call. + // Start in the caller's frame. + if(pc == 0) { + pc = *(uint64*)sp; + sp += 8; + } + + stk = (Stktop*)g->stackbase; + for(n=0; n<100; n++) { + while(pc == (uint64)retfromnewstack) { + // pop to earlier stack block + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *(uint64*)(sp+8); + sp += 16; // two irrelevant calls on stack: morestack plus its call + } + 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; + } + if(f->frame < 8) // assembly funcs say 0 but lie + sp += 8; + else + sp += f->frame; + + // print this frame + // main+0xf /home/rsc/go/src/runtime/x.go:23 + // main(0x1, 0x2, 0x3) + printf("%S", f->name); + if(pc > f->entry) + printf("+%X", pc - f->entry); + printf(" %S:%d\n", f->src, funcline(f, pc-1)); // -1 to get to CALL instr. + printf("\t%S(", f->name); + for(i = 0; i < f->args; i++) { + if(i != 0) + prints(", "); + sys·printhex(((uint32*)sp)[i]); + if(i >= 4) { + prints(", ..."); + break; + } + } + prints(")\n"); + + pc = *(uint64*)(sp-8); + if(pc <= 0x1000) + return; + } + prints("...\n"); +} + +// func caller(n int) (pc uint64, file string, line int, ok bool) +void +runtime·Caller(int32 n, uint64 retpc, String retfile, int32 retline, bool retbool) +{ + uint64 pc; + byte *sp; + byte *p; + Stktop *stk; + Func *f; + + // our caller's pc, sp. + sp = (byte*)&n; + pc = *(uint64*)(sp-8); + if((f = findfunc(pc)) == nil) { + error: + retpc = 0; + retline = 0; + retfile = emptystring; + retbool = false; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); + return; + } + + // now unwind n levels + stk = (Stktop*)g->stackbase; + while(n-- > 0) { + while(pc == (uint64)retfromnewstack) { + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *(uint64*)(sp+8); + sp += 16; + } + + if(f->frame < 8) // assembly functions lie + sp += 8; + else + sp += f->frame; + + loop: + pc = *(uint64*)(sp-8); + 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; + retfile = f->src; + retline = funcline(f, pc-1); + retbool = true; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); +} + + |
