diff options
author | Russ Cox <rsc@golang.org> | 2009-06-06 22:04:39 -0700 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2009-06-06 22:04:39 -0700 |
commit | 8257be5f9d4a4bdbb0ff096756a0e48401d620c8 (patch) | |
tree | 1195452c472b99bfd365ee1cc057e870178e63e0 /src/lib | |
parent | af9643dcc1336bcb6e6ed0817e68a9c2db2c5613 (diff) | |
download | golang-8257be5f9d4a4bdbb0ff096756a0e48401d620c8.tar.gz |
move src/runtime -> src/lib/runtime;
only automatic g4 mv here.
R=r
OCL=30002
CL=30007
Diffstat (limited to 'src/lib')
74 files changed, 14614 insertions, 0 deletions
diff --git a/src/lib/runtime/386/asm.s b/src/lib/runtime/386/asm.s new file mode 100644 index 000000000..5d3c4261a --- /dev/null +++ b/src/lib/runtime/386/asm.s @@ -0,0 +1,217 @@ +// 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_386(SB),7,$0 + // copy arguments forward on an even stack + MOVL 0(SP), AX // argc + LEAL 4(SP), BX // argv + SUBL $128, SP // plenty of scratch + ANDL $~7, SP + MOVL AX, 120(SP) // save argc, argv away + MOVL BX, 124(SP) + +/* + // write "go386\n" + PUSHL $6 + PUSHL $hello(SB) + PUSHL $1 + CALL sys·write(SB) + POPL AX + POPL AX + POPL AX +*/ + + CALL ldt0setup(SB) + + // set up %fs to refer to that ldt entry + MOVL $(7*8+7), AX + MOVW AX, FS + + // store through it, to make sure it works + MOVL $0x123, 0(FS) + MOVL tls0(SB), AX + CMPL AX, $0x123 + JEQ ok + MOVL AX, 0 +ok: + + // set up m and g "registers" + // g is 0(FS), m is 4(FS) + LEAL g0(SB), CX + MOVL CX, 0(FS) + LEAL m0(SB), AX + MOVL AX, 4(FS) + + // save m->g0 = g0 + MOVL CX, 0(AX) + + // create istack out of the OS stack + LEAL (-8192+104)(SP), AX // TODO: 104? + MOVL AX, 0(CX) // 8(g) is stack limit (w 104b guard) + MOVL SP, 4(CX) // 12(g) is base + CALL emptyfunc(SB) // fault if stack check is wrong + + // convention is D is always cleared + CLD + + CALL check(SB) + + // saved argc, argv + MOVL 120(SP), AX + MOVL AX, 0(SP) + MOVL 124(SP), AX + MOVL AX, 4(SP) + CALL args(SB) + CALL osinit(SB) + CALL schedinit(SB) + + // create a new goroutine to start program + PUSHL $mainstart(SB) // entry + PUSHL $8 // arg size + CALL sys·newproc(SB) + POPL AX + POPL AX + + // start this M + CALL mstart(SB) + + INT $3 + RET + +TEXT mainstart(SB),7,$0 + CALL main·init(SB) + CALL initdone(SB) + CALL main·main(SB) + PUSHL $0 + CALL exit(SB) + POPL AX + INT $3 + RET + +TEXT breakpoint(SB),7,$0 + BYTE $0xcc + RET + +// go-routine +TEXT gogo(SB), 7, $0 + MOVL 4(SP), AX // gobuf + MOVL 0(AX), SP // restore SP + MOVL 4(AX), AX + MOVL AX, 0(SP) // put PC on the stack + MOVL $1, AX + RET + +TEXT gosave(SB), 7, $0 + MOVL 4(SP), AX // gobuf + MOVL SP, 0(AX) // save SP + MOVL 0(SP), BX + MOVL BX, 4(AX) // save PC + MOVL $0, AX // return 0 + RET + +// support for morestack + +// return point when leaving new stack. +// save AX, jmp to lesstack to switch back +TEXT retfromnewstack(SB),7,$0 + MOVL 4(FS), BX // m + MOVL AX, 12(BX) // save AX in m->cret + JMP lessstack(SB) + +// gogo, returning 2nd arg instead of 1 +TEXT gogoret(SB), 7, $0 + MOVL 8(SP), AX // return 2nd arg + MOVL 4(SP), BX // gobuf + MOVL 0(BX), SP // restore SP + MOVL 4(BX), BX + MOVL BX, 0(SP) // put PC on the stack + RET + +TEXT setspgoto(SB), 7, $0 + MOVL 4(SP), AX // SP + MOVL 8(SP), BX // fn to call + MOVL 12(SP), CX // fn to return + MOVL AX, SP + PUSHL CX + JMP BX + POPL 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 + MOVL 4(SP), BX + MOVL 8(SP), AX + MOVL 12(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 + MOVL 4(SP), AX // fn + MOVL 8(SP), BX // caller sp + LEAL -4(BX), SP // caller sp after CALL + SUBL $5, (SP) // return to CALL again + JMP AX // but first run the deferred function + +TEXT sys·memclr(SB),7,$0 + MOVL 4(SP), DI // arg 1 addr + MOVL 8(SP), CX // arg 2 count + ADDL $3, CX + SHRL $2, CX + MOVL $0, AX + CLD + REP + STOSL + RET + +TEXT sys·getcallerpc+0(SB),7,$0 + MOVL x+0(FP),AX // addr of first arg + MOVL -4(AX),AX // get calling pc + RET + +TEXT sys·setcallerpc+0(SB),7,$0 + MOVL x+0(FP),AX // addr of first arg + MOVL x+4(FP), BX + MOVL BX, -4(AX) // set calling pc + RET + +TEXT ldt0setup(SB),7,$16 + // set up ldt 7 to point at tls0 + // ldt 1 would be fine on Linux, but on OS X, 7 is as low as we can go. + MOVL $7, 0(SP) + LEAL tls0(SB), AX + MOVL AX, 4(SP) + MOVL $32, 8(SP) // sizeof(tls array) + CALL setldt(SB) + RET + +GLOBL m0+0(SB), $1024 +GLOBL g0+0(SB), $1024 + +GLOBL tls0+0(SB), $32 + +TEXT emptyfunc(SB),0,$0 + RET + +TEXT abort(SB),7,$0 + INT $0x3 + +DATA hello+0(SB)/8, $"go386\n\z\z" +GLOBL hello+0(SB), $8 + diff --git a/src/lib/runtime/386/closure.c b/src/lib/runtime/386/closure.c new file mode 100644 index 000000000..6ccbe3b8b --- /dev/null +++ b/src/lib/runtime/386/closure.c @@ -0,0 +1,104 @@ +// 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; + int32 pcrel; + + if(siz < 0 || siz%4 != 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 = 6+5+2+1; // SUBL MOVL MOVL CLD + if(siz <= 4*4) + n += 1*siz/4; // MOVSL MOVSL... + else + n += 6+2; // MOVL REP MOVSL + n += 5; // CALL + n += 6+1; // ADDL RET + + // store args aligned after code, so gc can find them. + n += siz; + if(n%4) + n += 4 - n%4; + + p = mal(n); + *ret = p; + q = p + n - siz; + mcpy(q, (byte*)&arg0, siz); + + // SUBL $siz, SP + *p++ = 0x81; + *p++ = 0xec; + *(uint32*)p = siz; + p += 4; + + // MOVL $q, SI + *p++ = 0xbe; + *(byte**)p = q; + p += 4; + + // MOVL SP, DI + *p++ = 0x89; + *p++ = 0xe7; + + // CLD + *p++ = 0xfc; + + if(siz <= 4*4) { + for(i=0; i<siz; i+=4) { + // MOVSL + *p++ = 0xa5; + } + } else { + // MOVL $(siz/4), CX [32-bit immediate siz/4] + *p++ = 0xc7; + *p++ = 0xc1; + *(uint32*)p = siz/4; + p += 4; + + // REP; MOVSL + *p++ = 0xf3; + *p++ = 0xa5; + } + + // call fn + pcrel = fn - (p+5); + // direct call with pc-relative offset + // CALL fn + *p++ = 0xe8; + *(int32*)p = pcrel; + p += 4; + + // ADDL $siz, SP + *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/lib/runtime/386/traceback.c b/src/lib/runtime/386/traceback.c new file mode 100644 index 000000000..05724d9ac --- /dev/null +++ b/src/lib/runtime/386/traceback.c @@ -0,0 +1,148 @@ +// 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" + +// TODO(rsc): Move this into portable code, with calls to a +// machine-dependent isclosure() function. + +void +traceback(byte *pc0, byte *sp, G *g) +{ + Stktop *stk; + uintptr pc; + int32 i, n; + Func *f; + byte *p; + + pc = (uintptr)pc0; + + // If the PC is zero, it's likely a nil function call. + // Start in the caller's frame. + if(pc == 0) { + pc = *(uintptr*)sp; + sp += sizeof(uintptr); + } + + stk = (Stktop*)g->stackbase; + for(n=0; n<100; n++) { + while(pc == (uintptr)retfromnewstack) { + // pop to earlier stack block + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *(uintptr*)(sp+sizeof(uintptr)); + sp += 2*sizeof(uintptr); // 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; + // ADDL $xxx, SP; RET + if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { + sp += *(uint32*)(p+2) + 8; + pc = *(uintptr*)(sp - 8); + if(pc <= 0x1000) + return; + continue; + } + printf("%p unknown pc\n", pc); + return; + } + if(f->frame < sizeof(uintptr)) // assembly funcs say 0 but lie + sp += sizeof(uintptr); + 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("+%p", (uintptr)(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 = *(uintptr*)(sp-sizeof(uintptr)); + if(pc <= 0x1000) + return; + } + prints("...\n"); +} + +// func caller(n int) (pc uintptr, file string, line int, ok bool) +void +runtime·Caller(int32 n, uintptr retpc, String retfile, int32 retline, bool retbool) +{ + uintptr pc; + byte *sp; + byte *p; + Stktop *stk; + Func *f; + + // our caller's pc, sp. + sp = (byte*)&n; + pc = *((uintptr*)sp - 1); + 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 == (uintptr)retfromnewstack) { + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *((uintptr*)sp + 1); + sp += 2*sizeof(uintptr); + } + + if(f->frame < sizeof(uintptr)) // assembly functions lie + sp += sizeof(uintptr); + else + sp += f->frame; + + loop: + pc = *((uintptr*)sp - 1); + if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { + // dangerous, but let's try this. + // see if it is a closure. + p = (byte*)pc; + // ADDL $xxx, SP; RET + if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { + sp += *(uint32*)(p+2) + sizeof(uintptr); + goto loop; + } + goto error; + } + } + + retpc = pc; + retfile = f->src; + retline = funcline(f, pc-1); + retbool = true; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); +} + diff --git a/src/lib/runtime/386/vlop.s b/src/lib/runtime/386/vlop.s new file mode 100755 index 000000000..803276ce2 --- /dev/null +++ b/src/lib/runtime/386/vlop.s @@ -0,0 +1,48 @@ +// Inferno's libkern/vlop-386.s +// http://code.google.com/p/inferno-os/source/browse/libkern/vlop-386.s +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. +// Portions Copyright 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* + * C runtime for 64-bit divide. + */ + +TEXT _mul64by32(SB), 7, $0 + MOVL r+0(FP), CX + MOVL a+4(FP), AX + MULL b+12(FP) + MOVL AX, 0(CX) + MOVL DX, BX + MOVL a+8(FP), AX + MULL b+12(FP) + ADDL AX, BX + MOVL BX, 4(CX) + RET + +TEXT _div64by32(SB), 7, $0 + MOVL r+12(FP), CX + MOVL a+0(FP), AX + MOVL a+4(FP), DX + DIVL b+8(FP) + MOVL DX, 0(CX) + RET diff --git a/src/lib/runtime/386/vlrt.c b/src/lib/runtime/386/vlrt.c new file mode 100755 index 000000000..093cca70d --- /dev/null +++ b/src/lib/runtime/386/vlrt.c @@ -0,0 +1,815 @@ +// Inferno's libkern/vlrt-386.c +// http://code.google.com/p/inferno-os/source/browse/libkern/vlrt-386.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. +// Portions Copyright 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* + * C runtime for 64-bit divide, others. + * + * TODO(rsc): The simple functions are dregs--8c knows how + * to generate the code directly now. Find and remove. + */ + +typedef unsigned long ulong; +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef signed char schar; + +#define SIGN(n) (1UL<<(n-1)) + +typedef struct Vlong Vlong; +struct Vlong +{ + union + { + long long v; + struct + { + ulong lo; + ulong hi; + }; + struct + { + ushort lols; + ushort loms; + ushort hils; + ushort hims; + }; + }; +}; + +void abort(void); + +void +_d2v(Vlong *y, double d) +{ + union { double d; struct Vlong; } x; + ulong xhi, xlo, ylo, yhi; + int sh; + + x.d = d; + + xhi = (x.hi & 0xfffff) | 0x100000; + xlo = x.lo; + sh = 1075 - ((x.hi >> 20) & 0x7ff); + + ylo = 0; + yhi = 0; + if(sh >= 0) { + /* v = (hi||lo) >> sh */ + if(sh < 32) { + if(sh == 0) { + ylo = xlo; + yhi = xhi; + } else { + ylo = (xlo >> sh) | (xhi << (32-sh)); + yhi = xhi >> sh; + } + } else { + if(sh == 32) { + ylo = xhi; + } else + if(sh < 64) { + ylo = xhi >> (sh-32); + } + } + } else { + /* v = (hi||lo) << -sh */ + sh = -sh; + if(sh <= 10) { + ylo = xlo << sh; + yhi = (xhi << sh) | (xlo >> (32-sh)); + } else { + /* overflow */ + yhi = d; /* causes something awful */ + } + } + if(x.hi & SIGN(32)) { + if(ylo != 0) { + ylo = -ylo; + yhi = ~yhi; + } else + yhi = -yhi; + } + + y->hi = yhi; + y->lo = ylo; +} + +void +_f2v(Vlong *y, float f) +{ + + _d2v(y, f); +} + +double +_v2d(Vlong x) +{ + if(x.hi & SIGN(32)) { + if(x.lo) { + x.lo = -x.lo; + x.hi = ~x.hi; + } else + x.hi = -x.hi; + return -((long)x.hi*4294967296. + x.lo); + } + return (long)x.hi*4294967296. + x.lo; +} + +float +_v2f(Vlong x) +{ + return _v2d(x); +} + +ulong _div64by32(Vlong, ulong, ulong*); +void _mul64by32(Vlong*, Vlong, ulong); + +static void +slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r) +{ + ulong numlo, numhi, denhi, denlo, quohi, quolo, t; + int i; + + numhi = num.hi; + numlo = num.lo; + denhi = den.hi; + denlo = den.lo; + + /* + * get a divide by zero + */ + if(denlo==0 && denhi==0) { + numlo = numlo / denlo; + } + + /* + * set up the divisor and find the number of iterations needed + */ + if(numhi >= SIGN(32)) { + quohi = SIGN(32); + quolo = 0; + } else { + quohi = numhi; + quolo = numlo; + } + i = 0; + while(denhi < quohi || (denhi == quohi && denlo < quolo)) { + denhi = (denhi<<1) | (denlo>>31); + denlo <<= 1; + i++; + } + + quohi = 0; + quolo = 0; + for(; i >= 0; i--) { + quohi = (quohi<<1) | (quolo>>31); + quolo <<= 1; + if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { + t = numlo; + numlo -= denlo; + if(numlo > t) + numhi--; + numhi -= denhi; + quolo |= 1; + } + denlo = (denlo>>1) | (denhi<<31); + denhi >>= 1; + } + + if(q) { + q->lo = quolo; + q->hi = quohi; + } + if(r) { + r->lo = numlo; + r->hi = numhi; + } +} + +static void +dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp) +{ + ulong n; + Vlong x, q, r; + + if(den.hi > num.hi || (den.hi == num.hi && den.lo > num.lo)){ + if(qp) { + qp->hi = 0; + qp->lo = 0; + } + if(rp) { + rp->hi = num.hi; + rp->lo = num.lo; + } + return; + } + + if(den.hi != 0){ + q.hi = 0; + n = num.hi/den.hi; + _mul64by32(&x, den, n); + if(x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo)) + slowdodiv(num, den, &q, &r); + else { + q.lo = n; + r.v = num.v - x.v; + } + } else { + if(num.hi >= den.lo){ + q.hi = n = num.hi/den.lo; + num.hi -= den.lo*n; + } else { + q.hi = 0; + } + q.lo = _div64by32(num, den.lo, &r.lo); + r.hi = 0; + } + if(qp) { + qp->lo = q.lo; + qp->hi = q.hi; + } + if(rp) { + rp->lo = r.lo; + rp->hi = r.hi; + } +} + +void +_divvu(Vlong *q, Vlong n, Vlong d) +{ + + if(n.hi == 0 && d.hi == 0) { + q->hi = 0; + q->lo = n.lo / d.lo; + return; + } + dodiv(n, d, q, 0); +} + +void +sys·uint64div(Vlong n, Vlong d, Vlong q) +{ + _divvu(&q, n, d); +} + +void +_modvu(Vlong *r, Vlong n, Vlong d) +{ + + if(n.hi == 0 && d.hi == 0) { + r->hi = 0; + r->lo = n.lo % d.lo; + return; + } + dodiv(n, d, 0, r); +} + +void +sys·uint64mod(Vlong n, Vlong d, Vlong q) +{ + _modvu(&q, n, d); +} + +static void +vneg(Vlong *v) +{ + + if(v->lo == 0) { + v->hi = -v->hi; + return; + } + v->lo = -v->lo; + v->hi = ~v->hi; +} + +void +_divv(Vlong *q, Vlong n, Vlong d) +{ + long nneg, dneg; + + if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { + if((long)n.lo == -0x80000000 && (long)d.lo == -1) { + // special case: 32-bit -0x80000000 / -1 causes divide error, + // but it's okay in this 64-bit context. + q->lo = 0x80000000; + q->hi = 0; + return; + } + q->lo = (long)n.lo / (long)d.lo; + q->hi = ((long)q->lo) >> 31; + return; + } + nneg = n.hi >> 31; + if(nneg) + vneg(&n); + dneg = d.hi >> 31; + if(dneg) + vneg(&d); + dodiv(n, d, q, 0); + if(nneg != dneg) + vneg(q); +} + +void +sys·int64div(Vlong n, Vlong d, Vlong q) +{ + _divv(&q, n, d); +} + +void +_modv(Vlong *r, Vlong n, Vlong d) +{ + long nneg, dneg; + + if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { + if((long)n.lo == -0x80000000 && (long)d.lo == -1) { + // special case: 32-bit -0x80000000 % -1 causes divide error, + // but it's okay in this 64-bit context. + r->lo = 0; + r->hi = 0; + return; + } + r->lo = (long)n.lo % (long)d.lo; + r->hi = ((long)r->lo) >> 31; + return; + } + nneg = n.hi >> 31; + if(nneg) + vneg(&n); + dneg = d.hi >> 31; + if(dneg) + vneg(&d); + dodiv(n, d, 0, r); + if(nneg) + vneg(r); +} + +void +sys·int64mod(Vlong n, Vlong d, Vlong q) +{ + _modv(&q, n, d); +} + +void +_rshav(Vlong *r, Vlong a, int b) +{ + long t; + + t = a.hi; + if(b >= 32) { + r->hi = t>>31; + if(b >= 64) { + /* this is illegal re C standard */ + r->lo = t>>31; + return; + } + r->lo = t >> (b-32); + return; + } + if(b <= 0) { + r->hi = t; + r->lo = a.lo; + return; + } + r->hi = t >> b; + r->lo = (t << (32-b)) | (a.lo >> b); +} + +void +_rshlv(Vlong *r, Vlong a, int b) +{ + ulong t; + + t = a.hi; + if(b >= 32) { + r->hi = 0; + if(b >= 64) { + /* this is illegal re C standard */ + r->lo = 0; + return; + } + r->lo = t >> (b-32); + return; + } + if(b <= 0) { + r->hi = t; + r->lo = a.lo; + return; + } + r->hi = t >> b; + r->lo = (t << (32-b)) | (a.lo >> b); +} + +void +_lshv(Vlong *r, Vlong a, int b) +{ + ulong t; + + t = a.lo; + if(b >= 32) { + r->lo = 0; + if(b >= 64) { + /* this is illegal re C standard */ + r->hi = 0; + return; + } + r->hi = t << (b-32); + return; + } + if(b <= 0) { + r->lo = t; + r->hi = a.hi; + return; + } + r->lo = t << b; + r->hi = (t >> (32-b)) | (a.hi << b); +} + +void +_andv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi & b.hi; + r->lo = a.lo & b.lo; +} + +void +_orv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi | b.hi; + r->lo = a.lo | b.lo; +} + +void +_xorv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi ^ b.hi; + r->lo = a.lo ^ b.lo; +} + +void +_vpp(Vlong *l, Vlong *r) +{ + + l->hi = r->hi; + l->lo = r->lo; + r->lo++; + if(r->lo == 0) + r->hi++; +} + +void +_vmm(Vlong *l, Vlong *r) +{ + + l->hi = r->hi; + l->lo = r->lo; + if(r->lo == 0) + r->hi--; + r->lo--; +} + +void +_ppv(Vlong *l, Vlong *r) +{ + + r->lo++; + if(r->lo == 0) + r->hi++; + l->hi = r->hi; + l->lo = r->lo; +} + +void +_mmv(Vlong *l, Vlong *r) +{ + + if(r->lo == 0) + r->hi--; + r->lo--; + l->hi = r->hi; + l->lo = r->lo; +} + +void +_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) +{ + Vlong t, u; + + u.lo = 0; + u.hi = 0; + switch(type) { + default: + abort(); + break; + + case 1: /* schar */ + t.lo = *(schar*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(schar*)lv = u.lo; + break; + + case 2: /* uchar */ + t.lo = *(uchar*)lv; + t.hi = 0; + fn(&u, t, rv); + *(uchar*)lv = u.lo; + break; + + case 3: /* short */ + t.lo = *(short*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(short*)lv = u.lo; + break; + + case 4: /* ushort */ + t.lo = *(ushort*)lv; + t.hi = 0; + fn(&u, t, rv); + *(ushort*)lv = u.lo; + break; + + case 9: /* int */ + t.lo = *(int*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(int*)lv = u.lo; + break; + + case 10: /* uint */ + t.lo = *(uint*)lv; + t.hi = 0; + fn(&u, t, rv); + *(uint*)lv = u.lo; + break; + + case 5: /* long */ + t.lo = *(long*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(long*)lv = u.lo; + break; + + case 6: /* ulong */ + t.lo = *(ulong*)lv; + t.hi = 0; + fn(&u, t, rv); + *(ulong*)lv = u.lo; + break; + + case 7: /* vlong */ + case 8: /* uvlong */ + fn(&u, *(Vlong*)lv, rv); + *(Vlong*)lv = u; + break; + } + *ret = u; +} + +void +_p2v(Vlong *ret, void *p) +{ + long t; + + t = (ulong)p; + ret->lo = t; + ret->hi = 0; +} + +void +_sl2v(Vlong *ret, long sl) +{ + long t; + + t = sl; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_ul2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul; + ret->lo = t; + ret->hi = 0; +} + +void +_si2v(Vlong *ret, int si) +{ + long t; + + t = si; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_ui2v(Vlong *ret, uint ui) +{ + long t; + + t = ui; + ret->lo = t; + ret->hi = 0; +} + +void +_sh2v(Vlong *ret, long sh) +{ + long t; + + t = (sh << 16) >> 16; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_uh2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul & 0xffff; + ret->lo = t; + ret->hi = 0; +} + +void +_sc2v(Vlong *ret, long uc) +{ + long t; + + t = (uc << 24) >> 24; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_uc2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul & 0xff; + ret->lo = t; + ret->hi = 0; +} + +long +_v2sc(Vlong rv) +{ + long t; + + t = rv.lo & 0xff; + return (t << 24) >> 24; +} + +long +_v2uc(Vlong rv) +{ + + return rv.lo & 0xff; +} + +long +_v2sh(Vlong rv) +{ + long t; + + t = rv.lo & 0xffff; + return (t << 16) >> 16; +} + +long +_v2uh(Vlong rv) +{ + + return rv.lo & 0xffff; +} + +long +_v2sl(Vlong rv) +{ + + return rv.lo; +} + +long +_v2ul(Vlong rv) +{ + + return rv.lo; +} + +long +_v2si(Vlong rv) +{ + + return rv.lo; +} + +long +_v2ui(Vlong rv) +{ + + return rv.lo; +} + +int +_testv(Vlong rv) +{ + return rv.lo || rv.hi; +} + +int +_eqv(Vlong lv, Vlong rv) +{ + return lv.lo == rv.lo && lv.hi == rv.hi; +} + +int +_nev(Vlong lv, Vlong rv) +{ + return lv.lo != rv.lo || lv.hi != rv.hi; +} + +int +_ltv(Vlong lv, Vlong rv) +{ + return (long)lv.hi < (long)rv.hi || + (lv.hi == rv.hi && lv.lo < rv.lo); +} + +int +_lev(Vlong lv, Vlong rv) +{ + return (long)lv.hi < (long)rv.hi || + (lv.hi == rv.hi && lv.lo <= rv.lo); +} + +int +_gtv(Vlong lv, Vlong rv) +{ + return (long)lv.hi > (long)rv.hi || + (lv.hi == rv.hi && lv.lo > rv.lo); +} + +int +_gev(Vlong lv, Vlong rv) +{ + return (long)lv.hi > (long)rv.hi || + (lv.hi == rv.hi && lv.lo >= rv.lo); +} + +int +_lov(Vlong lv, Vlong rv) +{ + return lv.hi < rv.hi || + (lv.hi == rv.hi && lv.lo < rv.lo); +} + +int +_lsv(Vlong lv, Vlong rv) +{ + return lv.hi < rv.hi || + (lv.hi == rv.hi && lv.lo <= rv.lo); +} + +int +_hiv(Vlong lv, Vlong rv) +{ + return lv.hi > rv.hi || + (lv.hi == rv.hi && lv.lo > rv.lo); +} + +int +_hsv(Vlong lv, Vlong rv) +{ + return lv.hi > rv.hi || + (lv.hi == rv.hi && lv.lo >= rv.lo); +} diff --git a/src/lib/runtime/amd64/asm.s b/src/lib/runtime/amd64/asm.s new file mode 100644 index 000000000..6fc01bbc9 --- /dev/null +++ b/src/lib/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/lib/runtime/amd64/closure.c b/src/lib/runtime/amd64/closure.c new file mode 100644 index 000000000..5717d3c5e --- /dev/null +++ b/src/lib/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/lib/runtime/amd64/traceback.c b/src/lib/runtime/amd64/traceback.c new file mode 100644 index 000000000..16d7bed72 --- /dev/null +++ b/src/lib/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); +} + + diff --git a/src/lib/runtime/arm/asm.s b/src/lib/runtime/arm/asm.s new file mode 100644 index 000000000..232ab4ddf --- /dev/null +++ b/src/lib/runtime/arm/asm.s @@ -0,0 +1,83 @@ +// 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_arm(SB),7,$0 + // copy arguments forward on an even stack + // MOVW $0(SP), R0 + // MOVL 0(SP), R1 // argc +// LEAL 4(SP), R1 // argv +// SUBL $128, SP // plenty of scratch +// ANDL $~7, SP +// MOVL AX, 120(SP) // save argc, argv away +// MOVL BX, 124(SP) + + +// // write "go386\n" +// PUSHL $6 +// PUSHL $hello(SB) +// PUSHL $1 +// CALL sys·write(SB) +// POPL AX +// POPL AX +// POPL AX + + +// CALL ldt0setup(SB) + + // set up %fs to refer to that ldt entry +// MOVL $(7*8+7), AX +// MOVW AX, FS + +// // store through it, to make sure it works +// MOVL $0x123, 0(FS) +// MOVL tls0(SB), AX +// CMPL AX, $0x123 +// JEQ ok +// MOVL AX, 0 +// ok: + +// // set up m and g "registers" +// // g is 0(FS), m is 4(FS) +// LEAL g0(SB), CX +// MOVL CX, 0(FS) +// LEAL m0(SB), AX +// MOVL AX, 4(FS) + +// // save m->g0 = g0 +// MOVL CX, 0(AX) + +// // create istack out of the OS stack +// LEAL (-8192+104)(SP), AX // TODO: 104? +// MOVL AX, 0(CX) // 8(g) is stack limit (w 104b guard) +// MOVL SP, 4(CX) // 12(g) is base +// CALL emptyfunc(SB) // fault if stack check is wrong + +// // convention is D is always cleared +// CLD + +// CALL check(SB) + +// // saved argc, argv +// MOVL 120(SP), AX +// MOVL AX, 0(SP) +// MOVL 124(SP), AX +// MOVL AX, 4(SP) +// CALL args(SB) +// CALL osinit(SB) +// CALL schedinit(SB) + +// // create a new goroutine to start program +// PUSHL $mainstart(SB) // entry +// PUSHL $8 // arg size +// CALL sys·newproc(SB) +// POPL AX +// POPL AX + +// // start this M +// CALL mstart(SB) + + BL main�main(SB) + MOVW $99, R0 + SWI $0x00900001 + diff --git a/src/lib/runtime/arm/closure.c b/src/lib/runtime/arm/closure.c new file mode 100644 index 000000000..024018d5a --- /dev/null +++ b/src/lib/runtime/arm/closure.c @@ -0,0 +1,4 @@ +// 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. + diff --git a/src/lib/runtime/arm/traceback.s b/src/lib/runtime/arm/traceback.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/lib/runtime/arm/traceback.s diff --git a/src/lib/runtime/array.c b/src/lib/runtime/array.c new file mode 100644 index 000000000..bbd57b03e --- /dev/null +++ b/src/lib/runtime/array.c @@ -0,0 +1,175 @@ +// 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" + +static int32 debug = 0; + +// newarray(nel int, cap int, width int) (ary []any); +void +sys·newarray(uint32 nel, uint32 cap, uint32 width, Array ret) +{ + uint64 size; + + if(cap < nel) + cap = nel; + size = cap*width; + + ret.nel = nel; + ret.cap = cap; + ret.array = mal(size); + + FLUSH(&ret); + + if(debug) { + prints("newarray: nel="); + sys·printint(nel); + prints("; cap="); + sys·printint(cap); + prints("; width="); + sys·printint(width); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +static void +throwslice(uint32 lb, uint32 hb, uint32 n) +{ + prints("slice["); + sys·printint(lb); + prints(":"); + sys·printint(hb); + prints("] of ["); + sys·printint(n); + prints("] array\n"); + throw("array slice"); +} + +// arraysliced(old []any, lb int, hb int, width int) (ary []any); +void +sys·arraysliced(Array old, uint32 lb, uint32 hb, uint32 width, Array ret) +{ + + if(hb > old.cap || lb > hb) { + if(debug) { + prints("sys·arraysliced: old="); + sys·printarray(old); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("\n"); + + prints("oldarray: nel="); + sys·printint(old.nel); + prints("; cap="); + sys·printint(old.cap); + prints("\n"); + } + throwslice(lb, hb, old.cap); + } + + // new array is inside old array + ret.nel = hb-lb; + ret.cap = old.cap - lb; + ret.array = old.array + lb*width; + + FLUSH(&ret); + + if(debug) { + prints("sys·arraysliced: old="); + sys·printarray(old); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +// arrayslices(old *any, nel int, lb int, hb int, width int) (ary []any); +void +sys·arrayslices(byte* old, uint32 nel, uint32 lb, uint32 hb, uint32 width, Array ret) +{ + + if(hb > nel || lb > hb) { + if(debug) { + prints("sys·arrayslices: old="); + sys·printpointer(old); + prints("; nel="); + sys·printint(nel); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("\n"); + } + throwslice(lb, hb, nel); + } + + // new array is inside old array + ret.nel = hb-lb; + ret.cap = nel-lb; + ret.array = old + lb*width; + + FLUSH(&ret); + + if(debug) { + prints("sys·arrayslices: old="); + sys·printpointer(old); + prints("; nel="); + sys·printint(nel); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +// arrays2d(old *any, nel int) (ary []any) +void +sys·arrays2d(byte* old, uint32 nel, Array ret) +{ + + // new dope to old array + ret.nel = nel; + ret.cap = nel; + ret.array = old; + + FLUSH(&ret); + + if(debug) { + prints("sys·arrays2d: old="); + sys·printpointer(old); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +void +sys·printarray(Array a) +{ + prints("["); + sys·printint(a.nel); + prints("/"); + sys·printint(a.cap); + prints("]"); + sys·printpointer(a.array); +} diff --git a/src/lib/runtime/cgo2c.c b/src/lib/runtime/cgo2c.c new file mode 100644 index 000000000..3905f7e6d --- /dev/null +++ b/src/lib/runtime/cgo2c.c @@ -0,0 +1,602 @@ +// 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. + +/* Translate a .cgo file into a .c file. A .cgo file is a combination + of a limited form of Go with C. */ + +/* + package PACKAGENAME + {# line} + func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{ + C code with proper brace nesting + \} +*/ + +/* We generate C code which implements the function such that it can + be called from Go and executes the C code. */ + +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* Whether we're emitting for gcc */ +static int gcc; + +/* File and line number */ +static const char *file; +static unsigned int lineno; + +/* List of names and types. */ +struct params { + struct params *next; + char *name; + char *type; +}; + +/* Unexpected EOF. */ +static void +bad_eof(void) +{ + fprintf(stderr, "%s:%u: unexpected EOF\n", file, lineno); + exit(1); +} + +/* Out of memory. */ +static void +bad_mem(void) +{ + fprintf(stderr, "%s:%u: out of memory\n", file, lineno); + exit(1); +} + +/* Allocate memory without fail. */ +static void * +xmalloc(unsigned int size) +{ + void *ret = malloc(size); + if (ret == NULL) + bad_mem(); + return ret; +} + +/* Reallocate memory without fail. */ +static void* +xrealloc(void *buf, unsigned int size) +{ + void *ret = realloc(buf, size); + if (ret == NULL) + bad_mem(); + return ret; +} + +/* Free a list of parameters. */ +static void +free_params(struct params *p) +{ + while (p != NULL) { + struct params *next; + + next = p->next; + free(p->name); + free(p->type); + free(p); + p = next; + } +} + +/* Read a character, tracking lineno. */ +static int +getchar_update_lineno(void) +{ + int c; + + c = getchar(); + if (c == '\n') + ++lineno; + return c; +} + +/* Read a character, giving an error on EOF, tracking lineno. */ +static int +getchar_no_eof(void) +{ + int c; + + c = getchar_update_lineno(); + if (c == EOF) + bad_eof(); + return c; +} + +/* Read a character, skipping comments. */ +static int +getchar_skipping_comments(void) +{ + int c; + + while (1) { + c = getchar_update_lineno(); + if (c != '/') + return c; + + c = getchar(); + if (c == '/') { + do { + c = getchar_update_lineno(); + } while (c != EOF && c != '\n'); + return c; + } else if (c == '*') { + while (1) { + c = getchar_update_lineno(); + if (c == EOF) + return EOF; + if (c == '*') { + do { + c = getchar_update_lineno(); + } while (c == '*'); + if (c == '/') + break; + } + } + } else { + ungetc(c, stdin); + return '/'; + } + } +} + +/* Read and return a token. Tokens are delimited by whitespace or by + [(),{}]. The latter are all returned as single characters. */ +static char * +read_token(void) +{ + int c; + char *buf; + unsigned int alc, off; + const char* delims = "(),{}"; + + while (1) { + c = getchar_skipping_comments(); + if (c == EOF) + return NULL; + if (!isspace(c)) + break; + } + alc = 16; + buf = xmalloc(alc + 1); + off = 0; + if (strchr(delims, c) != NULL) { + buf[off] = c; + ++off; + } else { + while (1) { + if (off >= alc) { + alc *= 2; + buf = xrealloc(buf, alc + 1); + } + buf[off] = c; + ++off; + c = getchar_skipping_comments(); + if (c == EOF) + break; + if (isspace(c) || strchr(delims, c) != NULL) { + ungetc(c, stdin); + break; + } + } + } + buf[off] = '\0'; + return buf; +} + +/* Read a token, giving an error on EOF. */ +static char * +read_token_no_eof(void) +{ + char *token = read_token(); + if (token == NULL) + bad_eof(); + return token; +} + +/* Read the package clause, and return the package name. */ +static char * +read_package(void) +{ + char *token; + + token = read_token_no_eof(); + if (strcmp(token, "package") != 0) { + fprintf(stderr, + "%s:%u: expected \"package\", got \"%s\"\n", + file, lineno, token); + exit(1); + } + return read_token_no_eof(); +} + +/* Read and copy preprocessor lines. */ +static void +read_preprocessor_lines(void) +{ + while (1) { + int c; + + do { + c = getchar_skipping_comments(); + } while (isspace(c)); + if (c != '#') { + ungetc(c, stdin); + return; + } + putchar(c); + do { + c = getchar_update_lineno(); + putchar(c); + } while (c != '\n'); + } +} + +/* Read a type in Go syntax and return a type in C syntax. We only + permit basic types and pointers. */ +static char * +read_type(void) +{ + char *p, *op, *q; + int pointer_count; + unsigned int len; + + p = read_token_no_eof(); + if (*p != '*') + return p; + op = p; + pointer_count = 0; + while (*p == '*') { + ++pointer_count; + ++p; + } + len = strlen(p); + q = xmalloc(len + pointer_count + 1); + memcpy(q, p, len); + while (pointer_count > 0) { + q[len] = '*'; + ++len; + --pointer_count; + } + q[len] = '\0'; + free(op); + return q; +} + +/* Read a list of parameters. Each parameter is a name and a type. + The list ends with a ')'. We have already read the '('. */ +static struct params * +read_params(void) +{ + char *token; + struct params *ret, **pp; + + ret = NULL; + pp = &ret; + token = read_token_no_eof(); + if (strcmp(token, ")") != 0) { + while (1) { + *pp = xmalloc(sizeof(struct params)); + (*pp)->name = token; + (*pp)->type = read_type(); + pp = &(*pp)->next; + *pp = NULL; + + token = read_token_no_eof(); + if (strcmp(token, ",") != 0) + break; + token = read_token_no_eof(); + } + } + if (strcmp(token, ")") != 0) { + fprintf(stderr, "%s:%u: expected '('\n", + file, lineno); + exit(1); + } + return ret; +} + +/* Read a function header. This reads up to and including the initial + '{' character. Returns 1 if it read a header, 0 at EOF. */ +static int +read_func_header(char **name, struct params **params, struct params **rets) +{ + char *token; + + token = read_token(); + if (token == NULL) + return 0; + if (strcmp(token, "func") != 0) { + fprintf(stderr, "%s:%u: expected \"func\"\n", + file, lineno); + exit(1); + } + *name = read_token_no_eof(); + + token = read_token(); + if (token == NULL || strcmp(token, "(") != 0) { + fprintf(stderr, "%s:%u: expected \"(\"\n", + file, lineno); + exit(1); + } + *params = read_params(); + + token = read_token(); + if (token == NULL || strcmp(token, "(") != 0) + *rets = NULL; + else { + *rets = read_params(); + token = read_token(); + } + if (token == NULL || strcmp(token, "{") != 0) { + fprintf(stderr, "%s:%u: expected \"{\"\n", + file, lineno); + exit(1); + } + return 1; +} + +/* Write out parameters. */ +static void +write_params(struct params *params, int *first) +{ + struct params *p; + + for (p = params; p != NULL; p = p->next) { + if (*first) + *first = 0; + else + printf(", "); + printf("%s %s", p->type, p->name); + } +} + +/* Write a 6g function header. */ +static void +write_6g_func_header(char *package, char *name, struct params *params, + struct params *rets) +{ + int first; + + printf("void\n%s·%s(", package, name); + first = 1; + write_params(params, &first); + write_params(rets, &first); + printf(")\n{\n"); +} + +/* Write a 6g function trailer. */ +static void +write_6g_func_trailer(struct params *rets) +{ + struct params *p; + + for (p = rets; p != NULL; p = p->next) + printf("\tFLUSH(&%s);\n", p->name); + printf("}\n"); +} + +/* Define the gcc function return type if necessary. */ +static void +define_gcc_return_type(char *package, char *name, struct params *rets) +{ + struct params *p; + + if (rets == NULL || rets->next == NULL) + return; + printf("struct %s_%s_ret {\n", package, name); + for (p = rets; p != NULL; p = p->next) + printf(" %s %s;\n", p->type, p->name); + printf("};\n"); +} + +/* Write out the gcc function return type. */ +static void +write_gcc_return_type(char *package, char *name, struct params *rets) +{ + if (rets == NULL) + printf("void"); + else if (rets->next == NULL) + printf("%s", rets->type); + else + printf("struct %s_%s_ret", package, name); +} + +/* Write out a gcc function header. */ +static void +write_gcc_func_header(char *package, char *name, struct params *params, + struct params *rets) +{ + int first; + struct params *p; + + define_gcc_return_type(package, name, rets); + write_gcc_return_type(package, name, rets); + printf(" %s_%s(", package, name); + first = 1; + write_params(params, &first); + printf(") asm (\"%s.%s\");\n", package, name); + write_gcc_return_type(package, name, rets); + printf(" %s_%s(", package, name); + first = 1; + write_params(params, &first); + printf(")\n{\n"); + for (p = rets; p != NULL; p = p->next) + printf(" %s %s;\n", p->type, p->name); +} + +/* Write out a gcc function trailer. */ +static void +write_gcc_func_trailer(char *package, char *name, struct params *rets) +{ + if (rets == NULL) + ; + else if (rets->next == NULL) + printf("return %s;\n", rets->name); + else { + struct params *p; + + printf(" {\n struct %s_%s_ret __ret;\n", package, name); + for (p = rets; p != NULL; p = p->next) + printf(" __ret.%s = %s;\n", p->name, p->name); + printf(" return __ret;\n }\n"); + } + printf("}\n"); +} + +/* Write out a function header. */ +static void +write_func_header(char *package, char *name, + struct params *params, struct params *rets) +{ + if (gcc) + write_gcc_func_header(package, name, params, rets); + else + write_6g_func_header(package, name, params, rets); + printf("#line %d \"%s\"\n", lineno, file); +} + +/* Write out a function trailer. */ +static void +write_func_trailer(char *package, char *name, + struct params *rets) +{ + if (gcc) + write_gcc_func_trailer(package, name, rets); + else + write_6g_func_trailer(rets); +} + +/* Read and write the body of the function, ending in an unnested } + (which is read but not written). */ +static void +copy_body(void) +{ + int nesting = 0; + while (1) { + int c; + + c = getchar_no_eof(); + if (c == '}' && nesting == 0) + return; + putchar(c); + switch (c) { + default: + break; + case '{': + ++nesting; + break; + case '}': + --nesting; + break; + case '/': + c = getchar_update_lineno(); + putchar(c); + if (c == '/') { + do { + c = getchar_no_eof(); + putchar(c); + } while (c != '\n'); + } else if (c == '*') { + while (1) { + c = getchar_no_eof(); + putchar(c); + if (c == '*') { + do { + c = getchar_no_eof(); + putchar(c); + } while (c == '*'); + if (c == '/') + break; + } + } + } + break; + case '"': + case '\'': + { + int delim = c; + do { + c = getchar_no_eof(); + putchar(c); + if (c == '\\') { + c = getchar_no_eof(); + putchar(c); + c = '\0'; + } + } while (c != delim); + } + break; + } + } +} + +/* Process the entire file. */ +static void +process_file(void) +{ + char *package, *name; + struct params *params, *rets; + + package = read_package(); + read_preprocessor_lines(); + while (read_func_header(&name, ¶ms, &rets)) { + write_func_header(package, name, params, rets); + copy_body(); + write_func_trailer(package, name, rets); + free(name); + free_params(params); + free_params(rets); + } + free(package); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: cgo2c [--6g | --gc] [file]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + while(argc > 1 && argv[1][0] == '-') { + if(strcmp(argv[1], "-") == 0) + break; + if(strcmp(argv[1], "--6g") == 0) + gcc = 0; + else if(strcmp(argv[1], "--gcc") == 0) + gcc = 1; + else + usage(); + argc--; + argv++; + } + + if(argc <= 1 || strcmp(argv[1], "-") == 0) { + file = "<stdin>"; + process_file(); + return 0; + } + + if(argc > 2) + usage(); + + file = argv[1]; + if(freopen(file, "r", stdin) == 0) { + fprintf(stderr, "open %s: %s\n", file, strerror(errno)); + exit(1); + } + process_file(); + return 0; +} diff --git a/src/lib/runtime/chan.c b/src/lib/runtime/chan.c new file mode 100644 index 000000000..be65bcbc1 --- /dev/null +++ b/src/lib/runtime/chan.c @@ -0,0 +1,1024 @@ +// 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" + +static int32 debug = 0; +static Lock chanlock; + +enum +{ + Wclosed = 0x0001, // writer has closed + Rclosed = 0x0002, // reader has seen close + Eincr = 0x0004, // increment errors + Emax = 0x0800, // error limit before throw +}; + +typedef struct Hchan Hchan; +typedef struct Link Link; +typedef struct WaitQ WaitQ; +typedef struct SudoG SudoG; +typedef struct Select Select; +typedef struct Scase Scase; + +struct SudoG +{ + G* g; // g and selgen constitute + int32 selgen; // a weak pointer to g + int16 offset; // offset of case number + int8 isfree; // offset of case number + SudoG* link; + byte elem[8]; // synch data element (+ more) +}; + +struct WaitQ +{ + SudoG* first; + SudoG* last; +}; + +struct Hchan +{ + uint16 elemsize; + uint16 closed; // Wclosed Rclosed errorcount + uint32 dataqsiz; // size of the circular q + uint32 qcount; // total data in the q + Alg* elemalg; // interface for element type + Link* senddataq; // pointer for sender + Link* recvdataq; // pointer for receiver + WaitQ recvq; // list of recv waiters + WaitQ sendq; // list of send waiters + SudoG* free; // freelist +}; + +struct Link +{ + Link* link; // asynch queue circular linked list + byte elem[8]; // asynch queue data element (+ more) +}; + +struct Scase +{ + Hchan* chan; // chan + byte* pc; // return pc + uint16 send; // 0-recv 1-send 2-default + uint16 so; // vararg of selected bool + union { + byte elem[8]; // element (send) + byte* elemp; // pointer to element (recv) + } u; +}; + +struct Select +{ + uint16 tcase; // total count of scase[] + uint16 ncase; // currently filled scase[] + Select* link; // for freelist + Scase* scase[1]; // one per case +}; + +static Select* selfree[20]; + +static SudoG* dequeue(WaitQ*, Hchan*); +static void enqueue(WaitQ*, SudoG*); +static SudoG* allocsg(Hchan*); +static void freesg(Hchan*, SudoG*); +static uint32 gcd(uint32, uint32); +static uint32 fastrand1(void); +static uint32 fastrand2(void); + +// newchan(elemsize uint32, elemalg uint32, hint uint32) (hchan *chan any); +void +sys·newchan(uint32 elemsize, uint32 elemalg, uint32 hint, + Hchan* ret) +{ + Hchan *c; + int32 i; + + if(elemalg >= nelem(algarray)) { + printf("chan(alg=%d)\n", elemalg); + throw("sys·newchan: unsupported elem type"); + } + + c = mal(sizeof(*c)); + + c->elemsize = elemsize; + c->elemalg = &algarray[elemalg]; + + if(hint > 0) { + Link *d, *b, *e; + + // make a circular q + b = nil; + e = nil; + for(i=0; i<hint; i++) { + d = mal(sizeof(*d) + c->elemsize - sizeof(d->elem)); + if(e == nil) + e = d; + d->link = b; + b = d; + } + e->link = b; + c->recvdataq = b; + c->senddataq = b; + c->qcount = 0; + c->dataqsiz = hint; + } + + ret = c; + FLUSH(&ret); + + if(debug) { + prints("newchan: chan="); + sys·printpointer(c); + prints("; elemsize="); + sys·printint(elemsize); + prints("; elemalg="); + sys·printint(elemalg); + prints("; dataqsiz="); + sys·printint(c->dataqsiz); + prints("\n"); + } +} + +static void +incerr(Hchan* c) +{ + c->closed += Eincr; + if(c->closed & Emax) { + unlock(&chanlock); + throw("too many operations on a closed channel"); + } +} + +/* + * generic single channel send/recv + * if the bool pointer is nil, + * then the full exchange will + * occur. if pres is not nil, + * then the protocol will not + * sleep but return if it could + * not complete + */ +void +sendchan(Hchan *c, byte *ep, bool *pres) +{ + SudoG *sg; + G* gp; + + if(debug) { + prints("chansend: chan="); + sys·printpointer(c); + prints("; elem="); + c->elemalg->print(c->elemsize, ep); + prints("\n"); + } + + lock(&chanlock); +loop: + if(c->closed & Wclosed) + goto closed; + + if(c->dataqsiz > 0) + goto asynch; + + sg = dequeue(&c->recvq, c); + if(sg != nil) { + if(ep != nil) + c->elemalg->copy(c->elemsize, sg->elem, ep); + + gp = sg->g; + gp->param = sg; + unlock(&chanlock); + ready(gp); + + if(pres != nil) + *pres = true; + return; + } + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + + sg = allocsg(c); + if(ep != nil) + c->elemalg->copy(c->elemsize, sg->elem, ep); + g->param = nil; + g->status = Gwaiting; + enqueue(&c->sendq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + freesg(c, sg); + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +asynch: + if(c->closed & Wclosed) + goto closed; + + if(c->qcount >= c->dataqsiz) { + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + sg = allocsg(c); + g->status = Gwaiting; + enqueue(&c->sendq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + goto asynch; + } + if(ep != nil) + c->elemalg->copy(c->elemsize, c->senddataq->elem, ep); + c->senddataq = c->senddataq->link; + c->qcount++; + + sg = dequeue(&c->recvq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + unlock(&chanlock); + ready(gp); + } else + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +closed: + incerr(c); + if(pres != nil) + *pres = true; + unlock(&chanlock); +} + +static void +chanrecv(Hchan* c, byte *ep, bool* pres) +{ + SudoG *sg; + G *gp; + + if(debug) { + prints("chanrecv: chan="); + sys·printpointer(c); + prints("\n"); + } + + lock(&chanlock); +loop: + if(c->dataqsiz > 0) + goto asynch; + + if(c->closed & Wclosed) + goto closed; + + sg = dequeue(&c->sendq, c); + if(sg != nil) { + c->elemalg->copy(c->elemsize, ep, sg->elem); + + gp = sg->g; + gp->param = sg; + unlock(&chanlock); + ready(gp); + + if(pres != nil) + *pres = true; + return; + } + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + + sg = allocsg(c); + g->param = nil; + g->status = Gwaiting; + enqueue(&c->recvq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + + c->elemalg->copy(c->elemsize, ep, sg->elem); + freesg(c, sg); + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +asynch: + if(c->qcount <= 0) { + if(c->closed & Wclosed) + goto closed; + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + sg = allocsg(c); + g->status = Gwaiting; + enqueue(&c->recvq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + goto asynch; + } + c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem); + c->recvdataq = c->recvdataq->link; + c->qcount--; + sg = dequeue(&c->sendq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + unlock(&chanlock); + ready(gp); + if(pres != nil) + *pres = true; + return; + } + + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +closed: + c->elemalg->copy(c->elemsize, ep, nil); + c->closed |= Rclosed; + incerr(c); + if(pres != nil) + *pres = true; + unlock(&chanlock); +} + +// chansend1(hchan *chan any, elem any); +void +sys·chansend1(Hchan* c, ...) +{ + int32 o; + byte *ae; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + sendchan(c, ae, nil); +} + +// chansend2(hchan *chan any, elem any) (pres bool); +void +sys·chansend2(Hchan* c, ...) +{ + int32 o; + byte *ae, *ap; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + o = rnd(o+c->elemsize, 1); + ap = (byte*)&c + o; + + sendchan(c, ae, ap); +} + +// chanrecv1(hchan *chan any) (elem any); +void +sys·chanrecv1(Hchan* c, ...) +{ + int32 o; + byte *ae; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + + chanrecv(c, ae, nil); +} + +// chanrecv2(hchan *chan any) (elem any, pres bool); +void +sys·chanrecv2(Hchan* c, ...) +{ + int32 o; + byte *ae, *ap; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + o = rnd(o+c->elemsize, 1); + ap = (byte*)&c + o; + + chanrecv(c, ae, ap); +} + +// chanrecv3(hchan *chan any, elem *any) (pres bool); +void +sys·chanrecv3(Hchan* c, byte* ep, byte pres) +{ + chanrecv(c, ep, &pres); +} + +// newselect(size uint32) (sel *byte); +void +sys·newselect(int32 size, Select *sel) +{ + int32 n; + + n = 0; + if(size > 1) + n = size-1; + + lock(&chanlock); + sel = nil; + if(size >= 1 && size < nelem(selfree)) { + sel = selfree[size]; + if(sel != nil) + selfree[size] = sel->link; + } + unlock(&chanlock); + if(sel == nil) + sel = mal(sizeof(*sel) + n*sizeof(sel->scase[0])); + + sel->tcase = size; + sel->ncase = 0; + FLUSH(&sel); + if(debug) { + prints("newselect s="); + sys·printpointer(sel); + prints(" size="); + sys·printint(size); + prints("\n"); + } +} + +// selectsend(sel *byte, hchan *chan any, elem any) (selected bool); +void +sys·selectsend(Select *sel, Hchan *c, ...) +{ + int32 i, eo; + Scase *cas; + byte *ae; + + // nil cases do not compete + if(c == nil) + return; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectsend: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas + c->elemsize - sizeof(cas->u.elem)); + sel->scase[i] = cas; + } + + cas->pc = sys·getcallerpc(&sel); + cas->chan = c; + + eo = rnd(sizeof(sel), sizeof(c)); + eo = rnd(eo+sizeof(c), c->elemsize); + cas->so = rnd(eo+c->elemsize, 1); + cas->send = 1; + + ae = (byte*)&sel + eo; + c->elemalg->copy(c->elemsize, cas->u.elem, ae); + + if(debug) { + prints("selectsend s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" chan="); + sys·printpointer(cas->chan); + prints(" po="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + +// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); +void +sys·selectrecv(Select *sel, Hchan *c, ...) +{ + int32 i, eo; + Scase *cas; + + // nil cases do not compete + if(c == nil) + return; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectrecv: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas); + sel->scase[i] = cas; + } + cas->pc = sys·getcallerpc(&sel); + cas->chan = c; + + eo = rnd(sizeof(sel), sizeof(c)); + eo = rnd(eo+sizeof(c), sizeof(byte*)); + cas->so = rnd(eo+sizeof(byte*), 1); + cas->send = 0; + cas->u.elemp = *(byte**)((byte*)&sel + eo); + + if(debug) { + prints("selectrecv s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" chan="); + sys·printpointer(cas->chan); + prints(" so="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + + +// selectdefaul(sel *byte) (selected bool); +void +sys·selectdefault(Select *sel, ...) +{ + int32 i; + Scase *cas; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectdefault: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas); + sel->scase[i] = cas; + } + cas->pc = sys·getcallerpc(&sel); + cas->chan = nil; + + cas->so = rnd(sizeof(sel), 1); + cas->send = 2; + cas->u.elemp = nil; + + if(debug) { + prints("selectdefault s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" so="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + +// selectgo(sel *byte); +void +sys·selectgo(Select *sel) +{ + uint32 p, o, i; + Scase *cas, *dfl; + Hchan *c; + SudoG *sg; + G *gp; + byte *as; + + if(debug) { + prints("selectgo: sel="); + sys·printpointer(sel); + prints("\n"); + } + + if(sel->ncase < 2) { + if(sel->ncase < 1) + throw("selectgo: no cases"); + // make special case of one. + } + + // select a (relative) prime + for(i=0;; i++) { + p = fastrand1(); + if(gcd(p, sel->ncase) == 1) + break; + if(i > 1000) { + throw("selectgo: failed to select prime"); + } + } + + // select an initial offset + o = fastrand2(); + + p %= sel->ncase; + o %= sel->ncase; + + lock(&chanlock); + +loop: + // pass 1 - look for something already waiting + dfl = nil; + for(i=0; i<sel->ncase; i++) { + cas = sel->scase[o]; + + if(cas->send == 2) { // default + dfl = cas; + goto next1; + } + + c = cas->chan; + if(c->dataqsiz > 0) { + if(cas->send) { + if(c->closed & Wclosed) + goto sclose; + if(c->qcount < c->dataqsiz) + goto asyns; + goto next1; + } + if(c->qcount > 0) + goto asynr; + if(c->closed & Wclosed) + goto rclose; + goto next1; + } + + if(cas->send) { + if(c->closed & Wclosed) + goto sclose; + sg = dequeue(&c->recvq, c); + if(sg != nil) + goto gots; + goto next1; + } + sg = dequeue(&c->sendq, c); + if(sg != nil) + goto gotr; + if(c->closed & Wclosed) + goto rclose; + + next1: + o += p; + if(o >= sel->ncase) + o -= sel->ncase; + } + + if(dfl != nil) { + cas = dfl; + goto retc; + } + + + // pass 2 - enqueue on all chans + for(i=0; i<sel->ncase; i++) { + cas = sel->scase[o]; + c = cas->chan; + + if(c->dataqsiz > 0) { + if(cas->send) { + if(c->qcount < c->dataqsiz) { + prints("selectgo: pass 2 async send\n"); + goto asyns; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->sendq, sg); + goto next2; + } + if(c->qcount > 0) { + prints("selectgo: pass 2 async recv\n"); + goto asynr; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->recvq, sg); + goto next2; + } + + if(cas->send) { + sg = dequeue(&c->recvq, c); + if(sg != nil) { + prints("selectgo: pass 2 sync send\n"); + g->selgen++; + goto gots; + } + sg = allocsg(c); + sg->offset = o; + c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); + enqueue(&c->sendq, sg); + goto next2; + } + sg = dequeue(&c->sendq, c); + if(sg != nil) { + prints("selectgo: pass 2 sync recv\n"); + g->selgen++; + goto gotr; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->recvq, sg); + + next2: + o += p; + if(o >= sel->ncase) + o -= sel->ncase; + } + + g->param = nil; + g->status = Gwaiting; + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + + o = sg->offset; + cas = sel->scase[o]; + c = cas->chan; + + if(c->dataqsiz > 0) { +// prints("shouldnt happen\n"); + goto loop; + } + + if(debug) { + prints("wait-return: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" cas="); + sys·printpointer(cas); + prints(" send="); + sys·printint(cas->send); + prints(" o="); + sys·printint(o); + prints("\n"); + } + + if(!cas->send) { + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem); + } + + freesg(c, sg); + goto retc; + +asynr: + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, c->recvdataq->elem); + c->recvdataq = c->recvdataq->link; + c->qcount--; + sg = dequeue(&c->sendq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + ready(gp); + } + goto retc; + +asyns: + if(cas->u.elem != nil) + c->elemalg->copy(c->elemsize, c->senddataq->elem, cas->u.elem); + c->senddataq = c->senddataq->link; + c->qcount++; + sg = dequeue(&c->recvq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + ready(gp); + } + goto retc; + +gotr: + // recv path to wakeup the sender (sg) + if(debug) { + prints("gotr: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" o="); + sys·printint(o); + prints("\n"); + } + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem); + gp = sg->g; + gp->param = sg; + ready(gp); + goto retc; + +rclose: + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, nil); + c->closed |= Rclosed; + incerr(c); + goto retc; + +gots: + // send path to wakeup the receiver (sg) + if(debug) { + prints("gots: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" o="); + sys·printint(o); + prints("\n"); + } + if(c->closed & Wclosed) + goto sclose; + c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); + gp = sg->g; + gp->param = sg; + ready(gp); + goto retc; + +sclose: + incerr(c); + goto retc; + +retc: + if(sel->ncase >= 1 && sel->ncase < nelem(selfree)) { + sel->link = selfree[sel->ncase]; + selfree[sel->ncase] = sel; + } + unlock(&chanlock); + + sys·setcallerpc(&sel, cas->pc); + as = (byte*)&sel + cas->so; + *as = true; +} + +// closechan(sel *byte); +void +sys·closechan(Hchan *c) +{ + SudoG *sg; + G* gp; + + lock(&chanlock); + incerr(c); + c->closed |= Wclosed; + + // release all readers + for(;;) { + sg = dequeue(&c->recvq, c); + if(sg == nil) + break; + gp = sg->g; + gp->param = nil; + freesg(c, sg); + ready(gp); + } + + // release all writers + for(;;) { + sg = dequeue(&c->sendq, c); + if(sg == nil) + break; + gp = sg->g; + gp->param = nil; + freesg(c, sg); + ready(gp); + } + + unlock(&chanlock); +} + +// closedchan(sel *byte) bool; +void +sys·closedchan(Hchan *c, bool closed) +{ + // test Rclosed + closed = 0; + if(c->closed & Rclosed) + closed = 1; + FLUSH(&closed); +} + +static SudoG* +dequeue(WaitQ *q, Hchan *c) +{ + SudoG *sgp; + +loop: + sgp = q->first; + if(sgp == nil) + return nil; + q->first = sgp->link; + + // if sgp is stale, ignore it + if(sgp->selgen != sgp->g->selgen) { + //prints("INVALID PSEUDOG POINTER\n"); + freesg(c, sgp); + goto loop; + } + + // invalidate any others + sgp->g->selgen++; + return sgp; +} + +static void +enqueue(WaitQ *q, SudoG *sgp) +{ + sgp->link = nil; + if(q->first == nil) { + q->first = sgp; + q->last = sgp; + return; + } + q->last->link = sgp; + q->last = sgp; +} + +static SudoG* +allocsg(Hchan *c) +{ + SudoG* sg; + + sg = c->free; + if(sg != nil) { + c->free = sg->link; + } else + sg = mal(sizeof(*sg) + c->elemsize - sizeof(sg->elem)); + sg->selgen = g->selgen; + sg->g = g; + sg->offset = 0; + sg->isfree = 0; + + return sg; +} + +static void +freesg(Hchan *c, SudoG *sg) +{ + if(sg != nil) { + if(sg->isfree) + throw("chan.freesg: already free"); + sg->isfree = 1; + sg->link = c->free; + c->free = sg; + } +} + +static uint32 +gcd(uint32 u, uint32 v) +{ + for(;;) { + if(u > v) { + if(v == 0) + return u; + u = u%v; + continue; + } + if(u == 0) + return v; + v = v%u; + } +} + +static uint32 +fastrand1(void) +{ + static uint32 x = 0x49f6428aUL; + + x += x; + if(x & 0x80000000L) + x ^= 0x88888eefUL; + return x; +} + +static uint32 +fastrand2(void) +{ + static uint32 x = 0x49f6428aUL; + + x += x; + if(x & 0x80000000L) + x ^= 0xfafd871bUL; + return x; +} diff --git a/src/lib/runtime/darwin/386/defs.h b/src/lib/runtime/darwin/386/defs.h new file mode 100644 index 000000000..b66a5d8b4 --- /dev/null +++ b/src/lib/runtime/darwin/386/defs.h @@ -0,0 +1,229 @@ +// godefs -f -m32 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MACH_MSG_TYPE_MOVE_RECEIVE = 0x10, + MACH_MSG_TYPE_MOVE_SEND = 0x11, + MACH_MSG_TYPE_MOVE_SEND_ONCE = 0x12, + MACH_MSG_TYPE_COPY_SEND = 0x13, + MACH_MSG_TYPE_MAKE_SEND = 0x14, + MACH_MSG_TYPE_MAKE_SEND_ONCE = 0x15, + MACH_MSG_TYPE_COPY_RECEIVE = 0x16, + MACH_MSG_PORT_DESCRIPTOR = 0, + MACH_MSG_OOL_DESCRIPTOR = 0x1, + MACH_MSG_OOL_PORTS_DESCRIPTOR = 0x2, + MACH_MSG_OOL_VOLATILE_DESCRIPTOR = 0x3, + MACH_MSGH_BITS_COMPLEX = 0x80000000, + MACH_SEND_MSG = 0x1, + MACH_RCV_MSG = 0x2, + MACH_RCV_LARGE = 0x4, + MACH_SEND_TIMEOUT = 0x10, + MACH_SEND_INTERRUPT = 0x40, + MACH_SEND_CANCEL = 0x80, + MACH_SEND_ALWAYS = 0x10000, + MACH_SEND_TRAILER = 0x20000, + MACH_RCV_TIMEOUT = 0x100, + MACH_RCV_NOTIFY = 0x200, + MACH_RCV_INTERRUPT = 0x400, + MACH_RCV_OVERWRITE = 0x1000, + NDR_PROTOCOL_2_0 = 0, + NDR_INT_BIG_ENDIAN = 0, + NDR_INT_LITTLE_ENDIAN = 0x1, + NDR_FLOAT_IEEE = 0, + NDR_CHAR_ASCII = 0, + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + SA_USERTRAMP = 0x100, + SA_64REGSET = 0x200, +}; + +// Types +#pragma pack on + +typedef struct MachBody MachBody; +struct MachBody { + uint32 msgh_descriptor_count; +}; + +typedef struct MachHeader MachHeader; +struct MachHeader { + uint32 msgh_bits; + uint32 msgh_size; + uint32 msgh_remote_port; + uint32 msgh_local_port; + uint32 msgh_reserved; + int32 msgh_id; +}; + +typedef struct MachNDR MachNDR; +struct MachNDR { + uint8 mig_vers; + uint8 if_vers; + uint8 reserved1; + uint8 mig_encoding; + uint8 int_rep; + uint8 char_rep; + uint8 float_rep; + uint8 reserved2; +}; + +typedef struct MachPort MachPort; +struct MachPort { + uint32 name; + uint32 pad1; + uint16 pad2; + uint8 disposition; + uint8 type; +}; + +typedef struct StackT StackT; +struct StackT { + void *ss_sp; + uint32 ss_size; + int32 ss_flags; +}; + +typedef union Sighandler Sighandler; +union Sighandler { + void *__sa_handler; + void *__sa_sigaction; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + Sighandler __sigaction_u; + void *sa_tramp; + uint32 sa_mask; + int32 sa_flags; +}; + +typedef union Sigval Sigval; +union Sigval { + int32 sival_int; + void *sival_ptr; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + void *si_addr; + Sigval si_value; + int32 si_band; + uint32 __pad[7]; +}; + +typedef struct FPControl FPControl; +struct FPControl { + byte pad0[2]; +}; + +typedef struct FPStatus FPStatus; +struct FPStatus { + byte pad0[2]; +}; + +typedef struct RegMMST RegMMST; +struct RegMMST { + int8 mmst_reg[10]; + int8 mmst_rsrv[6]; +}; + +typedef struct RegXMM RegXMM; +struct RegXMM { + int8 xmm_reg[16]; +}; + +typedef struct Regs Regs; +struct Regs { + uint32 eax; + uint32 ebx; + uint32 ecx; + uint32 edx; + uint32 edi; + uint32 esi; + uint32 ebp; + uint32 esp; + uint32 ss; + uint32 eflags; + uint32 eip; + uint32 cs; + uint32 ds; + uint32 es; + uint32 fs; + uint32 gs; +}; + +typedef struct FloatState FloatState; +struct FloatState { + int32 fpu_reserved[2]; + FPControl fpu_fcw; + FPStatus fpu_fsw; + uint8 fpu_ftw; + uint8 fpu_rsrv1; + uint16 fpu_fop; + uint32 fpu_ip; + uint16 fpu_cs; + uint16 fpu_rsrv2; + uint32 fpu_dp; + uint16 fpu_ds; + uint16 fpu_rsrv3; + uint32 fpu_mxcsr; + uint32 fpu_mxcsrmask; + RegMMST fpu_stmm0; + RegMMST fpu_stmm1; + RegMMST fpu_stmm2; + RegMMST fpu_stmm3; + RegMMST fpu_stmm4; + RegMMST fpu_stmm5; + RegMMST fpu_stmm6; + RegMMST fpu_stmm7; + RegXMM fpu_xmm0; + RegXMM fpu_xmm1; + RegXMM fpu_xmm2; + RegXMM fpu_xmm3; + RegXMM fpu_xmm4; + RegXMM fpu_xmm5; + RegXMM fpu_xmm6; + RegXMM fpu_xmm7; + int8 fpu_rsrv4[224]; + int32 fpu_reserved1; +}; + +typedef struct ExceptionState ExceptionState; +struct ExceptionState { + uint32 trapno; + uint32 err; + uint32 faultvaddr; +}; + +typedef struct Mcontext Mcontext; +struct Mcontext { + ExceptionState es; + Regs ss; + FloatState fs; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + int32 uc_onstack; + uint32 uc_sigmask; + StackT uc_stack; + Ucontext *uc_link; + uint32 uc_mcsize; + Mcontext *uc_mcontext; +}; +#pragma pack off diff --git a/src/lib/runtime/darwin/386/rt0.s b/src/lib/runtime/darwin/386/rt0.s new file mode 100755 index 000000000..5b52e912c --- /dev/null +++ b/src/lib/runtime/darwin/386/rt0.s @@ -0,0 +1,8 @@ +// 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_386_darwin(SB),7,$0 + JMP _rt0_386(SB) diff --git a/src/lib/runtime/darwin/386/signal.c b/src/lib/runtime/darwin/386/signal.c new file mode 100644 index 000000000..3a63c4b38 --- /dev/null +++ b/src/lib/runtime/darwin/386/signal.c @@ -0,0 +1,103 @@ +// 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" +#include "defs.h" +#include "os.h" +#include "signals.h" + +void +dumpregs(Regs *r) +{ + printf("eax %x\n", r->eax); + printf("ebx %x\n", r->ebx); + printf("ecx %x\n", r->ecx); + printf("edx %x\n", r->edx); + printf("edi %x\n", r->edi); + printf("esi %x\n", r->esi); + printf("ebp %x\n", r->ebp); + printf("esp %x\n", r->esp); + printf("eip %x\n", r->eip); + printf("eflags %x\n", r->eflags); + printf("cs %x\n", r->cs); + printf("fs %x\n", r->fs); + printf("gs %x\n", r->gs); +} + +void +sighandler(int32 sig, Siginfo *info, void *context) +{ + Ucontext *uc; + Mcontext *mc; + Regs *r; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + if(sig < 0 || sig >= NSIG){ + printf("Signal %d\n", sig); + }else{ + printf("%s\n", sigtab[sig].name); + } + + uc = context; + mc = uc->uc_mcontext; + r = &mc->ss; + + printf("Faulting address: %p\n", info->si_addr); + printf("pc: %x\n", r->eip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)r->eip, (void*)r->esp, m->curg); + tracebackothers(m->curg); + dumpregs(r); + } + + breakpoint(); + exit(2); +} + +void +sigignore(int32, Siginfo*, void*) +{ +} + +void +signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + int32 i; + static Sigaction sa; + + sa.sa_flags |= SA_SIGINFO|SA_ONSTACK; + sa.sa_mask = 0; // 0xFFFFFFFFU; + sa.sa_tramp = sigtramp; // sigtramp's job is to call into real handler + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) { + sa.__sigaction_u.__sa_sigaction = sighandler; + } else { + sa.__sigaction_u.__sa_sigaction = sigignore; + } + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + sigaction(i, &sa, nil); + } + } +} + diff --git a/src/lib/runtime/darwin/386/sys.s b/src/lib/runtime/darwin/386/sys.s new file mode 100644 index 000000000..bbcb622d5 --- /dev/null +++ b/src/lib/runtime/darwin/386/sys.s @@ -0,0 +1,278 @@ +// 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. + +// System calls and other sys.stuff for 386, Darwin +// See http://fxr.watson.org/fxr/source/bsd/kern/syscalls.c?v=xnu-1228 +// or /usr/include/sys/syscall.h (on a Mac) for system call numbers. + +TEXT notok(SB),7,$0 + MOVL $0xf1, 0xf1 + RET + +// Exit the entire program (like C exit) +TEXT exit(SB),7,$0 + MOVL $1, AX + INT $0x80 + CALL notok(SB) + RET + +// Exit this OS thread (like pthread_exit, which eventually +// calls __bsdthread_terminate). +TEXT exit1(SB),7,$0 + MOVL $361, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +TEXT sys·write(SB),7,$0 + MOVL $4, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +TEXT sys·mmap(SB),7,$0 + MOVL $197, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +TEXT sigaction(SB),7,$0 + MOVL $46, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// Sigtramp's job is to call the actual signal handler. +// It is called with the following arguments on the stack: +// 0(FP) "return address" - ignored +// 4(FP) actual handler +// 8(FP) siginfo style - ignored +// 12(FP) signal number +// 16(FP) siginfo +// 20(FP) context +TEXT sigtramp(SB),7,$40 + MOVL 4(FS), BP // m + MOVL 28(BP), BP // m->gsignal + MOVL BP, 0(FS) // g = m->gsignal + + MOVL handler+4(FP), DI + MOVL signo+12(FP), AX + MOVL siginfo+16(FP), BX + MOVL context+20(FP), CX + + MOVL AX, 0(SP) + MOVL BX, 4(SP) + MOVL CX, 8(SP) + CALL DI + + MOVL context+20(FP), CX + MOVL style+8(FP), BX + + MOVL $0, 0(SP) // "caller PC" - ignored + MOVL CX, 4(SP) + MOVL BX, 8(SP) + MOVL $184, AX // sigreturn(ucontext, infostyle) + INT $0x80 + CALL notok(SB) + RET + +TEXT sigaltstack(SB),7,$0 + MOVL $53, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +// System call args are: func arg stack pthread flags. +TEXT bsdthread_create(SB),7,$32 + MOVL $360, AX + // 0(SP) is where the caller PC would be; kernel skips it + MOVL func+12(FP), BX + MOVL BX, 4(SP) // func + MOVL m+4(FP), BX + MOVL BX, 8(SP) // arg + MOVL stk+0(FP), BX + MOVL BX, 12(SP) // stack + MOVL g+8(FP), BX + MOVL BX, 16(SP) // pthread + MOVL $0x1000000, 20(SP) // flags = PTHREAD_START_CUSTOM + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// The thread that bsdthread_create creates starts executing here, +// because we registered this function using bsdthread_register +// at startup. +// AX = "pthread" (= g) +// BX = mach thread port +// CX = "func" (= fn) +// DX = "arg" (= m) +// DI = stack top +// SI = flags (= 0x1000000) +// SP = stack - C_32_STK_ALIGN +TEXT bsdthread_start(SB),7,$0 + // set up ldt 7+id to point at m->tls. + // m->tls is at m+40. newosproc left + // the m->id in tls[0]. + LEAL 40(DX), BP + MOVL 0(BP), DI + ADDL $7, DI // m0 is LDT#7. count up. + // setldt(tls#, &tls, sizeof tls) + PUSHAL // save registers + PUSHL $32 // sizeof tls + PUSHL BP // &tls + PUSHL DI // tls # + CALL setldt(SB) + POPL AX + POPL AX + POPL AX + POPAL + SHLL $3, DI // segment# is ldt*8 + 7. + ADDL $7, DI + MOVW DI, FS + + // Now segment is established. Initialize m, g. + MOVL AX, 0(FS) // g + MOVL DX, 4(FS) // m + MOVL BX, 20(DX) // m->procid = thread port (for debuggers) + CALL CX // fn() + CALL exit1(SB) + RET + +// void bsdthread_register(void) +// registers callbacks for threadstart (see bsdthread_create above +// and wqthread and pthsize (not used). returns 0 on success. +TEXT bsdthread_register(SB),7,$40 + MOVL $366, AX + // 0(SP) is where kernel expects caller PC; ignored + MOVL $bsdthread_start(SB), 4(SP) // threadstart + MOVL $0, 8(SP) // wqthread, not used by us + MOVL $0, 12(SP) // pthsize, not used by us + MOVL $0, 16(SP) // paranoia + MOVL $0, 20(SP) + MOVL $0, 24(SP) + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// Invoke Mach system call. +// Assumes system call number in AX, +// caller PC on stack, caller's caller PC next, +// and then the system call arguments. +// +// Can be used for BSD too, but we don't, +// because if you use this interface the BSD +// system call numbers need an extra field +// in the high 16 bits that seems to be the +// argument count in bytes but is not always. +// INT $0x80 works fine for those. +TEXT sysenter(SB),7,$0 + POPL DX + MOVL SP, CX + BYTE $0x0F; BYTE $0x34; // SYSENTER + // returns to DX with SP set to CX + +TEXT mach_msg_trap(SB),7,$0 + MOVL $-31, AX + CALL sysenter(SB) + RET + +TEXT mach_reply_port(SB),7,$0 + MOVL $-26, AX + CALL sysenter(SB) + RET + +TEXT mach_task_self(SB),7,$0 + MOVL $-28, AX + CALL sysenter(SB) + RET + +// Mach provides trap versions of the semaphore ops, +// instead of requiring the use of RPC. + +// uint32 mach_semaphore_wait(uint32) +TEXT mach_semaphore_wait(SB),7,$0 + MOVL $-36, AX + CALL sysenter(SB) + RET + +// uint32 mach_semaphore_timedwait(uint32, uint32, uint32) +TEXT mach_semaphore_timedwait(SB),7,$0 + MOVL $-38, AX + CALL sysenter(SB) + RET + +// uint32 mach_semaphore_signal(uint32) +TEXT mach_semaphore_signal(SB),7,$0 + MOVL $-33, AX + CALL sysenter(SB) + RET + +// uint32 mach_semaphore_signal_all(uint32) +TEXT mach_semaphore_signal_all(SB),7,$0 + MOVL $-34, AX + CALL sysenter(SB) + RET + +/* +descriptor entry format for system call +is the native machine format, ugly as it is: + + 2-byte limit + 3-byte base + 1-byte: 0x80=present, 0x60=dpl<<5, 0x1F=type + 1-byte: 0x80=limit is *4k, 0x40=32-bit operand size, + 0x0F=4 more bits of limit + 1 byte: 8 more bits of base + +int i386_get_ldt(int, union ldt_entry *, int); +int i386_set_ldt(int, const union ldt_entry *, int); + +*/ + +// setldt(int entry, int address, int limit) +TEXT setldt(SB),7,$32 + // set up data_desc + LEAL 16(SP), AX // struct data_desc + MOVL $0, 0(AX) + MOVL $0, 4(AX) + + MOVL address+4(FP), BX // aka base + MOVW BX, 2(AX) + SHRL $16, BX + MOVB BX, 4(AX) + SHRL $8, BX + MOVB BX, 7(AX) + + MOVL limit+8(FP), BX + MOVW BX, 0(AX) + SHRL $16, BX + ANDL $0x0F, BX + ORL $0x40, BX // 32-bit operand size + MOVB BX, 6(AX) + + MOVL $0xF2, 5(AX) // r/w data descriptor, dpl=3, present + + // call i386_set_ldt(entry, desc, 1) + MOVL entry+0(FP), BX + MOVL BX, 0(SP) + MOVL AX, 4(SP) + MOVL $1, 8(SP) + CALL i386_set_ldt(SB) + RET + +TEXT i386_set_ldt(SB),7,$0 + MOVL $5, AX + INT $0x82 // sic + JAE 2(PC) + CALL notok(SB) + RET + diff --git a/src/lib/runtime/darwin/amd64/defs.h b/src/lib/runtime/darwin/amd64/defs.h new file mode 100644 index 000000000..1076e4c10 --- /dev/null +++ b/src/lib/runtime/darwin/amd64/defs.h @@ -0,0 +1,244 @@ +// godefs -f -m64 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MACH_MSG_TYPE_MOVE_RECEIVE = 0x10, + MACH_MSG_TYPE_MOVE_SEND = 0x11, + MACH_MSG_TYPE_MOVE_SEND_ONCE = 0x12, + MACH_MSG_TYPE_COPY_SEND = 0x13, + MACH_MSG_TYPE_MAKE_SEND = 0x14, + MACH_MSG_TYPE_MAKE_SEND_ONCE = 0x15, + MACH_MSG_TYPE_COPY_RECEIVE = 0x16, + MACH_MSG_PORT_DESCRIPTOR = 0, + MACH_MSG_OOL_DESCRIPTOR = 0x1, + MACH_MSG_OOL_PORTS_DESCRIPTOR = 0x2, + MACH_MSG_OOL_VOLATILE_DESCRIPTOR = 0x3, + MACH_MSGH_BITS_COMPLEX = 0x80000000, + MACH_SEND_MSG = 0x1, + MACH_RCV_MSG = 0x2, + MACH_RCV_LARGE = 0x4, + MACH_SEND_TIMEOUT = 0x10, + MACH_SEND_INTERRUPT = 0x40, + MACH_SEND_CANCEL = 0x80, + MACH_SEND_ALWAYS = 0x10000, + MACH_SEND_TRAILER = 0x20000, + MACH_RCV_TIMEOUT = 0x100, + MACH_RCV_NOTIFY = 0x200, + MACH_RCV_INTERRUPT = 0x400, + MACH_RCV_OVERWRITE = 0x1000, + NDR_PROTOCOL_2_0 = 0, + NDR_INT_BIG_ENDIAN = 0, + NDR_INT_LITTLE_ENDIAN = 0x1, + NDR_FLOAT_IEEE = 0, + NDR_CHAR_ASCII = 0, + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + SA_USERTRAMP = 0x100, + SA_64REGSET = 0x200, +}; + +// Types +#pragma pack on + +typedef struct MachBody MachBody; +struct MachBody { + uint32 msgh_descriptor_count; +}; + +typedef struct MachHeader MachHeader; +struct MachHeader { + uint32 msgh_bits; + uint32 msgh_size; + uint32 msgh_remote_port; + uint32 msgh_local_port; + uint32 msgh_reserved; + int32 msgh_id; +}; + +typedef struct MachNDR MachNDR; +struct MachNDR { + uint8 mig_vers; + uint8 if_vers; + uint8 reserved1; + uint8 mig_encoding; + uint8 int_rep; + uint8 char_rep; + uint8 float_rep; + uint8 reserved2; +}; + +typedef struct MachPort MachPort; +struct MachPort { + uint32 name; + uint32 pad1; + uint16 pad2; + uint8 disposition; + uint8 type; +}; + +typedef struct StackT StackT; +struct StackT { + void *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte pad0[4]; +}; + +typedef union Sighandler Sighandler; +union Sighandler { + void *__sa_handler; + void *__sa_sigaction; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + Sighandler __sigaction_u; + void *sa_tramp; + uint32 sa_mask; + int32 sa_flags; +}; + +typedef union Sigval Sigval; +union Sigval { + int32 sival_int; + void *sival_ptr; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + void *si_addr; + Sigval si_value; + int64 si_band; + uint64 __pad[7]; +}; + +typedef struct FPControl FPControl; +struct FPControl { + byte pad0[2]; +}; + +typedef struct FPStatus FPStatus; +struct FPStatus { + byte pad0[2]; +}; + +typedef struct RegMMST RegMMST; +struct RegMMST { + int8 mmst_reg[10]; + int8 mmst_rsrv[6]; +}; + +typedef struct RegXMM RegXMM; +struct RegXMM { + int8 xmm_reg[16]; +}; + +typedef struct Regs Regs; +struct Regs { + uint64 rax; + uint64 rbx; + uint64 rcx; + uint64 rdx; + uint64 rdi; + uint64 rsi; + uint64 rbp; + uint64 rsp; + uint64 r8; + uint64 r9; + uint64 r10; + uint64 r11; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; + uint64 rip; + uint64 rflags; + uint64 cs; + uint64 fs; + uint64 gs; +}; + +typedef struct FloatState FloatState; +struct FloatState { + int32 fpu_reserved[2]; + FPControl fpu_fcw; + FPStatus fpu_fsw; + uint8 fpu_ftw; + uint8 fpu_rsrv1; + uint16 fpu_fop; + uint32 fpu_ip; + uint16 fpu_cs; + uint16 fpu_rsrv2; + uint32 fpu_dp; + uint16 fpu_ds; + uint16 fpu_rsrv3; + uint32 fpu_mxcsr; + uint32 fpu_mxcsrmask; + RegMMST fpu_stmm0; + RegMMST fpu_stmm1; + RegMMST fpu_stmm2; + RegMMST fpu_stmm3; + RegMMST fpu_stmm4; + RegMMST fpu_stmm5; + RegMMST fpu_stmm6; + RegMMST fpu_stmm7; + RegXMM fpu_xmm0; + RegXMM fpu_xmm1; + RegXMM fpu_xmm2; + RegXMM fpu_xmm3; + RegXMM fpu_xmm4; + RegXMM fpu_xmm5; + RegXMM fpu_xmm6; + RegXMM fpu_xmm7; + RegXMM fpu_xmm8; + RegXMM fpu_xmm9; + RegXMM fpu_xmm10; + RegXMM fpu_xmm11; + RegXMM fpu_xmm12; + RegXMM fpu_xmm13; + RegXMM fpu_xmm14; + RegXMM fpu_xmm15; + int8 fpu_rsrv4[96]; + int32 fpu_reserved1; +}; + +typedef struct ExceptionState ExceptionState; +struct ExceptionState { + uint32 trapno; + uint32 err; + uint64 faultvaddr; +}; + +typedef struct Mcontext Mcontext; +struct Mcontext { + ExceptionState es; + Regs ss; + FloatState fs; + byte pad0[4]; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + int32 uc_onstack; + uint32 uc_sigmask; + StackT uc_stack; + Ucontext *uc_link; + uint64 uc_mcsize; + Mcontext *uc_mcontext; +}; +#pragma pack off diff --git a/src/lib/runtime/darwin/amd64/rt0.s b/src/lib/runtime/darwin/amd64/rt0.s new file mode 100644 index 000000000..0a0011781 --- /dev/null +++ b/src/lib/runtime/darwin/amd64/rt0.s @@ -0,0 +1,9 @@ +// 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_amd64_darwin(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/lib/runtime/darwin/amd64/signal.c b/src/lib/runtime/darwin/amd64/signal.c new file mode 100644 index 000000000..45e5e8d47 --- /dev/null +++ b/src/lib/runtime/darwin/amd64/signal.c @@ -0,0 +1,111 @@ +// 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" +#include "defs.h" +#include "os.h" +#include "signals.h" + +void +dumpregs(Regs *r) +{ + printf("rax %X\n", r->rax); + printf("rbx %X\n", r->rbx); + printf("rcx %X\n", r->rcx); + printf("rdx %X\n", r->rdx); + printf("rdi %X\n", r->rdi); + printf("rsi %X\n", r->rsi); + printf("rbp %X\n", r->rbp); + printf("rsp %X\n", r->rsp); + printf("r8 %X\n", r->r8 ); + printf("r9 %X\n", r->r9 ); + printf("r10 %X\n", r->r10); + printf("r11 %X\n", r->r11); + printf("r12 %X\n", r->r12); + printf("r13 %X\n", r->r13); + printf("r14 %X\n", r->r14); + printf("r15 %X\n", r->r15); + printf("rip %X\n", r->rip); + printf("rflags %X\n", r->rflags); + printf("cs %X\n", r->cs); + printf("fs %X\n", r->fs); + printf("gs %X\n", r->gs); +} + +void +sighandler(int32 sig, Siginfo *info, void *context) +{ + Ucontext *uc; + Mcontext *mc; + Regs *r; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + if(sig < 0 || sig >= NSIG){ + printf("Signal %d\n", sig); + }else{ + printf("%s\n", sigtab[sig].name); + } + + uc = context; + mc = uc->uc_mcontext; + r = &mc->ss; + + printf("Faulting address: %p\n", info->si_addr); + printf("pc: %X\n", r->rip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)r->rip, (void*)r->rsp, (void*)r->r15); + tracebackothers((void*)r->r15); + dumpregs(r); + } + + breakpoint(); + exit(2); +} + +void +sigignore(int32, Siginfo*, void*) +{ +} + +void +signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + int32 i; + static Sigaction sa; + + sa.sa_flags |= SA_SIGINFO|SA_ONSTACK; + sa.sa_mask = 0; // 0xFFFFFFFFU; + sa.sa_tramp = sigtramp; // sigtramp's job is to call into real handler + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) { + sa.__sigaction_u.__sa_sigaction = sighandler; + } else { + sa.__sigaction_u.__sa_sigaction = sigignore; + } + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + sigaction(i, &sa, nil); + } + } +} + diff --git a/src/lib/runtime/darwin/amd64/sys.s b/src/lib/runtime/darwin/amd64/sys.s new file mode 100644 index 000000000..b46c823ae --- /dev/null +++ b/src/lib/runtime/darwin/amd64/sys.s @@ -0,0 +1,263 @@ +// 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. + +// +// System calls and other sys.stuff for AMD64, Darwin +// See http://fxr.watson.org/fxr/source/bsd/kern/syscalls.c?v=xnu-1228 +// or /usr/include/sys/syscall.h (on a Mac) for system call numbers. +// + +// Exit the entire program (like C exit) +TEXT exit(SB),7,$-8 + MOVL 8(SP), DI // arg 1 exit status + MOVL $(0x2000000+1), AX // syscall entry + SYSCALL + CALL notok(SB) + RET + +// Exit this OS thread (like pthread_exit, which eventually +// calls __bsdthread_terminate). +TEXT exit1(SB),7,$-8 + MOVL 8(SP), DI // arg 1 exit status + MOVL $(0x2000000+361), AX // syscall entry + SYSCALL + CALL notok(SB) + RET + +TEXT sys·write(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fid + MOVQ 16(SP), SI // arg 2 buf + MOVL 24(SP), DX // arg 3 count + MOVL $(0x2000000+4), AX // syscall entry + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +TEXT open(SB),7,$-8 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVQ $0, R10 + MOVL $(0x2000000+5), AX // syscall entry + SYSCALL + RET + +TEXT close(SB),7,$-8 + MOVL 8(SP), DI + MOVL $(0x2000000+6), AX // syscall entry + SYSCALL + RET + +TEXT fstat(SB),7,$-8 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL $(0x2000000+339), AX // syscall entry; really fstat64 + SYSCALL + RET + +TEXT read(SB),7,$-8 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $(0x2000000+3), AX // syscall entry + SYSCALL + RET + +TEXT write(SB),7,$-8 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $(0x2000000+4), AX // syscall entry + SYSCALL + RET + +TEXT sigaction(SB),7,$-8 + MOVL 8(SP), DI // arg 1 sig + MOVQ 16(SP), SI // arg 2 act + MOVQ 24(SP), DX // arg 3 oact + MOVQ 24(SP), CX // arg 3 oact + MOVQ 24(SP), R10 // arg 3 oact + MOVL $(0x2000000+46), AX // syscall entry + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +TEXT sigtramp(SB),7,$40 + MOVQ 32(R14), R15 // g = m->gsignal + MOVL DX,0(SP) + MOVQ CX,8(SP) + MOVQ R8,16(SP) + MOVQ R8, 24(SP) // save ucontext + MOVQ SI, 32(SP) // save infostyle + CALL DI + MOVL $(0x2000000+184), AX // sigreturn(ucontext, infostyle) + MOVQ 24(SP), DI // saved ucontext + MOVQ 32(SP), SI // saved infostyle + SYSCALL + INT $3 // not reached + +TEXT sys·mmap(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 addr + MOVL 16(SP), SI // arg 2 len + MOVL 20(SP), DX // arg 3 prot + MOVL 24(SP), R10 // arg 4 flags + MOVL 28(SP), R8 // arg 5 fid + MOVL 32(SP), R9 // arg 6 offset + MOVL $(0x2000000+197), AX // syscall entry + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +TEXT notok(SB),7,$-8 + MOVL $0xf1, BP + MOVQ BP, (BP) + RET + +TEXT sys·memclr(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 addr + MOVL 16(SP), CX // arg 2 count + ADDL $7, CX + SHRL $3, CX + MOVQ $0, AX + CLD + REP + STOSQ + RET + +TEXT sys·getcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ -8(AX),AX // get calling pc + RET + +TEXT sys·setcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ x+8(FP), BX + MOVQ BX, -8(AX) // set calling pc + RET + +TEXT sigaltstack(SB),7,$-8 + MOVQ new+8(SP), DI + MOVQ old+16(SP), SI + MOVQ $(0x2000000+53), AX + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +TEXT bsdthread_create(SB),7,$-8 + // Set up arguments to bsdthread_create system call. + // The ones in quotes pass through to the thread callback + // uninterpreted, so we can put whatever we want there. + MOVQ fn+32(SP), DI // "func" + MOVQ m+16(SP), SI // "arg" + MOVQ stk+8(SP), DX // stack + MOVQ g+24(SP), R10 // "pthread" +// TODO(rsc): why do we get away with 0 flags here but not on 386? + MOVQ $0, R8 // flags + MOVQ $(0x2000000+360), AX // bsdthread_create + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +// The thread that bsdthread_create creates starts executing here, +// because we registered this function using bsdthread_register +// at startup. +// DI = "pthread" (= g) +// SI = mach thread port +// DX = "func" (= fn) +// CX = "arg" (= m) +// R8 = stack +// R9 = flags (= 0) +// SP = stack - C_64_REDZONE_LEN (= stack - 128) +TEXT bsdthread_start(SB),7,$-8 + MOVQ CX, R14 // m + MOVQ DI, R15 // g + MOVQ SI, 24(R14) // thread port is m->procid + CALL DX // fn + CALL exit1(SB) + RET + +// void bsdthread_register(void) +// registers callbacks for threadstart (see bsdthread_create above +// and wqthread and pthsize (not used). returns 0 on success. +TEXT bsdthread_register(SB),7,$-8 + MOVQ $bsdthread_start(SB), DI // threadstart + MOVQ $0, SI // wqthread, not used by us + MOVQ $0, DX // pthsize, not used by us + MOVQ $(0x2000000+366), AX // bsdthread_register + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +// Mach system calls use 0x1000000 instead of the BSD's 0x2000000. + +// uint32 mach_msg_trap(void*, uint32, uint32, uint32, uint32, uint32, uint32) +TEXT mach_msg_trap(SB),7,$0 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVL 24(SP), R10 + MOVL 28(SP), R8 + MOVL 32(SP), R9 + MOVL 36(SP), R11 + PUSHQ R11 // seventh arg, on stack + MOVL $(0x1000000+31), AX // mach_msg_trap + SYSCALL + POPQ R11 + RET + +TEXT mach_task_self(SB),7,$0 + MOVL $(0x1000000+28), AX // task_self_trap + SYSCALL + RET + +TEXT mach_thread_self(SB),7,$0 + MOVL $(0x1000000+27), AX // thread_self_trap + SYSCALL + RET + +TEXT mach_reply_port(SB),7,$0 + MOVL $(0x1000000+26), AX // mach_reply_port + SYSCALL + RET + +// Mach provides trap versions of the semaphore ops, +// instead of requiring the use of RPC. + +// uint32 mach_semaphore_wait(uint32) +TEXT mach_semaphore_wait(SB),7,$0 + MOVL 8(SP), DI + MOVL $(0x1000000+36), AX // semaphore_wait_trap + SYSCALL + RET + +// uint32 mach_semaphore_timedwait(uint32, uint32, uint32) +TEXT mach_semaphore_timedwait(SB),7,$0 + MOVL 8(SP), DI + MOVL 12(SP), SI + MOVL 16(SP), DX + MOVL $(0x1000000+38), AX // semaphore_timedwait_trap + SYSCALL + RET + +// uint32 mach_semaphore_signal(uint32) +TEXT mach_semaphore_signal(SB),7,$0 + MOVL 8(SP), DI + MOVL $(0x1000000+33), AX // semaphore_signal_trap + SYSCALL + RET + +// uint32 mach_semaphore_signal_all(uint32) +TEXT mach_semaphore_signal_all(SB),7,$0 + MOVL 8(SP), DI + MOVL $(0x1000000+34), AX // semaphore_signal_all_trap + SYSCALL + RET + diff --git a/src/lib/runtime/darwin/defs.c b/src/lib/runtime/darwin/defs.c new file mode 100644 index 000000000..1ed662957 --- /dev/null +++ b/src/lib/runtime/darwin/defs.c @@ -0,0 +1,104 @@ +// 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. + +/* + * Input to godefs. + * + godefs -f -m64 defs.c >amd64/defs.h + godefs defs.c >386/defs.h + */ + +#define __DARWIN_UNIX03 0 + +#include <mach/mach.h> +#include <mach/message.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/mman.h> + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANON, + $MAP_PRIVATE = MAP_PRIVATE, + + $MACH_MSG_TYPE_MOVE_RECEIVE = MACH_MSG_TYPE_MOVE_RECEIVE, + $MACH_MSG_TYPE_MOVE_SEND = MACH_MSG_TYPE_MOVE_SEND, + $MACH_MSG_TYPE_MOVE_SEND_ONCE = MACH_MSG_TYPE_MOVE_SEND_ONCE, + $MACH_MSG_TYPE_COPY_SEND = MACH_MSG_TYPE_COPY_SEND, + $MACH_MSG_TYPE_MAKE_SEND = MACH_MSG_TYPE_MAKE_SEND, + $MACH_MSG_TYPE_MAKE_SEND_ONCE = MACH_MSG_TYPE_MAKE_SEND_ONCE, + $MACH_MSG_TYPE_COPY_RECEIVE = MACH_MSG_TYPE_COPY_RECEIVE, + + $MACH_MSG_PORT_DESCRIPTOR = MACH_MSG_PORT_DESCRIPTOR, + $MACH_MSG_OOL_DESCRIPTOR = MACH_MSG_OOL_DESCRIPTOR, + $MACH_MSG_OOL_PORTS_DESCRIPTOR = MACH_MSG_OOL_PORTS_DESCRIPTOR, + $MACH_MSG_OOL_VOLATILE_DESCRIPTOR = MACH_MSG_OOL_VOLATILE_DESCRIPTOR, + + $MACH_MSGH_BITS_COMPLEX = MACH_MSGH_BITS_COMPLEX, + + $MACH_SEND_MSG = MACH_SEND_MSG, + $MACH_RCV_MSG = MACH_RCV_MSG, + $MACH_RCV_LARGE = MACH_RCV_LARGE, + + $MACH_SEND_TIMEOUT = MACH_SEND_TIMEOUT, + $MACH_SEND_INTERRUPT = MACH_SEND_INTERRUPT, + $MACH_SEND_CANCEL = MACH_SEND_CANCEL, + $MACH_SEND_ALWAYS = MACH_SEND_ALWAYS, + $MACH_SEND_TRAILER = MACH_SEND_TRAILER, + $MACH_RCV_TIMEOUT = MACH_RCV_TIMEOUT, + $MACH_RCV_NOTIFY = MACH_RCV_NOTIFY, + $MACH_RCV_INTERRUPT = MACH_RCV_INTERRUPT, + $MACH_RCV_OVERWRITE = MACH_RCV_OVERWRITE, + + $NDR_PROTOCOL_2_0 = NDR_PROTOCOL_2_0, + $NDR_INT_BIG_ENDIAN = NDR_INT_BIG_ENDIAN, + $NDR_INT_LITTLE_ENDIAN = NDR_INT_LITTLE_ENDIAN, + $NDR_FLOAT_IEEE = NDR_FLOAT_IEEE, + $NDR_CHAR_ASCII = NDR_CHAR_ASCII, + + $SA_SIGINFO = SA_SIGINFO, + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_USERTRAMP = SA_USERTRAMP, + $SA_64REGSET = SA_64REGSET, +}; + +typedef mach_msg_body_t $MachBody; +typedef mach_msg_header_t $MachHeader; +typedef NDR_record_t $MachNDR; +typedef mach_msg_port_descriptor_t $MachPort; + +typedef stack_t $StackT; +typedef union __sigaction_u $Sighandler; + +typedef struct __sigaction $Sigaction; // used in syscalls +// typedef struct sigaction $Sigaction; // used by the C library +typedef union sigval $Sigval; +typedef siginfo_t $Siginfo; + +typedef struct fp_control $FPControl; +typedef struct fp_status $FPStatus; +typedef struct mmst_reg $RegMMST; +typedef struct xmm_reg $RegXMM; + +#ifdef __LP64__ +// amd64 +typedef x86_thread_state64_t $Regs; +typedef x86_float_state64_t $FloatState; +typedef x86_exception_state64_t $ExceptionState; +typedef struct mcontext64 $Mcontext; +#else +// 386 +typedef x86_thread_state32_t $Regs; +typedef x86_float_state32_t $FloatState; +typedef x86_exception_state32_t $ExceptionState; +typedef struct mcontext32 $Mcontext; +#endif + +typedef ucontext_t $Ucontext; diff --git a/src/lib/runtime/darwin/os.h b/src/lib/runtime/darwin/os.h new file mode 100644 index 000000000..2a3ca87bd --- /dev/null +++ b/src/lib/runtime/darwin/os.h @@ -0,0 +1,24 @@ +// 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. + +void bsdthread_create(void*, M*, G*, void(*)(void)); +void bsdthread_register(void); +int32 mach_msg_trap(MachHeader*, int32, uint32, uint32, uint32, uint32, uint32); +uint32 mach_reply_port(void); +void mach_semacquire(uint32); +uint32 mach_semcreate(void); +void mach_semdestroy(uint32); +void mach_semrelease(uint32); +void mach_semreset(uint32); +uint32 mach_task_self(void); +uint32 mach_task_self(void); +uint32 mach_thread_self(void); +uint32 mach_thread_self(void); + +struct Sigaction; +void sigaction(int64, struct Sigaction*, struct Sigaction*); + +struct StackT; +void sigaltstack(struct StackT*, struct StackT*); +void sigtramp(void); diff --git a/src/lib/runtime/darwin/signals.h b/src/lib/runtime/darwin/signals.h new file mode 100644 index 000000000..4051dc4dc --- /dev/null +++ b/src/lib/runtime/darwin/signals.h @@ -0,0 +1,48 @@ +// 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. + + +#define C SigCatch +#define I SigIgnore +#define R SigRestart + +static SigTab sigtab[] = { + /* 0 */ 0, "SIGNONE: no trap", + /* 1 */ 0, "SIGHUP: terminal line hangup", + /* 2 */ 0, "SIGINT: interrupt", + /* 3 */ C, "SIGQUIT: quit", + /* 4 */ C, "SIGILL: illegal instruction", + /* 5 */ C, "SIGTRAP: trace trap", /* used by panic and array out of bounds, etc. */ + /* 6 */ C, "SIGABRT: abort", + /* 7 */ C, "SIGEMT: emulate instruction executed", + /* 8 */ C, "SIGFPE: floating-point exception", + /* 9 */ 0, "SIGKILL: kill", + /* 10 */ C, "SIGBUS: bus error", + /* 11 */ C, "SIGSEGV: segmentation violation", + /* 12 */ C, "SIGSYS: bad system call", + /* 13 */ I, "SIGPIPE: write to broken pipe", + /* 14 */ 0, "SIGALRM: alarm clock", + /* 15 */ 0, "SIGTERM: termination", + /* 16 */ 0, "SIGURG: urgent condition on socket", + /* 17 */ 0, "SIGSTOP: stop", + /* 18 */ 0, "SIGTSTP: keyboard stop", + /* 19 */ 0, "SIGCONT: continue after stop", + /* 20 */ I+R, "SIGCHLD: child status has changed", + /* 21 */ 0, "SIGTTIN: background read from tty", + /* 22 */ 0, "SIGTTOU: background write to tty", + /* 23 */ 0, "SIGIO: i/o now possible", + /* 24 */ 0, "SIGXCPU: cpu limit exceeded", + /* 25 */ 0, "SIGXFSZ: file size limit exceeded", + /* 26 */ 0, "SIGVTALRM: virtual alarm clock", + /* 27 */ 0, "SIGPROF: profiling alarm clock", + /* 28 */ I+R, "SIGWINCH: window size change", + /* 29 */ 0, "SIGINFO: status request from keyboard", + /* 30 */ 0, "SIGUSR1: user-defined signal 1", + /* 31 */ 0, "SIGUSR2: user-defined signal 2", +}; +#undef C +#undef I +#undef R + +#define NSIG 32 diff --git a/src/lib/runtime/darwin/thread.c b/src/lib/runtime/darwin/thread.c new file mode 100644 index 000000000..79267085e --- /dev/null +++ b/src/lib/runtime/darwin/thread.c @@ -0,0 +1,427 @@ +// 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" +#include "defs.h" +#include "os.h" + +static void +unimplemented(int8 *name) +{ + prints(name); + prints(" not implemented\n"); + *(int32*)1231 = 1231; +} + +// Thread-safe allocation of a semaphore. +// Psema points at a kernel semaphore key. +// It starts out zero, meaning no semaphore. +// Fill it in, being careful of others calling initsema +// simultaneously. +static void +initsema(uint32 *psema) +{ + uint32 sema; + + if(*psema != 0) // already have one + return; + + sema = mach_semcreate(); + if(!cas(psema, 0, sema)){ + // Someone else filled it in. Use theirs. + mach_semdestroy(sema); + return; + } +} + + +// Atomic add and return new value. +static uint32 +xadd(uint32 volatile *val, int32 delta) +{ + uint32 oval, nval; + + for(;;){ + oval = *val; + nval = oval + delta; + if(cas(val, oval, nval)) + return nval; + } +} + + +// Blocking locks. + +// Implement Locks, using semaphores. +// l->key is the number of threads who want the lock. +// In a race, one thread increments l->key from 0 to 1 +// and the others increment it from >0 to >1. The thread +// who does the 0->1 increment gets the lock, and the +// others wait on the semaphore. When the 0->1 thread +// releases the lock by decrementing l->key, l->key will +// be >0, so it will increment the semaphore to wake up +// one of the others. This is the same algorithm used +// in Plan 9's user-level locks. +// +// Note that semaphores are never destroyed (the kernel +// will clean up when the process exits). We assume for now +// that Locks are only used for long-lived structures like M and G. + +void +lock(Lock *l) +{ + if(m->locks < 0) + throw("lock count"); + m->locks++; + + // Allocate semaphore if needed. + if(l->sema == 0) + initsema(&l->sema); + + if(xadd(&l->key, 1) > 1) // someone else has it; wait + mach_semacquire(l->sema); +} + +void +unlock(Lock *l) +{ + m->locks--; + if(m->locks < 0) + throw("lock count"); + + if(xadd(&l->key, -1) > 0) // someone else is waiting + mach_semrelease(l->sema); +} + + +// User-level semaphore implementation: +// try to do the operations in user space on u, +// but when it's time to block, fall back on the kernel semaphore k. +// This is the same algorithm used in Plan 9. +void +usemacquire(Usema *s) +{ + if((int32)xadd(&s->u, -1) < 0) + mach_semacquire(s->k); +} + +void +usemrelease(Usema *s) +{ + if((int32)xadd(&s->u, 1) <= 0) + mach_semrelease(s->k); +} + + +// Event notifications. +void +noteclear(Note *n) +{ + n->wakeup = 0; +} + +void +notesleep(Note *n) +{ + if(n->sema.k == 0) + initsema(&n->sema.k); + while(!n->wakeup) + usemacquire(&n->sema); +} + +void +notewakeup(Note *n) +{ + if(n->sema.k == 0) + initsema(&n->sema.k); + n->wakeup = 1; + usemrelease(&n->sema); +} + + +// BSD interface for threading. +void +osinit(void) +{ + // Register our thread-creation callback (see {amd64,386}/sys.s). + bsdthread_register(); +} + +void +newosproc(M *m, G *g, void *stk, void (*fn)(void)) +{ + // printf("newosproc m=%p g=%p stk=%p fn=%p\n", m, g, stk, fn); + m->tls[0] = m->id; // so 386 asm can find it + bsdthread_create(stk, m, g, fn); +} + +// Called to initialize a new m (including the bootstrap m). +void +minit(void) +{ + // Initialize signal handling. + m->gsignal = malg(32*1024); // OS X wants >=8K, Linux >=2K + signalstack(m->gsignal->stackguard, 32*1024); +} + +// Mach IPC, to get at semaphores +// Definitions are in /usr/include/mach on a Mac. + +static void +macherror(int32 r, int8 *fn) +{ + printf("mach error %s: %d\n", fn, r); + throw("mach error"); +} + +enum +{ + DebugMach = 0 +}; + +static MachNDR zerondr; + +#define MACH_MSGH_BITS(a, b) ((a) | ((b)<<8)) + +static int32 +mach_msg(MachHeader *h, + int32 op, + uint32 send_size, + uint32 rcv_size, + uint32 rcv_name, + uint32 timeout, + uint32 notify) +{ + // TODO: Loop on interrupt. + return mach_msg_trap(h, op, send_size, rcv_size, rcv_name, timeout, notify); +} + +// Mach RPC (MIG) + +enum +{ + MinMachMsg = 48, + Reply = 100, +}; + +#pragma pack on +typedef struct CodeMsg CodeMsg; +struct CodeMsg +{ + MachHeader h; + MachNDR NDR; + int32 code; +}; +#pragma pack off + +static int32 +machcall(MachHeader *h, int32 maxsize, int32 rxsize) +{ + uint32 *p; + int32 i, ret, id; + uint32 port; + CodeMsg *c; + + if((port = m->machport) == 0){ + port = mach_reply_port(); + m->machport = port; + } + + h->msgh_bits |= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + h->msgh_local_port = port; + h->msgh_reserved = 0; + id = h->msgh_id; + + if(DebugMach){ + p = (uint32*)h; + prints("send:\t"); + for(i=0; i<h->msgh_size/sizeof(p[0]); i++){ + prints(" "); + sys·printpointer((void*)p[i]); + if(i%8 == 7) + prints("\n\t"); + } + if(i%8) + prints("\n"); + } + + ret = mach_msg(h, MACH_SEND_MSG|MACH_RCV_MSG, + h->msgh_size, maxsize, port, 0, 0); + if(ret != 0){ + if(DebugMach){ + prints("mach_msg error "); + sys·printint(ret); + prints("\n"); + } + return ret; + } + + if(DebugMach){ + p = (uint32*)h; + prints("recv:\t"); + for(i=0; i<h->msgh_size/sizeof(p[0]); i++){ + prints(" "); + sys·printpointer((void*)p[i]); + if(i%8 == 7) + prints("\n\t"); + } + if(i%8) + prints("\n"); + } + + if(h->msgh_id != id+Reply){ + if(DebugMach){ + prints("mach_msg reply id mismatch "); + sys·printint(h->msgh_id); + prints(" != "); + sys·printint(id+Reply); + prints("\n"); + } + return -303; // MIG_REPLY_MISMATCH + } + + // Look for a response giving the return value. + // Any call can send this back with an error, + // and some calls only have return values so they + // send it back on success too. I don't quite see how + // you know it's one of these and not the full response + // format, so just look if the message is right. + c = (CodeMsg*)h; + if(h->msgh_size == sizeof(CodeMsg) + && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)){ + if(DebugMach){ + prints("mig result "); + sys·printint(c->code); + prints("\n"); + } + return c->code; + } + + if(h->msgh_size != rxsize){ + if(DebugMach){ + prints("mach_msg reply size mismatch "); + sys·printint(h->msgh_size); + prints(" != "); + sys·printint(rxsize); + prints("\n"); + } + return -307; // MIG_ARRAY_TOO_LARGE + } + + return 0; +} + + +// Semaphores! + +enum +{ + Tmach_semcreate = 3418, + Rmach_semcreate = Tmach_semcreate + Reply, + + Tmach_semdestroy = 3419, + Rmach_semdestroy = Tmach_semdestroy + Reply, +}; + +typedef struct Tmach_semcreateMsg Tmach_semcreateMsg; +typedef struct Rmach_semcreateMsg Rmach_semcreateMsg; +typedef struct Tmach_semdestroyMsg Tmach_semdestroyMsg; +// Rmach_semdestroyMsg = CodeMsg + +#pragma pack on +struct Tmach_semcreateMsg +{ + MachHeader h; + MachNDR ndr; + int32 policy; + int32 value; +}; + +struct Rmach_semcreateMsg +{ + MachHeader h; + MachBody body; + MachPort semaphore; +}; + +struct Tmach_semdestroyMsg +{ + MachHeader h; + MachBody body; + MachPort semaphore; +}; +#pragma pack off + +uint32 +mach_semcreate(void) +{ + union { + Tmach_semcreateMsg tx; + Rmach_semcreateMsg rx; + uint8 pad[MinMachMsg]; + } m; + int32 r; + + m.tx.h.msgh_bits = 0; + m.tx.h.msgh_size = sizeof(m.tx); + m.tx.h.msgh_remote_port = mach_task_self(); + m.tx.h.msgh_id = Tmach_semcreate; + m.tx.ndr = zerondr; + + m.tx.policy = 0; // 0 = SYNC_POLICY_FIFO + m.tx.value = 0; + + if((r = machcall(&m.tx.h, sizeof m, sizeof(m.rx))) != 0) + macherror(r, "semaphore_create"); + if(m.rx.body.msgh_descriptor_count != 1) + unimplemented("mach_semcreate desc count"); + return m.rx.semaphore.name; +} + +void +mach_semdestroy(uint32 sem) +{ + union { + Tmach_semdestroyMsg tx; + uint8 pad[MinMachMsg]; + } m; + int32 r; + + m.tx.h.msgh_bits = MACH_MSGH_BITS_COMPLEX; + m.tx.h.msgh_size = sizeof(m.tx); + m.tx.h.msgh_remote_port = mach_task_self(); + m.tx.h.msgh_id = Tmach_semdestroy; + m.tx.body.msgh_descriptor_count = 1; + m.tx.semaphore.name = sem; + m.tx.semaphore.disposition = MACH_MSG_TYPE_MOVE_SEND; + m.tx.semaphore.type = 0; + + if((r = machcall(&m.tx.h, sizeof m, 0)) != 0) + macherror(r, "semaphore_destroy"); +} + +// The other calls have simple system call traps in sys.s +int32 mach_semaphore_wait(uint32 sema); +int32 mach_semaphore_timedwait(uint32 sema, uint32 sec, uint32 nsec); +int32 mach_semaphore_signal(uint32 sema); +int32 mach_semaphore_signal_all(uint32 sema); + +void +mach_semacquire(uint32 sem) +{ + int32 r; + + if((r = mach_semaphore_wait(sem)) != 0) + macherror(r, "semaphore_wait"); +} + +void +mach_semrelease(uint32 sem) +{ + int32 r; + + if((r = mach_semaphore_signal(sem)) != 0) + macherror(r, "semaphore_signal"); +} + diff --git a/src/lib/runtime/runtime.go b/src/lib/runtime/extern.go index 6fb5756d6..6fb5756d6 100644 --- a/src/lib/runtime/runtime.go +++ b/src/lib/runtime/extern.go diff --git a/src/lib/runtime/float.c b/src/lib/runtime/float.c new file mode 100644 index 000000000..5122f359a --- /dev/null +++ b/src/lib/runtime/float.c @@ -0,0 +1,173 @@ +// 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" + +static uint64 uvnan = 0x7FF0000000000001ULL; +static uint64 uvinf = 0x7FF0000000000000ULL; +static uint64 uvneginf = 0xFFF0000000000000ULL; + +uint32 +float32tobits(float32 f) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float32 f; + uint32 i; + } u; + u.f = f; + return u.i; +} + +uint64 +float64tobits(float64 f) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float64 f; + uint64 i; + } u; + u.f = f; + return u.i; +} + +float64 +float64frombits(uint64 i) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float64 f; + uint64 i; + } u; + u.i = i; + return u.f; +} + +float32 +float32frombits(uint32 i) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float32 f; + uint32 i; + } u; + u.i = i; + return u.f; +} + +bool +isInf(float64 f, int32 sign) +{ + uint64 x; + + x = float64tobits(f); + if(sign == 0) + return x == uvinf || x == uvneginf; + if(sign > 0) + return x == uvinf; + return x == uvneginf; +} + +float64 +NaN(void) +{ + return float64frombits(uvnan); +} + +bool +isNaN(float64 f) +{ + uint64 x; + + x = float64tobits(f); + return ((uint32)(x>>52) & 0x7FF) == 0x7FF && !isInf(f, 0); +} + +float64 +Inf(int32 sign) +{ + if(sign >= 0) + return float64frombits(uvinf); + else + return float64frombits(uvneginf); +} + +enum +{ + MASK = 0x7ffL, + SHIFT = 64-11-1, + BIAS = 1022L, +}; + +float64 +frexp(float64 d, int32 *ep) +{ + uint64 x; + + if(d == 0) { + *ep = 0; + return 0; + } + x = float64tobits(d); + *ep = (int32)((x >> SHIFT) & MASK) - BIAS; + x &= ~((uint64)MASK << SHIFT); + x |= (uint64)BIAS << SHIFT; + return float64frombits(x); +} + +float64 +ldexp(float64 d, int32 e) +{ + uint64 x; + + if(d == 0) + return 0; + x = float64tobits(d); + e += (int32)(x >> SHIFT) & MASK; + if(e <= 0) + return 0; /* underflow */ + if(e >= MASK){ /* overflow */ + if(d < 0) + return Inf(-1); + return Inf(1); + } + x &= ~((uint64)MASK << SHIFT); + x |= (uint64)e << SHIFT; + return float64frombits(x); +} + +float64 +modf(float64 d, float64 *ip) +{ + float64 dd; + uint64 x; + int32 e; + + if(d < 1) { + if(d < 0) { + d = modf(-d, ip); + *ip = -*ip; + return -d; + } + *ip = 0; + return d; + } + + x = float64tobits(d); + e = (int32)((x >> SHIFT) & MASK) - BIAS; + + /* + * Keep the top 11+e bits; clear the rest. + */ + if(e <= 64-11) + x &= ~(((uint64)1 << (64LL-11LL-e))-1); + dd = float64frombits(x); + *ip = dd; + return d - dd; +} + diff --git a/src/lib/runtime/float_go.cgo b/src/lib/runtime/float_go.cgo new file mode 100644 index 000000000..518d55950 --- /dev/null +++ b/src/lib/runtime/float_go.cgo @@ -0,0 +1,52 @@ +// 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. + +package math + +#include "runtime.h" + +func Frexp(f float64) (frac float64, exp int32) { + frac = frexp(f, &exp); +} + +func Ldexp(frac float64, exp int32) (f float64) { + f = ldexp(frac, exp); +} + +func Modf(f float64) (integer float64, frac float64) { + frac = modf(f, &integer); +} + +func IsInf(f float64, sign int32) (is bool) { + is = isInf(f, sign); +} + +func IsNaN(f float64) (is bool) { + is = isNaN(f); +} + +func Inf(sign int32) (f float64) { + f = Inf(sign); +} + +func NaN() (f float64) { + f = NaN(); +} + +func Float32bits(f float32) (b uint32) { + b = float32tobits(f); +} + +func Float64bits(f float64) (b uint64) { + b = float64tobits(f); +} + +func Float32frombits(b uint32) (f float32) { + f = float32frombits(b); +} + +func Float64frombits(b uint64) (f float64) { + f = float64frombits(b); +} + diff --git a/src/lib/runtime/hashmap.c b/src/lib/runtime/hashmap.c new file mode 100644 index 000000000..b3022ca14 --- /dev/null +++ b/src/lib/runtime/hashmap.c @@ -0,0 +1,954 @@ +// 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" +#include "hashmap.h" + +/* Return a pointer to the struct/union of type "type" + whose "field" field is addressed by pointer "p". */ + + +struct hash { /* a hash table; initialize with hash_init() */ + uint32 count; /* elements in table - must be first */ + + uint8 datasize; /* amount of data to store in entry */ + uint8 max_power; /* max power of 2 to create sub-tables */ + uint8 max_probes; /* max entries to probe before rehashing */ + int32 changes; /* inc'ed whenever a subtable is created/grown */ + hash_hash_t (*data_hash) (uint32, void *a); /* return hash of *a */ + uint32 (*data_eq) (uint32, void *a, void *b); /* return whether *a == *b */ + void (*data_del) (uint32, void *arg, void *data); /* invoked on deletion */ + struct hash_subtable *st; /* first-level table */ + + uint32 keysize; + uint32 valsize; + uint32 datavo; + uint32 ko; + uint32 vo; + uint32 po; + Alg* keyalg; + Alg* valalg; +}; + +struct hash_entry { + hash_hash_t hash; /* hash value of data */ + byte data[1]; /* user data has "datasize" bytes */ +}; + +struct hash_subtable { + uint8 power; /* bits used to index this table */ + uint8 used; /* bits in hash used before reaching this table */ + uint8 datasize; /* bytes of client data in an entry */ + uint8 max_probes; /* max number of probes when searching */ + int16 limit_bytes; /* max_probes * (datasize+sizeof (hash_hash_t)) */ + struct hash_entry *end; /* points just past end of entry[] */ + struct hash_entry entry[1]; /* 2**power+max_probes-1 elements of elemsize bytes */ +}; + +#define HASH_DATA_EQ(h,x,y) ((*h->data_eq) (h->keysize, (x), (y))) + +#define HASH_REHASH 0x2 /* an internal flag */ +/* the number of bits used is stored in the flags word too */ +#define HASH_USED(x) ((x) >> 2) +#define HASH_MAKE_USED(x) ((x) << 2) + +#define HASH_LOW 6 +#define HASH_ONE (((hash_hash_t)1) << HASH_LOW) +#define HASH_MASK (HASH_ONE - 1) +#define HASH_ADJUST(x) (((x) < HASH_ONE) << HASH_LOW) + +#define HASH_BITS (sizeof (hash_hash_t) * 8) + +#define HASH_SUBHASH HASH_MASK +#define HASH_NIL 0 +#define HASH_NIL_MEMSET 0 + +#define HASH_OFFSET(base, byte_offset) \ + ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) + + +/* return a hash layer with 2**power empty entries */ +static struct hash_subtable * +hash_subtable_new (struct hash *h, int32 power, int32 used) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + int32 bytes = elemsize << power; + struct hash_subtable *st; + int32 limit_bytes = h->max_probes * elemsize; + int32 max_probes = h->max_probes; + + if (bytes < limit_bytes) { + limit_bytes = bytes; + max_probes = 1 << power; + } + bytes += limit_bytes - elemsize; + st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes); + st->power = power; + st->used = used; + st->datasize = h->datasize; + st->max_probes = max_probes; + st->limit_bytes = limit_bytes; + st->end = HASH_OFFSET (st->entry, bytes); + memset (st->entry, HASH_NIL_MEMSET, bytes); + return (st); +} + +static void +init_sizes (int64 hint, int32 *init_power, int32 *max_power) +{ + int32 log = 0; + int32 i; + + for (i = 32; i != 0; i >>= 1) { + if ((hint >> (log + i)) != 0) { + log += i; + } + } + log += 1 + (((hint << 3) >> log) >= 11); /* round up for utilization */ + if (log <= 14) { + *init_power = log; + } else { + *init_power = 12; + } + *max_power = 12; +} + +static void +hash_init (struct hash *h, + int32 datasize, + hash_hash_t (*data_hash) (uint32, void *), + uint32 (*data_eq) (uint32, void *, void *), + void (*data_del) (uint32, void *, void *), + int64 hint) +{ + int32 init_power; + int32 max_power; + + if(datasize < sizeof (void *)) + datasize = sizeof (void *); + datasize = rnd(datasize, sizeof (void *)); + init_sizes (hint, &init_power, &max_power); + h->datasize = datasize; + h->max_power = max_power; + h->max_probes = 15; + assert (h->datasize == datasize); + assert (h->max_power == max_power); + assert (sizeof (void *) <= h->datasize || h->max_power == 255); + h->count = 0; + h->changes = 0; + h->data_hash = data_hash; + h->data_eq = data_eq; + h->data_del = data_del; + h->st = hash_subtable_new (h, init_power, 0); +} + +static void +hash_remove_n (struct hash_subtable *st, struct hash_entry *dst_e, int32 n) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *src_e = HASH_OFFSET (dst_e, n * elemsize); + struct hash_entry *end_e = st->end; + int32 shift = HASH_BITS - (st->power + st->used); + int32 index_mask = (((hash_hash_t)1) << st->power) - 1; + int32 dst_i = (((byte *) dst_e) - ((byte *) st->entry)) / elemsize; + int32 src_i = dst_i + n; + hash_hash_t hash; + int32 skip; + int32 bytes; + + while (dst_e != src_e) { + if (src_e != end_e) { + struct hash_entry *cp_e = src_e; + int32 save_dst_i = dst_i; + while (cp_e != end_e && (hash = cp_e->hash) != HASH_NIL && + ((hash >> shift) & index_mask) <= dst_i) { + cp_e = HASH_OFFSET (cp_e, elemsize); + dst_i++; + } + bytes = ((byte *) cp_e) - (byte *) src_e; + memmove (dst_e, src_e, bytes); + dst_e = HASH_OFFSET (dst_e, bytes); + src_e = cp_e; + src_i += dst_i - save_dst_i; + if (src_e != end_e && (hash = src_e->hash) != HASH_NIL) { + skip = ((hash >> shift) & index_mask) - dst_i; + } else { + skip = src_i - dst_i; + } + } else { + skip = src_i - dst_i; + } + bytes = skip * elemsize; + memset (dst_e, HASH_NIL_MEMSET, bytes); + dst_e = HASH_OFFSET (dst_e, bytes); + dst_i += skip; + } +} + +static int32 +hash_insert_internal (struct hash_subtable **pst, int32 flags, hash_hash_t hash, + struct hash *h, void *data, void **pres); + +static void +hash_conv (struct hash *h, + struct hash_subtable *st, int32 flags, + hash_hash_t hash, + struct hash_entry *e) +{ + int32 new_flags = (flags + HASH_MAKE_USED (st->power)) | HASH_REHASH; + int32 shift = HASH_BITS - HASH_USED (new_flags); + hash_hash_t prefix_mask = (-(hash_hash_t)1) << shift; + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + void *dummy_result; + struct hash_entry *de; + int32 index_mask = (1 << st->power) - 1; + hash_hash_t e_hash; + struct hash_entry *pe = HASH_OFFSET (e, -elemsize); + + while (e != st->entry && (e_hash = pe->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { + e = pe; + pe = HASH_OFFSET (pe, -elemsize); + } + + de = e; + while (e != st->end && + (e_hash = e->hash) != HASH_NIL && + (e_hash & HASH_MASK) != HASH_SUBHASH) { + struct hash_entry *target_e = HASH_OFFSET (st->entry, ((e_hash >> shift) & index_mask) * elemsize); + struct hash_entry *ne = HASH_OFFSET (e, elemsize); + hash_hash_t current = e_hash & prefix_mask; + if (de < target_e) { + memset (de, HASH_NIL_MEMSET, ((byte *) target_e) - (byte *) de); + de = target_e; + } + if ((hash & prefix_mask) == current || + (ne != st->end && (e_hash = ne->hash) != HASH_NIL && + (e_hash & prefix_mask) == current)) { + struct hash_subtable *new_st = hash_subtable_new (h, 1, HASH_USED (new_flags)); + int32 rc = hash_insert_internal (&new_st, new_flags, e->hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + e = ne; + while (e != st->end && (e_hash = e->hash) != HASH_NIL && (e_hash & prefix_mask) == current) { + assert ((e_hash & HASH_MASK) != HASH_SUBHASH); + rc = hash_insert_internal (&new_st, new_flags, e_hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + e = HASH_OFFSET (e, elemsize); + } + memset (de->data, HASH_NIL_MEMSET, h->datasize); + *(struct hash_subtable **)de->data = new_st; + de->hash = current | HASH_SUBHASH; + } else { + if (e != de) { + memcpy (de, e, elemsize); + } + e = HASH_OFFSET (e, elemsize); + } + de = HASH_OFFSET (de, elemsize); + } + if (e != de) { + hash_remove_n (st, de, (((byte *) e) - (byte *) de) / elemsize); + } +} + +static void +hash_grow (struct hash *h, struct hash_subtable **pst, int32 flags) +{ + struct hash_subtable *old_st = *pst; + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + *pst = hash_subtable_new (h, old_st->power + 1, HASH_USED (flags)); + struct hash_entry *end_e = old_st->end; + struct hash_entry *e; + void *dummy_result; + int32 used = 0; + + flags |= HASH_REHASH; + for (e = old_st->entry; e != end_e; e = HASH_OFFSET (e, elemsize)) { + hash_hash_t hash = e->hash; + if (hash != HASH_NIL) { + int32 rc = hash_insert_internal (pst, flags, e->hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + used++; + } + } + free (old_st); +} + +int32 +hash_lookup (struct hash *h, void *data, void **pres) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + hash_hash_t hash = (*h->data_hash) (h->keysize, data) & ~HASH_MASK; + struct hash_subtable *st = h->st; + int32 used = 0; + hash_hash_t e_hash; + struct hash_entry *e; + struct hash_entry *end_e; + + hash += HASH_ADJUST (hash); + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + + e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ + e_hash = e->hash; + if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ + break; + } + used += st->power; + st = *(struct hash_subtable **)e->data; + } + end_e = HASH_OFFSET (e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + } + while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, e->data)) { /* a match */ + *pres = e->data; + return (1); + } + e = HASH_OFFSET (e, elemsize); + } + USED(e_hash); + *pres = 0; + return (0); +} + +int32 +hash_remove (struct hash *h, void *data, void *arg) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + hash_hash_t hash = (*h->data_hash) (h->keysize, data) & ~HASH_MASK; + struct hash_subtable *st = h->st; + int32 used = 0; + hash_hash_t e_hash; + struct hash_entry *e; + struct hash_entry *end_e; + + hash += HASH_ADJUST (hash); + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + + e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ + e_hash = e->hash; + if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ + break; + } + used += st->power; + st = *(struct hash_subtable **)e->data; + } + end_e = HASH_OFFSET (e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + } + while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, e->data)) { /* a match */ + (*h->data_del) (h->keysize, arg, e->data); + hash_remove_n (st, e, 1); + h->count--; + return (1); + } + e = HASH_OFFSET (e, elemsize); + } + USED(e_hash); + return (0); +} + +static int32 +hash_insert_internal (struct hash_subtable **pst, int32 flags, hash_hash_t hash, + struct hash *h, void *data, void **pres) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + + if ((flags & HASH_REHASH) == 0) { + hash += HASH_ADJUST (hash); + hash &= ~HASH_MASK; + } + for (;;) { + struct hash_subtable *st = *pst; + int32 shift = HASH_BITS - (st->power + HASH_USED (flags)); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + struct hash_entry *start_e = + HASH_OFFSET (st->entry, i * elemsize); /* start_e is the pointer to element i */ + struct hash_entry *e = start_e; /* e is going to range over [start_e, end_e) */ + struct hash_entry *end_e; + hash_hash_t e_hash = e->hash; + + if ((e_hash & HASH_MASK) == HASH_SUBHASH) { /* a subtable */ + pst = (struct hash_subtable **) e->data; + flags += HASH_MAKE_USED (st->power); + continue; + } + end_e = HASH_OFFSET (start_e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + i++; + } + if (e != end_e && e_hash != HASH_NIL) { + /* ins_e ranges over the elements that may match */ + struct hash_entry *ins_e = e; + int32 ins_i = i; + hash_hash_t ins_e_hash; + while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, ins_e->data)) { /* a match */ + *pres = ins_e->data; + return (1); + } + assert (e_hash != hash || (flags & HASH_REHASH) == 0); + hash += (e_hash == hash); /* adjust hash if it collides */ + ins_e = HASH_OFFSET (ins_e, elemsize); + ins_i++; + if (e_hash <= hash) { /* set e to insertion point */ + e = ins_e; + i = ins_i; + } + } + /* set ins_e to the insertion point for the new element */ + ins_e = e; + ins_i = i; + ins_e_hash = 0; + /* move ins_e to point at the end of the contiguous block, but + stop if any element can't be moved by one up */ + while (ins_e != st->end && (ins_e_hash = ins_e->hash) != HASH_NIL && + ins_i + 1 - ((ins_e_hash >> shift) & index_mask) < st->max_probes && + (ins_e_hash & HASH_MASK) != HASH_SUBHASH) { + ins_e = HASH_OFFSET (ins_e, elemsize); + ins_i++; + } + if (e == end_e || ins_e == st->end || ins_e_hash != HASH_NIL) { + e = end_e; /* can't insert; must grow or convert to subtable */ + } else { /* make space for element */ + memmove (HASH_OFFSET (e, elemsize), e, ((byte *) ins_e) - (byte *) e); + } + } + if (e != end_e) { + e->hash = hash; + *pres = e->data; + return (0); + } + h->changes++; + if (st->power < h->max_power) { + hash_grow (h, pst, flags); + } else { + hash_conv (h, st, flags, hash, start_e); + } + } +} + +int32 +hash_insert (struct hash *h, void *data, void **pres) +{ + int32 rc = hash_insert_internal (&h->st, 0, (*h->data_hash) (h->keysize, data), h, data, pres); + + h->count += (rc == 0); /* increment count if element didn't previously exist */ + return (rc); +} + +uint32 +hash_count (struct hash *h) +{ + return (h->count); +} + +static void +iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) +{ + int32 elemsize = it->elemsize; + hash_hash_t last_hash = it->last_hash; + struct hash_entry *e; + hash_hash_t e_hash; + struct hash_iter_sub *sub = &it->subtable_state[it->i]; + struct hash_entry *end; + + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (last_hash >> shift) & index_mask; + + end = st->end; + e = HASH_OFFSET (st->entry, i * elemsize); + sub->start = st->entry; + sub->end = end; + + if ((e->hash & HASH_MASK) != HASH_SUBHASH) { + break; + } + sub->e = HASH_OFFSET (e, elemsize); + sub = &it->subtable_state[++(it->i)]; + used += st->power; + st = *(struct hash_subtable **)e->data; + } + while (e != end && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { + e = HASH_OFFSET (e, elemsize); + } + sub->e = e; +} + +void * +hash_next (struct hash_iter *it) +{ + int32 elemsize = it->elemsize; + struct hash_iter_sub *sub = &it->subtable_state[it->i]; + struct hash_entry *e = sub->e; + struct hash_entry *end = sub->end; + hash_hash_t e_hash = 0; + + if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ + it->changes = it->h->changes; + it->i = 0; + iter_restart (it, it->h->st, 0); + sub = &it->subtable_state[it->i]; + e = sub->e; + end = sub->end; + } + if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { + struct hash_entry *start = HASH_OFFSET (e, -(elemsize * it->h->max_probes)); + struct hash_entry *pe = HASH_OFFSET (e, -elemsize); + hash_hash_t last_hash = it->last_hash; + if (start < sub->start) { + start = sub->start; + } + while (e != start && ((e_hash = pe->hash) == HASH_NIL || last_hash < e_hash)) { + e = pe; + pe = HASH_OFFSET (pe, -elemsize); + } + while (e != end && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { + e = HASH_OFFSET (e, elemsize); + } + } + + for (;;) { + while (e != end && (e_hash = e->hash) == HASH_NIL) { + e = HASH_OFFSET (e, elemsize); + } + if (e == end) { + if (it->i == 0) { + it->last_hash = HASH_OFFSET (e, -elemsize)->hash; + sub->e = e; + return (0); + } else { + it->i--; + sub = &it->subtable_state[it->i]; + e = sub->e; + end = sub->end; + } + } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { + it->last_hash = e->hash; + sub->e = HASH_OFFSET (e, elemsize); + return (e->data); + } else { + struct hash_subtable *st = + *(struct hash_subtable **)e->data; + sub->e = HASH_OFFSET (e, elemsize); + it->i++; + assert (it->i < sizeof (it->subtable_state) / + sizeof (it->subtable_state[0])); + sub = &it->subtable_state[it->i]; + sub->e = e = st->entry; + sub->start = st->entry; + sub->end = end = st->end; + } + } +} + +void +hash_iter_init (struct hash *h, struct hash_iter *it) +{ + it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + it->changes = h->changes; + it->i = 0; + it->h = h; + it->last_hash = 0; + it->subtable_state[0].e = h->st->entry; + it->subtable_state[0].start = h->st->entry; + it->subtable_state[0].end = h->st->end; +} + +static void +clean_st (struct hash_subtable *st, int32 *slots, int32 *used) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *e = st->entry; + struct hash_entry *end = st->end; + int32 lslots = (((byte *) end) - (byte *) e) / elemsize; + int32 lused = 0; + + while (e != end) { + hash_hash_t hash = e->hash; + if ((hash & HASH_MASK) == HASH_SUBHASH) { + clean_st (*(struct hash_subtable **)e->data, slots, used); + } else { + lused += (hash != HASH_NIL); + } + e = HASH_OFFSET (e, elemsize); + } + free (st); + *slots += lslots; + *used += lused; +} + +void +hash_destroy (struct hash *h) +{ + int32 slots = 0; + int32 used = 0; + + clean_st (h->st, &slots, &used); + free (h); +} + +static void +hash_visit_internal (struct hash_subtable *st, + int32 used, int32 level, + void (*data_visit) (void *arg, int32 level, void *data), + void *arg) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *e = st->entry; + int32 shift = HASH_BITS - (used + st->power); + int32 i = 0; + + while (e != st->end) { + int32 index = ((e->hash >> (shift - 1)) >> 1) & ((1 << st->power) - 1); + if ((e->hash & HASH_MASK) == HASH_SUBHASH) { + (*data_visit) (arg, level, e->data); + hash_visit_internal (*(struct hash_subtable **)e->data, + used + st->power, level + 1, data_visit, arg); + } else { + (*data_visit) (arg, level, e->data); + } + if (e->hash != HASH_NIL) { + assert (i < index + st->max_probes); + assert (index <= i); + } + e = HASH_OFFSET (e, elemsize); + i++; + } +} + +void +hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg) +{ + hash_visit_internal (h->st, 0, 0, data_visit, arg); +} + +// +/// interfaces to go runtime +// + +static void +donothing(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); +} + +typedef struct hash Hmap; +static int32 debug = 0; + +// newmap(keysize uint32, valsize uint32, +// keyalg uint32, valalg uint32, +// hint uint32) (hmap *map[any]any); +void +sys·newmap(uint32 keysize, uint32 valsize, + uint32 keyalg, uint32 valalg, uint32 hint, + Hmap* ret) +{ + Hmap *h; + + if(keyalg >= nelem(algarray) || algarray[keyalg].hash == nohash) { + printf("map(keyalg=%d)\n", keyalg); + throw("sys·newmap: unsupported map key type"); + } + + if(valalg >= nelem(algarray)) { + printf("map(valalg=%d)\n", valalg); + throw("sys·newmap: unsupported map value type"); + } + + h = mal(sizeof(*h)); + + // align value inside data so that mark-sweep gc can find it. + // might remove in the future and just assume datavo == keysize. + h->datavo = keysize; + if(valsize >= sizeof(void*)) + h->datavo = rnd(keysize, sizeof(void*)); + + hash_init(h, h->datavo+valsize, + algarray[keyalg].hash, + algarray[keyalg].equal, + donothing, + hint); + + h->keysize = keysize; + h->valsize = valsize; + h->keyalg = &algarray[keyalg]; + h->valalg = &algarray[valalg]; + + // these calculations are compiler dependent. + // figure out offsets of map call arguments. + h->ko = rnd(sizeof(h), keysize); + h->vo = rnd(h->ko+keysize, valsize); + h->po = rnd(h->vo+valsize, 1); + + ret = h; + FLUSH(&ret); + + if(debug) { + prints("newmap: map="); + sys·printpointer(h); + prints("; keysize="); + sys·printint(keysize); + prints("; valsize="); + sys·printint(valsize); + prints("; keyalg="); + sys·printint(keyalg); + prints("; valalg="); + sys·printint(valalg); + prints("; ko="); + sys·printint(h->ko); + prints("; vo="); + sys·printint(h->vo); + prints("; po="); + sys·printint(h->po); + prints("\n"); + } +} + +// mapaccess1(hmap *map[any]any, key any) (val any); +void +sys·mapaccess1(Hmap *h, ...) +{ + byte *ak, *av; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + + res = nil; + hit = hash_lookup(h, ak, (void**)&res); + if(!hit) + throw("sys·mapaccess1: key not in map"); + h->valalg->copy(h->valsize, av, res+h->datavo); + + if(debug) { + prints("sys·mapaccess1: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapaccess2(hmap *map[any]any, key any) (val any, pres bool); +void +sys·mapaccess2(Hmap *h, ...) +{ + byte *ak, *av, *ap; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + ap = (byte*)&h + h->po; + + res = nil; + hit = hash_lookup(h, ak, (void**)&res); + if(!hit) { + *ap = false; + h->valalg->copy(h->valsize, av, nil); + } else { + *ap = true; + h->valalg->copy(h->valsize, av, res+h->datavo); + } + + if(debug) { + prints("sys·mapaccess2: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("; pres="); + sys·printbool(*ap); + prints("\n"); + } +} + +static void +mapassign(Hmap *h, byte *ak, byte *av) +{ + byte *res; + int32 hit; + + res = nil; + hit = hash_insert(h, ak, (void**)&res); + h->keyalg->copy(h->keysize, res, ak); + h->valalg->copy(h->valsize, res+h->datavo, av); + + if(debug) { + prints("mapassign: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapassign1(hmap *map[any]any, key any, val any); +void +sys·mapassign1(Hmap *h, ...) +{ + byte *ak, *av; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + + mapassign(h, ak, av); +} + +// mapassign2(hmap *map[any]any, key any, val any, pres bool); +void +sys·mapassign2(Hmap *h, ...) +{ + byte *ak, *av, *ap; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + ap = (byte*)&h + h->po; + + if(*ap == true) { + // assign + mapassign(h, ak, av); + return; + } + + // delete + hit = hash_remove(h, ak, (void**)&res); + + if(debug) { + prints("mapassign2: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapiterinit(hmap *map[any]any, hiter *any); +void +sys·mapiterinit(Hmap *h, struct hash_iter *it) +{ + if(h == nil) { + it->data = nil; + return; + } + hash_iter_init(h, it); + it->data = hash_next(it); + if(debug) { + prints("sys·mapiterinit: map="); + sys·printpointer(h); + prints("; iter="); + sys·printpointer(it); + prints("; data="); + sys·printpointer(it->data); + prints("\n"); + } +} + +// mapiternext(hiter *any); +void +sys·mapiternext(struct hash_iter *it) +{ + it->data = hash_next(it); + if(debug) { + prints("sys·mapiternext: iter="); + sys·printpointer(it); + prints("; data="); + sys·printpointer(it->data); + prints("\n"); + } +} + +// mapiter1(hiter *any) (key any); +void +sys·mapiter1(struct hash_iter *it, ...) +{ + Hmap *h; + byte *ak, *res; + + h = it->h; + ak = (byte*)&it + h->ko; + + res = it->data; + if(res == nil) + throw("sys·mapiter2: key:val nil pointer"); + + h->keyalg->copy(h->keysize, ak, res); + + if(debug) { + prints("mapiter2: iter="); + sys·printpointer(it); + prints("; map="); + sys·printpointer(h); + prints("\n"); + } +} + +// mapiter2(hiter *any) (key any, val any); +void +sys·mapiter2(struct hash_iter *it, ...) +{ + Hmap *h; + byte *ak, *av, *res; + + h = it->h; + ak = (byte*)&it + h->ko; + av = (byte*)&it + h->vo; + + res = it->data; + if(res == nil) + throw("sys·mapiter2: key:val nil pointer"); + + h->keyalg->copy(h->keysize, ak, res); + h->valalg->copy(h->valsize, av, res+h->datavo); + + if(debug) { + prints("mapiter2: iter="); + sys·printpointer(it); + prints("; map="); + sys·printpointer(h); + prints("\n"); + } +} diff --git a/src/lib/runtime/hashmap.h b/src/lib/runtime/hashmap.h new file mode 100644 index 000000000..ff93e9ee3 --- /dev/null +++ b/src/lib/runtime/hashmap.h @@ -0,0 +1,161 @@ +// 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. + + +/* A hash table. + Example, hashing nul-terminated char*s: + hash_hash_t str_hash (void *v) { + char *s; + hash_hash_t hash = 0; + for (s = *(char **)v; *s != 0; s++) { + hash = (hash ^ *s) * 2654435769U; + } + return (hash); + } + int str_eq (void *a, void *b) { + return (strcmp (*(char **)a, *(char **)b) == 0); + } + void str_del (void *arg, void *data) { + *(char **)arg = *(char **)data; + } + + struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); + ... 3=> 2**3 entries initial size + ... 12=> 2**12 entries before sprouting sub-tables + ... 15=> number of adjacent probes to attempt before growing + + Example lookup: + char *key = "foobar"; + char **result_ptr; + if (hash_lookup (h, &key, (void **) &result_ptr)) { + printf ("found in table: %s\n", *result_ptr); + } else { + printf ("not found in table\n"); + } + + Example insertion: + char *key = strdup ("foobar"); + char **result_ptr; + if (hash_lookup (h, &key, (void **) &result_ptr)) { + printf ("found in table: %s\n", *result_ptr); + printf ("to overwrite, do *result_ptr = key\n"); + } else { + printf ("not found in table; inserted as %s\n", *result_ptr); + assert (*result_ptr == key); + } + + Example deletion: + char *key = "foobar"; + char *result; + if (hash_remove (h, &key, &result)) { + printf ("key found and deleted from table\n"); + printf ("called str_del (&result, data) to copy data to result: %s\n", result); + } else { + printf ("not found in table\n"); + } + + Example iteration over the elements of *h: + char **data; + struct hash_iter it; + hash_iter_init (h, &it); + for (data = hash_next (&it); data != 0; data = hash_next (&it)) { + printf ("%s\n", *data); + } + */ + +#define malloc mal +#define free(a) USED(a) +#define offsetof(s,m) (uint32)(&(((s*)0)->m)) +#define memset(a,b,c) sys·memclr((byte*)(a), (uint32)(c)) +#define memmove(a,b,c) mmov((byte*)(a),(byte*)(b),(uint32)(c)) +#define memcpy(a,b,c) mcpy((byte*)(a),(byte*)(b),(uint32)(c)) +#define assert(a) if(!(a)) throw("assert") + +struct hash; /* opaque */ +struct hash_subtable; /* opaque */ +struct hash_entry; /* opaque */ + +typedef uintptr uintptr_t; +typedef uintptr_t hash_hash_t; + +struct hash_iter { + uint8* data; /* returned from next */ + int32 elemsize; /* size of elements in table */ + int32 changes; /* number of changes observed last time */ + int32 i; /* stack pointer in subtable_state */ + hash_hash_t last_hash; /* last hash value returned */ + struct hash *h; /* the hash table */ + struct hash_iter_sub { + struct hash_entry *e; /* pointer into subtable */ + struct hash_entry *start; /* start of subtable */ + struct hash_entry *end; /* end of subtable */ + } subtable_state[4]; /* Should be large enough unless the hashing is + so bad that many distinct data values hash + to the same hash value. */ +}; + +/* Return a hashtable h 2**init_power empty entries, each with + "datasize" data bytes. + (*data_hash)(a) should return the hash value of data element *a. + (*data_eq)(a,b) should return whether the data at "a" and the data at "b" + are equal. + (*data_del)(arg, a) will be invoked when data element *a is about to be removed + from the table. "arg" is the argument passed to "hash_remove()". + + Growing is accomplished by resizing if the current tables size is less than + a threshold, and by adding subtables otherwise. hint should be set + the expected maximum size of the table. + "datasize" should be in [sizeof (void*), ..., 255]. If you need a + bigger "datasize", store a pointer to another piece of memory. */ + +//struct hash *hash_new (int32 datasize, +// hash_hash_t (*data_hash) (void *), +// int32 (*data_eq) (void *, void *), +// void (*data_del) (void *, void *), +// int64 hint); + +/* Lookup *data in *h. If the data is found, return 1 and place a pointer to + the found element in *pres. Otherwise return 0 and place 0 in *pres. */ +int32 hash_lookup (struct hash *h, void *data, void **pres); + +/* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) + where p points to the data in the table, then remove it from *h and return + 1. Otherwise return 0. */ +int32 hash_remove (struct hash *h, void *data, void *arg); + +/* Lookup *data in *h. If the data is found, return 1, and place a pointer + to the found element in *pres. Otherwise, return 0, allocate a region + for the data to be inserted, and place a pointer to the inserted element + in *pres; it is the caller's responsibility to copy the data to be + inserted to the pointer returned in *pres in this case. + + If using garbage collection, it is the caller's responsibility to + add references for **pres if HASH_ADDED is returned. */ +int32 hash_insert (struct hash *h, void *data, void **pres); + +/* Return the number of elements in the table. */ +uint32 hash_count (struct hash *h); + +/* The following call is useful only if not using garbage collection on the + table. + Remove all sub-tables associated with *h. + This undoes the effects of hash_init(). + If other memory pointed to by user data must be freed, the caller is + responsible for doiing do by iterating over *h first; see + hash_iter_init()/hash_next(). */ +void hash_destroy (struct hash *h); + +/*----- iteration -----*/ + +/* Initialize *it from *h. */ +void hash_iter_init (struct hash *h, struct hash_iter *it); + +/* Return the next used entry in the table which which *it was initialized. */ +void *hash_next (struct hash_iter *it); + +/*---- test interface ----*/ +/* Call (*data_visit) (arg, level, data) for every data entry in the table, + whether used or not. "level" is the subtable level, 0 means first level. */ +/* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ +void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); diff --git a/src/lib/runtime/iface.c b/src/lib/runtime/iface.c new file mode 100644 index 000000000..6c933b1b2 --- /dev/null +++ b/src/lib/runtime/iface.c @@ -0,0 +1,906 @@ +// 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" + +int32 iface_debug = 0; + +typedef struct Sigt Sigt; +typedef struct Sigi Sigi; +typedef struct Itype Itype; + +/* + * the layout of Iface, Sigt and Sigi are known to the compiler + */ +struct Sigt +{ + byte* name; // name of basic type + Sigt* link; // for linking into hash tables + uint32 thash; // hash of type + uint32 mhash; // hash of methods + uint16 width; // width of base type in bytes + uint16 alg; // algorithm + // note: on amd64 there is a 32-bit pad here. + struct { + byte* fname; + uint32 fhash; // hash of type + uint32 offset; // offset of substruct + void (*fun)(void); + } meth[1]; // one or more - last name is nil +}; + +struct Sigi +{ + byte* name; + uint32 hash; + uint32 size; // number of methods + struct { + byte* fname; + uint32 fhash; + uint32 perm; // location of fun in Sigt + } meth[1]; // [size+1] - last name is nil +}; + +struct Itype +{ + Sigi* sigi; + Sigt* sigt; + Itype* link; + int32 bad; + int32 unused; + void (*fun[])(void); +}; + +static Iface niliface; +static Eface nileface; + +static Itype* hash[1009]; +static Lock ifacelock; + +Sigi sigi·empty[2] = { (byte*)"interface { }" }; + +static void +printsigi(Sigi *si) +{ + int32 i; + byte *name; + + sys·printpointer(si); + prints("{"); + prints((int8*)si->name); + prints(":"); + for(i=0;; i++) { + name = si->meth[i].fname; + if(name == nil) + break; + prints("["); + sys·printint(i); + prints("]\""); + prints((int8*)name); + prints("\""); + sys·printint(si->meth[i].fhash%999); + prints("/"); + sys·printint(si->meth[i].perm); + } + prints("}"); +} + +static void +printsigt(Sigt *st) +{ + int32 i; + byte *name; + + sys·printpointer(st); + prints("{"); + prints((int8*)st->name); + prints(":"); + sys·printint(st->thash%999); // type hash + prints(","); + sys·printint(st->mhash%999); // method hash + prints(","); + sys·printint(st->width); // width + prints(","); + sys·printint(st->alg); // algorithm + for(i=0;; i++) { + name = st->meth[i].fname; + if(name == nil) + break; + prints("["); + sys·printint(i); + prints("]\""); + prints((int8*)name); + prints("\""); + sys·printint(st->meth[i].fhash%999); + prints("/"); + sys·printint(st->meth[i].offset); + prints("/"); + sys·printpointer(st->meth[i].fun); + } + prints("}"); +} + +static void +printiface(Iface i) +{ + prints("("); + sys·printpointer(i.type); + prints(","); + sys·printpointer(i.data); + prints(")"); +} + +static void +printeface(Eface e) +{ + prints("("); + sys·printpointer(e.type); + prints(","); + sys·printpointer(e.data); + prints(")"); +} + +static Itype* +itype(Sigi *si, Sigt *st, int32 canfail) +{ + int32 locked; + int32 nt, ni; + uint32 ihash, h; + byte *sname, *iname; + Itype *m; + + if(si->size == 0) + throw("internal error - misuse of itype"); + + // easy case + if(st->meth[0].fname == nil) { + if(canfail) + return nil; + iname = si->meth[0].fname; + goto throw1; + } + + // compiler has provided some good hash codes for us. + h = 0; + if(si) + h += si->hash; + if(st) { + h += st->thash; + h += st->mhash; + } + + h %= nelem(hash); + + // look twice - once without lock, once with. + // common case will be no lock contention. + for(locked=0; locked<2; locked++) { + if(locked) + lock(&ifacelock); + for(m=hash[h]; m!=nil; m=m->link) { + if(m->sigi == si && m->sigt == st) { + if(m->bad) { + m = nil; + if(!canfail) { + // this can only happen if the conversion + // was already done once using the , ok form + // and we have a cached negative result. + // the cached result doesn't record which + // interface function was missing, so jump + // down to the interface check, which will + // give a better error. + goto throw; + } + } + if(locked) + unlock(&ifacelock); + return m; + } + } + } + + ni = si->size; + m = malloc(sizeof(*m) + ni*sizeof(m->fun[0])); + m->sigi = si; + m->sigt = st; + +throw: + nt = 0; + for(ni=0;; ni++) { + iname = si->meth[ni].fname; + if(iname == nil) + break; + + // pick up next name from + // interface signature + ihash = si->meth[ni].fhash; + + for(;; nt++) { + // pick up and compare next name + // from structure signature + sname = st->meth[nt].fname; + if(sname == nil) { + if(!canfail) { + throw1: + printf("cannot convert type %s to interface %s: missing method %s\n", + st->name, si->name, iname); + if(iface_debug) { + prints("interface"); + printsigi(si); + prints("\ntype"); + printsigt(st); + prints("\n"); + } + throw("interface conversion"); + return nil; // not reached + } + m->bad = 1; + m->link = hash[h]; + hash[h] = m; + if(locked) + unlock(&ifacelock); + return nil; + } + if(ihash == st->meth[nt].fhash && strcmp(sname, iname) == 0) + break; + } + m->fun[si->meth[ni].perm] = st->meth[nt].fun; + } + m->link = hash[h]; + hash[h] = m; + if(locked) + unlock(&ifacelock); + + return m; +} + +static void +copyin(Sigt *st, void *src, void **dst) +{ + int32 wid, alg; + void *p; + + wid = st->width; + alg = st->alg; + + if(wid <= sizeof(*dst)) + algarray[alg].copy(wid, dst, src); + else { + p = mal(wid); + algarray[alg].copy(wid, p, src); + *dst = p; + } +} + +static void +copyout(Sigt *st, void **src, void *dst) +{ + int32 wid, alg; + + wid = st->width; + alg = st->alg; + + if(wid <= sizeof(*src)) + algarray[alg].copy(wid, dst, src); + else + algarray[alg].copy(wid, dst, *src); +} + +// ifaceT2I(sigi *byte, sigt *byte, elem any) (ret any); +#pragma textflag 7 +void +sys·ifaceT2I(Sigi *si, Sigt *st, ...) +{ + byte *elem; + Iface *ret; + int32 wid; + + elem = (byte*)(&st+1); + wid = st->width; + ret = (Iface*)(elem + rnd(wid, sizeof(uintptr))); + + ret->type = itype(si, st, 0); + copyin(st, elem, &ret->data); +} + +// ifaceT2E(sigt *byte, elem any) (ret any); +#pragma textflag 7 +void +sys·ifaceT2E(Sigt *st, ...) +{ + byte *elem; + Eface *ret; + int32 wid; + + elem = (byte*)(&st+1); + wid = st->width; + ret = (Eface*)(elem + rnd(wid, sizeof(uintptr))); + + ret->type = st; + copyin(st, elem, &ret->data); +} + +// ifaceI2T(sigt *byte, iface any) (ret any); +#pragma textflag 7 +void +sys·ifaceI2T(Sigt *st, Iface i, ...) +{ + Itype *im; + byte *ret; + + ret = (byte*)(&i+1); + + im = i.type; + if(im == nil) { + printf("interface is nil, not %s\n", st->name); + throw("interface conversion"); + } + if(im->sigt != st) { + printf("%s is %s, not %s\n", im->sigi->name, im->sigt->name, st->name); + throw("interface conversion"); + } + copyout(st, &i.data, ret); +} + +// ifaceI2T2(sigt *byte, iface any) (ret any, ok bool); +#pragma textflag 7 +void +sys·ifaceI2T2(Sigt *st, Iface i, ...) +{ + byte *ret; + bool *ok; + Itype *im; + int32 wid; + + ret = (byte*)(&i+1); + wid = st->width; + ok = (bool*)(ret+rnd(wid, 1)); + + im = i.type; + if(im == nil || im->sigt != st) { + *ok = false; + sys·memclr(ret, wid); + return; + } + + *ok = true; + copyout(st, &i.data, ret); +} + +// ifaceE2T(sigt *byte, iface any) (ret any); +#pragma textflag 7 +void +sys·ifaceE2T(Sigt *st, Eface e, ...) +{ + Sigt *t; + byte *ret; + + ret = (byte*)(&e+1); + + t = e.type; + if(t == nil) { + printf("interface is nil, not %s\n", st->name); + throw("interface conversion"); + } + if(t != st) { + printf("interface is %s, not %s\n", t->name, st->name); + throw("interface conversion"); + } + copyout(st, &e.data, ret); +} + +// ifaceE2T2(sigt *byte, iface any) (ret any, ok bool); +#pragma textflag 7 +void +sys·ifaceE2T2(Sigt *st, Eface e, ...) +{ + byte *ret; + bool *ok; + Sigt *t; + int32 wid; + + ret = (byte*)(&e+1); + wid = st->width; + ok = (bool*)(ret+rnd(wid, 1)); + + t = e.type; + if(t != st) { + *ok = false; + sys·memclr(ret, wid); + return; + } + + *ok = true; + copyout(st, &e.data, ret); +} + +// ifaceI2E(sigi *byte, iface any) (ret any); +// TODO(rsc): Move to back end, throw away function. +void +sys·ifaceI2E(Iface i, Eface ret) +{ + Itype *im; + + ret.data = i.data; + im = i.type; + if(im == nil) + ret.type = nil; + else + ret.type = im->sigt; + FLUSH(&ret); +} + +// ifaceI2I(sigi *byte, iface any) (ret any); +// called only for implicit (no type assertion) conversions +void +sys·ifaceI2I(Sigi *si, Iface i, Iface ret) +{ + Itype *im; + + im = i.type; + if(im == nil) { + // If incoming interface is uninitialized (zeroed) + // make the outgoing interface zeroed as well. + ret = niliface; + } else { + ret = i; + if(im->sigi != si) + ret.type = itype(si, im->sigt, 0); + } + + FLUSH(&ret); +} + +// ifaceI2Ix(sigi *byte, iface any) (ret any); +// called only for explicit conversions (with type assertion). +void +sys·ifaceI2Ix(Sigi *si, Iface i, Iface ret) +{ + Itype *im; + + im = i.type; + if(im == nil) { + // explicit conversions require non-nil interface value. + printf("interface is nil, not %s\n", si->name); + throw("interface conversion"); + } else { + ret = i; + if(im->sigi != si) + ret.type = itype(si, im->sigt, 0); + } + + FLUSH(&ret); +} + +// ifaceI2I2(sigi *byte, iface any) (ret any, ok bool); +void +sys·ifaceI2I2(Sigi *si, Iface i, Iface ret, bool ok) +{ + Itype *im; + + im = i.type; + if(im == nil) { + // If incoming interface is nil, the conversion fails. + ret = niliface; + ok = false; + } else { + ret = i; + ok = true; + if(im->sigi != si) { + ret.type = itype(si, im->sigt, 1); + if(ret.type == nil) { + ret = niliface; + ok = false; + } + } + } + + FLUSH(&ret); + FLUSH(&ok); +} + +// ifaceE2I(sigi *byte, iface any) (ret any); +// Called only for explicit conversions (with type assertion). +void +sys·ifaceE2I(Sigi *si, Eface e, Iface ret) +{ + Sigt *t; + + t = e.type; + if(t == nil) { + // explicit conversions require non-nil interface value. + printf("interface is nil, not %s\n", si->name); + throw("interface conversion"); + } else { + ret.data = e.data; + ret.type = itype(si, t, 0); + } + FLUSH(&ret); +} + +// ifaceE2I2(sigi *byte, iface any) (ret any, ok bool); +void +sys·ifaceE2I2(Sigi *si, Eface e, Iface ret, bool ok) +{ + Sigt *t; + + t = e.type; + ok = true; + if(t == nil) { + // If incoming interface is nil, the conversion fails. + ret = niliface; + ok = false; + } else { + ret.data = e.data; + ret.type = itype(si, t, 1); + if(ret.type == nil) { + ret = niliface; + ok = false; + } + } + FLUSH(&ret); + FLUSH(&ok); +} + +static uintptr +ifacehash1(void *data, Sigt *sigt) +{ + int32 alg, wid; + + if(sigt == nil) + return 0; + + alg = sigt->alg; + wid = sigt->width; + if(algarray[alg].hash == nohash) { + // calling nohash will throw too, + // but we can print a better error. + printf("hash of unhashable type %s\n", sigt->name); + if(alg == AFAKE) + throw("fake interface hash"); + throw("interface hash"); + } + if(wid <= sizeof(data)) + return algarray[alg].hash(wid, &data); + return algarray[alg].hash(wid, data); +} + +uintptr +ifacehash(Iface a) +{ + if(a.type == nil) + return 0; + return ifacehash1(a.data, a.type->sigt); +} + +uintptr +efacehash(Eface a) +{ + return ifacehash1(a.data, a.type); +} + +static bool +ifaceeq1(void *data1, void *data2, Sigt *sigt) +{ + int32 alg, wid; + + alg = sigt->alg; + wid = sigt->width; + + if(algarray[alg].equal == noequal) { + // calling noequal will throw too, + // but we can print a better error. + printf("comparing uncomparable type %s\n", sigt->name); + if(alg == AFAKE) + throw("fake interface compare"); + throw("interface compare"); + } + + if(wid <= sizeof(data1)) + return algarray[alg].equal(wid, &data1, &data2); + return algarray[alg].equal(wid, data1, data2); +} + +bool +ifaceeq(Iface i1, Iface i2) +{ + if(i1.type != i2.type) + return false; + if(i1.type == nil) + return true; + return ifaceeq1(i1.data, i2.data, i1.type->sigt); +} + +bool +efaceeq(Eface e1, Eface e2) +{ + if(e1.type != e2.type) + return false; + if(e1.type == nil) + return true; + return ifaceeq1(e1.data, e2.data, e1.type); +} + +// ifaceeq(i1 any, i2 any) (ret bool); +void +sys·ifaceeq(Iface i1, Iface i2, bool ret) +{ + ret = ifaceeq(i1, i2); + FLUSH(&ret); +} + +// efaceeq(i1 any, i2 any) (ret bool) +void +sys·efaceeq(Eface e1, Eface e2, bool ret) +{ + ret = efaceeq(e1, e2); + FLUSH(&ret); +} + +// ifacethash(i1 any) (ret uint32); +void +sys·ifacethash(Iface i1, uint32 ret) +{ + Itype *im; + Sigt *st; + + ret = 0; + im = i1.type; + if(im != nil) { + st = im->sigt; + if(st != nil) + ret = st->thash; + } + FLUSH(&ret); +} + +// efacethash(e1 any) (ret uint32) +void +sys·efacethash(Eface e1, uint32 ret) +{ + Sigt *st; + + ret = 0; + st = e1.type; + if(st != nil) + ret = st->thash; + FLUSH(&ret); +} + +void +sys·printiface(Iface i) +{ + printiface(i); +} + +void +sys·printeface(Eface e) +{ + printeface(e); +} + +void +unsafe·Reflect(Eface i, uint64 retit, String rettype, bool retindir) +{ + int32 wid; + + if(i.type == nil) { + retit = 0; + rettype = emptystring; + retindir = false; + } else { + retit = (uint64)i.data; + rettype = gostring(i.type->name); + wid = i.type->width; + retindir = wid > sizeof(i.data); + } + FLUSH(&retit); + FLUSH(&rettype); + FLUSH(&retindir); +} + +extern Sigt *gotypesigs[]; +extern int32 ngotypesigs; + + +// The reflection library can ask to unreflect on a type +// that has never been used, so we don't have a signature for it. +// For concreteness, suppose a program does +// +// type T struct{ x []int } +// var t T; +// v := reflect.NewValue(v); +// vv := v.Field(0); +// if s, ok := vv.Interface().(string) { +// print("first field is string"); +// } +// +// vv.Interface() returns the result of sys.Unreflect with +// a typestring of "[]int". If []int is not used with interfaces +// in the rest of the program, there will be no signature in gotypesigs +// for "[]int", so we have to invent one. The requirements +// on the fake signature are: +// +// (1) any interface conversion using the signature will fail +// (2) calling unsafe.Reflect() returns the args to unreflect +// (3) the right algorithm type is used, for == and map insertion +// +// (1) is ensured by the fact that we allocate a new Sigt, +// so it will necessarily be != any Sigt in gotypesigs. +// (2) is ensured by storing the type string in the signature +// and setting the width to force the correct value of the bool indir. +// (3) is ensured by sniffing the type string. +// +// Note that (1) is correct behavior: if the program had tested +// for .([]int) instead of .(string) above, then there would be a +// signature with type string "[]int" in gotypesigs, and unreflect +// wouldn't call fakesigt. + +static Sigt* fake[1009]; +static int32 nfake; + +enum +{ + SizeofInt = 4, + SizeofFloat = 4, +}; + +// Table of prefixes of names of comparable types. +static struct { + int8 *s; + int8 n; + int8 alg; + int8 w; +} cmp[] = +{ + // basic types + "int", 3+1, AMEM, SizeofInt, // +1 is NUL + "uint", 4+1, AMEM, SizeofInt, + "int8", 4+1, AMEM, 1, + "uint8", 5+1, AMEM, 1, + "int16", 5+1, AMEM, 2, + "uint16", 6+1, AMEM, 2, + "int32", 5+1, AMEM, 4, + "uint32", 6+1, AMEM, 4, + "int64", 5+1, AMEM, 8, + "uint64", 6+1, AMEM, 8, + "uintptr", 7+1, AMEM, sizeof(uintptr), + "float", 5+1, AMEM, SizeofFloat, + "float32", 7+1, AMEM, 4, + "float64", 7+1, AMEM, 8, + "bool", 4+1, AMEM, sizeof(bool), + + // string compare is special + "string", 6+1, ASTRING, sizeof(String), + + // generic types, identified by prefix + "*", 1, AMEM, sizeof(uintptr), + "chan ", 5, AMEM, sizeof(uintptr), + "func(", 5, AMEM, sizeof(uintptr), + "map[", 4, AMEM, sizeof(uintptr), +}; + +static Sigt* +fakesigt(String type, bool indir) +{ + Sigt *sigt; + uint32 h; + int32 i, locked; + + h = 0; + for(i=0; i<type.len; i++) + h = h*37 + type.str[i]; + h += indir; + h %= nelem(fake); + + for(locked=0; locked<2; locked++) { + if(locked) + lock(&ifacelock); + for(sigt = fake[h]; sigt != nil; sigt = sigt->link) { + // don't need to compare indir. + // same type string but different indir will have + // different hashes. + if(mcmp(sigt->name, type.str, type.len) == 0) + if(sigt->name[type.len] == '\0') { + if(locked) + unlock(&ifacelock); + return sigt; + } + } + } + + sigt = malloc(sizeof(*sigt)); + sigt->name = malloc(type.len + 1); + mcpy(sigt->name, type.str, type.len); + + sigt->alg = AFAKE; + sigt->width = 1; // small width + if(indir) + sigt->width = 2*sizeof(niliface.data); // big width + + // AFAKE is like ANOEQ; check whether the type + // should have a more capable algorithm. + for(i=0; i<nelem(cmp); i++) { + if(mcmp((byte*)sigt->name, (byte*)cmp[i].s, cmp[i].n) == 0) { + sigt->alg = cmp[i].alg; + sigt->width = cmp[i].w; + break; + } + } + + sigt->link = fake[h]; + fake[h] = sigt; + + unlock(&ifacelock); + return sigt; +} + +static int32 +cmpstringchars(String a, uint8 *b) +{ + int32 i; + byte c1, c2; + + for(i=0;; i++) { + c1 = 0; + if(i < a.len) + c1 = a.str[i]; + c2 = b[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + if(c1 == 0) + return 0; + } +} + +static Sigt* +findtype(String type, bool indir) +{ + int32 i, lo, hi, m; + + lo = 0; + hi = ngotypesigs; + while(lo < hi) { + m = lo + (hi - lo)/2; + i = cmpstringchars(type, gotypesigs[m]->name); + if(i == 0) + return gotypesigs[m]; + if(i < 0) + hi = m; + else + lo = m+1; + } + return fakesigt(type, indir); +} + + +void +unsafe·Unreflect(uint64 it, String type, bool indir, Eface ret) +{ + Sigt *sigt; + + ret = nileface; + + if(cmpstring(type, emptystring) == 0) + goto out; + + if(type.len > 10 && mcmp(type.str, (byte*)"interface ", 10) == 0) { + printf("unsafe.Unreflect: cannot put %S in interface\n", type); + throw("unsafe.Unreflect"); + } + + // if we think the type should be indirect + // and caller does not, play it safe, return nil. + sigt = findtype(type, indir); + if(indir != (sigt->width > sizeof(ret.data))) + goto out; + + ret.type = sigt; + ret.data = (void*)it; + +out: + FLUSH(&ret); +} + diff --git a/src/lib/runtime/linux/386/defs.h b/src/lib/runtime/linux/386/defs.h new file mode 100755 index 000000000..112fc7b09 --- /dev/null +++ b/src/lib/runtime/linux/386/defs.h @@ -0,0 +1,136 @@ +// godefs -f -m32 -f -I/home/rsc/pub/linux-2.6/arch/x86/include -f -I/home/rsc/pub/linux-2.6/include defs2.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_RESTORER = 0x4000000, + SA_SIGINFO = 0x4, +}; + +// Types +#pragma pack on + +typedef struct Fpreg Fpreg; +struct Fpreg { + uint16 significand[4]; + uint16 exponent; +}; + +typedef struct Fpxreg Fpxreg; +struct Fpxreg { + uint16 significand[4]; + uint16 exponent; + uint16 padding[3]; +}; + +typedef struct Xmmreg Xmmreg; +struct Xmmreg { + uint32 element[4]; +}; + +typedef struct Fpstate Fpstate; +struct Fpstate { + uint32 cw; + uint32 sw; + uint32 tag; + uint32 ipoff; + uint32 cssel; + uint32 dataoff; + uint32 datasel; + Fpreg _st[8]; + uint16 status; + uint16 magic; + uint32 _fxsr_env[6]; + uint32 mxcsr; + uint32 reserved; + Fpxreg _fxsr_st[8]; + Xmmreg _xmm[8]; + uint32 padding1[44]; + byte _anon_[48]; +}; + +typedef struct Timespec Timespec; +struct Timespec { + int32 tv_sec; + int32 tv_nsec; +}; + +typedef struct Timeval Timeval; +struct Timeval { + int32 tv_sec; + int32 tv_usec; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + byte _u[4]; + uint32 sa_mask; + uint32 sa_flags; + void *sa_restorer; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + byte _sifields[116]; +}; + +typedef struct Sigaltstack Sigaltstack; +struct Sigaltstack { + void *ss_sp; + int32 ss_flags; + uint32 ss_size; +}; + +typedef struct Sigcontext Sigcontext; +struct Sigcontext { + uint16 gs; + uint16 __gsh; + uint16 fs; + uint16 __fsh; + uint16 es; + uint16 __esh; + uint16 ds; + uint16 __dsh; + uint32 edi; + uint32 esi; + uint32 ebp; + uint32 esp; + uint32 ebx; + uint32 edx; + uint32 ecx; + uint32 eax; + uint32 trapno; + uint32 err; + uint32 eip; + uint16 cs; + uint16 __csh; + uint32 eflags; + uint32 esp_at_signal; + uint16 ss; + uint16 __ssh; + Fpstate *fpstate; + uint32 oldmask; + uint32 cr2; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + uint32 uc_flags; + Ucontext *uc_link; + Sigaltstack uc_stack; + Sigcontext uc_mcontext; + uint32 uc_sigmask; +}; +#pragma pack off diff --git a/src/lib/runtime/linux/386/rt0.s b/src/lib/runtime/linux/386/rt0.s new file mode 100755 index 000000000..7717c37e8 --- /dev/null +++ b/src/lib/runtime/linux/386/rt0.s @@ -0,0 +1,8 @@ +// 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_386_linux(SB),7,$0 + JMP _rt0_386(SB) diff --git a/src/lib/runtime/linux/386/signal.c b/src/lib/runtime/linux/386/signal.c new file mode 100644 index 000000000..7dfca6bb4 --- /dev/null +++ b/src/lib/runtime/linux/386/signal.c @@ -0,0 +1,102 @@ +// 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" +#include "defs.h" +#include "signals.h" +#include "os.h" + +void +dumpregs(Sigcontext *r) +{ + printf("eax %X\n", r->eax); + printf("ebx %X\n", r->ebx); + printf("ecx %X\n", r->ecx); + printf("edx %X\n", r->edx); + printf("edi %X\n", r->edi); + printf("esi %X\n", r->esi); + printf("ebp %X\n", r->ebp); + printf("esp %X\n", r->esp); + printf("eip %X\n", r->eip); + printf("eflags %X\n", r->eflags); + printf("cs %X\n", r->cs); + printf("fs %X\n", r->fs); + printf("gs %X\n", r->gs); +} + +/* + * This assembler routine takes the args from registers, puts them on the stack, + * and calls sighandler(). + */ +extern void sigtramp(void); +extern void sigignore(void); // just returns +extern void sigreturn(void); // calls sigreturn + +void +sighandler(int32 sig, Siginfo* info, void* context) +{ + Ucontext *uc; + Sigcontext *sc; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + uc = context; + sc = &uc->uc_mcontext; + + if(sig < 0 || sig >= NSIG) + printf("Signal %d\n", sig); + else + printf("%s\n", sigtab[sig].name); + + printf("Faulting address: %p\n", *(void**)info->_sifields); + printf("pc=%X\n", sc->eip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)sc->eip, (void*)sc->esp, m->curg); + tracebackothers(m->curg); + dumpregs(sc); + } + + breakpoint(); + exit(2); +} + +void +signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + static Sigaction sa; + + int32 i; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; + sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL; + sa.sa_restorer = (void*)sigreturn; + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) + *(void**)sa._u = (void*)sigtramp; // handler + else + *(void**)sa._u = (void*)sigignore; // handler + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + rt_sigaction(i, &sa, nil, 8); + } + } +} + diff --git a/src/lib/runtime/linux/386/sys.s b/src/lib/runtime/linux/386/sys.s new file mode 100755 index 000000000..419973a5c --- /dev/null +++ b/src/lib/runtime/linux/386/sys.s @@ -0,0 +1,222 @@ +// 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. + +// +// System calls and other sys.stuff for 386, Linux +// + +TEXT syscall(SB),7,$0 + MOVL 4(SP), AX // syscall number + MOVL 8(SP), BX // arg1 + MOVL 12(SP), CX // arg2 + MOVL 16(SP), DX // arg3 + MOVL 20(SP), SI // arg4 + MOVL 24(SP), DI // arg5 + MOVL 28(SP), BP // arg6 + INT $0x80 + CMPL AX, $0xfffff001 + JLS 2(PC) + INT $3 // not reached + RET + +TEXT exit(SB),7,$0 + MOVL $252, AX // syscall number + MOVL 4(SP), BX + INT $0x80 + INT $3 // not reached + RET + +TEXT exit1(SB),7,$0 + MOVL $1, AX // exit - exit the current os thread + MOVL 4(SP), BX + INT $0x80 + INT $3 // not reached + RET + +TEXT write(SB),7,$0 + MOVL $4, AX // syscall - write + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + INT $0x80 + RET + +TEXT getpid(SB),7,$0 + MOVL $20, AX + INT $0x80 + RET + +TEXT kill(SB),7,$0 + MOVL $37, AX + MOVL 4(SP), BX + MOVL 8(SP), CX + INT $0x80 + RET + +TEXT sys·write(SB),7,$0 + MOVL $4, AX // syscall - write + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + INT $0x80 + RET + +TEXT rt_sigaction(SB),7,$0 + MOVL $174, AX // syscall - rt_sigaction + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + INT $0x80 + RET + +TEXT sigtramp(SB),7,$0 + MOVL 4(FS), BP // m + MOVL 20(BP), AX // m->gsignal + MOVL AX, 0(FS) // g = m->gsignal + JMP sighandler(SB) + +TEXT sigignore(SB),7,$0 + RET + +TEXT sigreturn(SB),7,$0 + MOVL 4(FS), BP // m + MOVL 32(BP), BP // m->curg + MOVL BP, 0(FS) // g = m->curg + MOVL $173, AX // rt_sigreturn + INT $0x80 + INT $3 // not reached + RET + +TEXT sys·mmap(SB),7,$0 + MOVL $192, AX // mmap2 + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + MOVL 20(SP), DI + MOVL 24(SP), BP + SHRL $12, BP + INT $0x80 + CMPL AX, $0xfffff001 + JLS 2(PC) + INT $3 + RET + +// int64 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT futex(SB),7,$0 + MOVL $240, AX // futex + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + MOVL 20(SP), DI + MOVL 24(SP), BP + INT $0x80 + RET + +// int64 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +TEXT clone(SB),7,$0 + MOVL $120, AX // clone + MOVL flags+4(SP), BX + MOVL stack+8(SP), CX + + // Copy m, g, fn off parent stack for use by child. + SUBL $12, CX + MOVL m+12(SP), DX + MOVL DX, 0(CX) + MOVL g+16(SP), DX + MOVL DX, 4(CX) + MOVL fn+20(SP), DX + MOVL DX, 8(CX) + + MOVL $120, AX + INT $0x80 + + // In parent, return. + CMPL AX, $0 + JEQ 2(PC) + RET + + // In child, set up new stack, etc. + MOVL 0(CX), BX // m + MOVL 12(AX), AX // fs (= m->cret) + MOVW AX, FS + MOVL 8(CX), DX // fn + ADDL $12, CX + MOVL CX, SP + + // fn is now on top of stack. + + // initialize m->procid to Linux tid + MOVL $224, AX + INT $0x80 + MOVL AX, 20(BX) + + // call fn + CALL DX + + // It shouldn't return; if it does, exit. + MOVL $111, DI + MOVL $1, AX + INT $0x80 + JMP -3(PC) // keep exiting + +TEXT sigaltstack(SB),7,$-8 + MOVL $186, AX // sigaltstack + MOVL new+4(SP), BX + MOVL old+8(SP), CX + INT $0x80 + CMPL AX, $0xfffff001 + JLS 2(PC) + INT $3 + RET + +// // fake the per-goroutine and per-mach registers +// LEAL m0(SB), + +// TODO(rsc): move to linux.s +// <asm-i386/ldt.h> +// struct user_desc { +// unsigned int entry_number; +// unsigned long base_addr; +// unsigned int limit; +// unsigned int seg_32bit:1; +// unsigned int contents:2; +// unsigned int read_exec_only:1; +// unsigned int limit_in_pages:1; +// unsigned int seg_not_present:1; +// unsigned int useable:1; +// }; +#define SEG_32BIT 0x01 +// contents are the 2 bits 0x02 and 0x04. +#define CONTENTS_DATA 0x00 +#define CONTENTS_STACK 0x02 +#define CONTENTS_CODE 0x04 +#define READ_EXEC_ONLY 0x08 +#define LIMIT_IN_PAGES 0x10 +#define SEG_NOT_PRESENT 0x20 +#define USEABLE 0x40 + +// setldt(int entry, int address, int limit) +TEXT setldt(SB),7,$32 + // set up user_desc + LEAL 16(SP), AX // struct user_desc + MOVL entry+0(FP), BX // entry + MOVL BX, 0(AX) + MOVL address+4(FP), BX // base address + MOVL BX, 4(AX) + MOVL limit+8(FP), BX // limit + MOVL BX, 8(AX) + MOVL $(SEG_32BIT|USEABLE|CONTENTS_DATA), 12(AX) // flag bits + + // call modify_ldt + MOVL $123, 0(SP) // syscall - modify_ldt + MOVL $1, 4(SP) // func = 1 (write) + MOVL AX, 8(SP) // user_desc + MOVL $16, 12(SP) // sizeof(user_desc) + CALL syscall(SB) + RET + diff --git a/src/lib/runtime/linux/amd64/defs.h b/src/lib/runtime/linux/amd64/defs.h new file mode 100644 index 000000000..43b047523 --- /dev/null +++ b/src/lib/runtime/linux/amd64/defs.h @@ -0,0 +1,175 @@ +// godefs -f -m64 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_RESTORER = 0x4000000, + SA_SIGINFO = 0x4, +}; + +// Types +#pragma pack on + +typedef struct Timespec Timespec; +struct Timespec { + int64 tv_sec; + int64 tv_nsec; +}; + +typedef struct Timeval Timeval; +struct Timeval { + int64 tv_sec; + int64 tv_usec; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + void *sa_handler; + uint64 sa_flags; + void *sa_restorer; + uint64 sa_mask; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + byte pad0[4]; + byte _sifields[112]; +}; +#pragma pack off +// godefs -f -m64 defs1.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants + +// Types +#pragma pack on + +typedef struct Usigset Usigset; +struct Usigset { + uint64 __val[16]; +}; + +typedef struct Fpxreg Fpxreg; +struct Fpxreg { + uint16 significand[4]; + uint16 exponent; + uint16 padding[3]; +}; + +typedef struct Xmmreg Xmmreg; +struct Xmmreg { + uint32 element[4]; +}; + +typedef struct Fpstate Fpstate; +struct Fpstate { + uint16 cwd; + uint16 swd; + uint16 ftw; + uint16 fop; + uint64 rip; + uint64 rdp; + uint32 mxcsr; + uint32 mxcr_mask; + Fpxreg _st[8]; + Xmmreg _xmm[16]; + uint32 padding[24]; +}; + +typedef struct Fpxreg1 Fpxreg1; +struct Fpxreg1 { + uint16 significand[4]; + uint16 exponent; + uint16 padding[3]; +}; + +typedef struct Xmmreg1 Xmmreg1; +struct Xmmreg1 { + uint32 element[4]; +}; + +typedef struct Fpstate1 Fpstate1; +struct Fpstate1 { + uint16 cwd; + uint16 swd; + uint16 ftw; + uint16 fop; + uint64 rip; + uint64 rdp; + uint32 mxcsr; + uint32 mxcr_mask; + Fpxreg1 _st[8]; + Xmmreg1 _xmm[16]; + uint32 padding[24]; +}; + +typedef struct Sigaltstack Sigaltstack; +struct Sigaltstack { + void *ss_sp; + int32 ss_flags; + byte pad0[4]; + uint64 ss_size; +}; + +typedef struct Mcontext Mcontext; +struct Mcontext { + int64 gregs[23]; + Fpstate *fpregs; + uint64 __reserved1[8]; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + uint64 uc_flags; + Ucontext *uc_link; + Sigaltstack uc_stack; + Mcontext uc_mcontext; + Usigset uc_sigmask; + Fpstate __fpregs_mem; +}; + +typedef struct Sigcontext Sigcontext; +struct Sigcontext { + uint64 r8; + uint64 r9; + uint64 r10; + uint64 r11; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; + uint64 rdi; + uint64 rsi; + uint64 rbp; + uint64 rbx; + uint64 rdx; + uint64 rax; + uint64 rcx; + uint64 rsp; + uint64 rip; + uint64 eflags; + uint16 cs; + uint16 gs; + uint16 fs; + uint16 __pad0; + uint64 err; + uint64 trapno; + uint64 oldmask; + uint64 cr2; + Fpstate1 *fpstate; + uint64 __reserved1[8]; +}; +#pragma pack off diff --git a/src/lib/runtime/linux/amd64/rt0.s b/src/lib/runtime/linux/amd64/rt0.s new file mode 100644 index 000000000..55be5bcee --- /dev/null +++ b/src/lib/runtime/linux/amd64/rt0.s @@ -0,0 +1,9 @@ +// 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_amd64_linux(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/lib/runtime/linux/amd64/signal.c b/src/lib/runtime/linux/amd64/signal.c new file mode 100644 index 000000000..55215176d --- /dev/null +++ b/src/lib/runtime/linux/amd64/signal.c @@ -0,0 +1,112 @@ +// 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" +#include "defs.h" +#include "signals.h" +#include "os.h" + +void +dumpregs(Sigcontext *r) +{ + printf("rax %X\n", r->rax); + printf("rbx %X\n", r->rbx); + printf("rcx %X\n", r->rcx); + printf("rdx %X\n", r->rdx); + printf("rdi %X\n", r->rdi); + printf("rsi %X\n", r->rsi); + printf("rbp %X\n", r->rbp); + printf("rsp %X\n", r->rsp); + printf("r8 %X\n", r->r8 ); + printf("r9 %X\n", r->r9 ); + printf("r10 %X\n", r->r10); + printf("r11 %X\n", r->r11); + printf("r12 %X\n", r->r12); + printf("r13 %X\n", r->r13); + printf("r14 %X\n", r->r14); + printf("r15 %X\n", r->r15); + printf("rip %X\n", r->rip); + printf("rflags %X\n", r->eflags); + printf("cs %X\n", (uint64)r->cs); + printf("fs %X\n", (uint64)r->fs); + printf("gs %X\n", (uint64)r->gs); +} + +/* + * This assembler routine takes the args from registers, puts them on the stack, + * and calls sighandler(). + */ +extern void sigtramp(void); +extern void sigignore(void); // just returns +extern void sigreturn(void); // calls sigreturn + +void +sighandler(int32 sig, Siginfo* info, void* context) +{ + Ucontext *uc; + Mcontext *mc; + Sigcontext *sc; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + uc = context; + mc = &uc->uc_mcontext; + sc = (Sigcontext*)mc; // same layout, more conveient names + + if(sig < 0 || sig >= NSIG) + printf("Signal %d\n", sig); + else + printf("%s\n", sigtab[sig].name); + + printf("Faulting address: %p\n", *(void**)info->_sifields); + printf("PC=%X\n", sc->rip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)sc->rip, (void*)sc->rsp, (void*)sc->r15); + tracebackothers((void*)sc->r15); + dumpregs(sc); + } + + breakpoint(); + exit(2); +} + +void +signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + static Sigaction sa; + + int32 i; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; + sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL; + sa.sa_restorer = (void*)sigreturn; + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) + sa.sa_handler = (void*)sigtramp; + else + sa.sa_handler = (void*)sigignore; + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + rt_sigaction(i, &sa, nil, 8); + } + } +} + diff --git a/src/lib/runtime/linux/amd64/sys.s b/src/lib/runtime/linux/amd64/sys.s new file mode 100644 index 000000000..f90c704fa --- /dev/null +++ b/src/lib/runtime/linux/amd64/sys.s @@ -0,0 +1,193 @@ +// 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. + +// +// System calls and other sys.stuff for AMD64, Linux +// + +TEXT exit(SB),7,$0-8 + MOVL 8(SP), DI + MOVL $231, AX // exitgroup - force all os threads to exi + SYSCALL + RET + +TEXT exit1(SB),7,$0-8 + MOVL 8(SP), DI + MOVL $60, AX // exit - exit the current os thread + SYSCALL + RET + +TEXT open(SB),7,$0-16 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVL $2, AX // syscall entry + SYSCALL + RET + +TEXT close(SB),7,$0-8 + MOVL 8(SP), DI + MOVL $3, AX // syscall entry + SYSCALL + RET + +TEXT fstat(SB),7,$0-16 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL $5, AX // syscall entry + SYSCALL + RET + +TEXT read(SB),7,$0-24 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $0, AX // syscall entry + SYSCALL + RET + +TEXT write(SB),7,$0-24 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $1, AX // syscall entry + SYSCALL + RET + +TEXT sys·write(SB),7,$0-24 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $1, AX // syscall entry + SYSCALL + RET + +TEXT rt_sigaction(SB),7,$0-32 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVQ 24(SP), DX + MOVQ 32(SP), R10 + MOVL $13, AX // syscall entry + SYSCALL + RET + +TEXT sigtramp(SB),7,$24-16 + MOVQ 32(R14), R15 // g = m->gsignal + MOVQ DI,0(SP) + MOVQ SI,8(SP) + MOVQ DX,16(SP) + CALL sighandler(SB) + RET + +TEXT sigignore(SB),7,$0 + RET + +TEXT sigreturn(SB),7,$0 + MOVL $15, AX // rt_sigreturn + SYSCALL + INT $3 // not reached + +TEXT sys·mmap(SB),7,$0-32 + MOVQ 8(SP), DI + MOVQ $0, SI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVL 24(SP), R10 + MOVL 28(SP), R8 + MOVL 32(SP), R9 + + MOVL $9, AX // syscall entry + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS 2(PC) + CALL notok(SB) + RET + +TEXT notok(SB),7,$0 + MOVQ $0xf1, BP + MOVQ BP, (BP) + RET + +TEXT sys·memclr(SB),7,$0-16 + MOVQ 8(SP), DI // arg 1 addr + MOVL 16(SP), CX // arg 2 count (cannot be zero) + ADDL $7, CX + SHRL $3, CX + MOVQ $0, AX + CLD + REP + STOSQ + RET + +TEXT sys·getcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ -8(AX),AX // get calling pc + RET + +TEXT sys·setcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ x+8(FP), BX + MOVQ BX, -8(AX) // set calling pc + RET + +// int64 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT futex(SB),7,$0 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVQ 24(SP), R10 + MOVQ 32(SP), R8 + MOVL 40(SP), R9 + MOVL $202, AX + SYSCALL + RET + +// int64 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +TEXT clone(SB),7,$0 + MOVL flags+8(SP), DI + MOVQ stack+16(SP), SI + + // Copy m, g, fn off parent stack for use by child. + // Careful: Linux system call clobbers CX and R11. + MOVQ m+24(SP), R8 + MOVQ g+32(SP), R9 + MOVQ fn+40(SP), R12 + + MOVL $56, AX + SYSCALL + + // In parent, return. + CMPQ AX, $0 + JEQ 2(PC) + RET + + // In child, set up new stack + MOVQ SI, SP + MOVQ R8, R14 // m + MOVQ R9, R15 // g + + // Initialize m->procid to Linux tid + MOVL $186, AX // gettid + SYSCALL + MOVQ AX, 24(R14) + + // Call fn + CALL R12 + + // It shouldn't return. If it does, exi + MOVL $111, DI + MOVL $60, AX + SYSCALL + JMP -3(PC) // keep exiting + +TEXT sigaltstack(SB),7,$-8 + MOVQ new+8(SP), DI + MOVQ old+16(SP), SI + MOVQ $131, AX + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS 2(PC) + CALL notok(SB) + RET diff --git a/src/lib/runtime/linux/arm/defs.h b/src/lib/runtime/linux/arm/defs.h new file mode 100644 index 000000000..caad66989 --- /dev/null +++ b/src/lib/runtime/linux/arm/defs.h @@ -0,0 +1,27 @@ +// godefs -carm-gcc -f -I/usr/local/google/src/linux-2.6.28/arch/arm/include -f -I/usr/local/google/src/linux-2.6.28/include defs_arm.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_RESTORER = 0x4000000, + SA_SIGINFO = 0x4, +}; + +// Types +#pragma pack on + +typedef struct Timespec Timespec; +struct Timespec { + int32 tv_sec; + int32 tv_nsec; +}; +#pragma pack off diff --git a/src/lib/runtime/linux/arm/rt0.s b/src/lib/runtime/linux/arm/rt0.s new file mode 100644 index 000000000..024547ddd --- /dev/null +++ b/src/lib/runtime/linux/arm/rt0.s @@ -0,0 +1,6 @@ +// 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_arm_linux(SB),7,$0 + B _rt0_arm(SB) diff --git a/src/lib/runtime/linux/arm/signal.c b/src/lib/runtime/linux/arm/signal.c new file mode 100644 index 000000000..024018d5a --- /dev/null +++ b/src/lib/runtime/linux/arm/signal.c @@ -0,0 +1,4 @@ +// 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. + diff --git a/src/lib/runtime/linux/arm/sys.s b/src/lib/runtime/linux/arm/sys.s new file mode 100644 index 000000000..f5db32305 --- /dev/null +++ b/src/lib/runtime/linux/arm/sys.s @@ -0,0 +1,15 @@ +// 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. + +// +// System calls and other sys.stuff for arm, Linux +// + +TEXT write(SB),7,$0 + MOVW 4(SP), R0 + MOVW 8(SP), R1 + MOVW 12(SP), R2 + SWI $0x00900004 // syscall write + RET + diff --git a/src/lib/runtime/linux/defs.c b/src/lib/runtime/linux/defs.c new file mode 100644 index 000000000..35fa02953 --- /dev/null +++ b/src/lib/runtime/linux/defs.c @@ -0,0 +1,40 @@ +// 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. + +/* + * Input to godefs + godefs -f -m64 defs.c >amd64/defs.h + godefs -f -m64 defs1.c >>amd64/defs.h + */ + +// Linux glibc and Linux kernel define different and conflicting +// definitions for struct sigaction, struct timespec, etc. +// We want the kernel ones, which are in the asm/* headers. +// But then we'd get conflicts when we include the system +// headers for things like ucontext_t, so that happens in +// a separate file, defs1.c. + +#include <asm/signal.h> +#include <asm/siginfo.h> +#include <asm/mman.h> + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANONYMOUS, + $MAP_PRIVATE = MAP_PRIVATE, + + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_RESTORER = SA_RESTORER, + $SA_SIGINFO = SA_SIGINFO, +}; + +typedef struct timespec $Timespec; +typedef struct timeval $Timeval; +typedef struct sigaction $Sigaction; +typedef siginfo_t $Siginfo; diff --git a/src/lib/runtime/linux/defs1.c b/src/lib/runtime/linux/defs1.c new file mode 100644 index 000000000..0fe3506ad --- /dev/null +++ b/src/lib/runtime/linux/defs1.c @@ -0,0 +1,25 @@ +// 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. + +/* + * Input to godefs + godefs -f -m64 defs.c >amd64/defs.h + godefs -f -m64 defs1.c >>amd64/defs.h + */ + +#include <ucontext.h> + +typedef __sigset_t $Usigset; +typedef struct _libc_fpxreg $Fpxreg; +typedef struct _libc_xmmreg $Xmmreg; +typedef struct _libc_fpstate $Fpstate; +typedef struct _libc_fpreg $Fpreg; +typedef struct _fpxreg $Fpxreg1; +typedef struct _xmmreg $Xmmreg1; +typedef struct _fpstate $Fpstate1; +typedef struct _fpreg $Fpreg1; +typedef struct sigaltstack $Sigaltstack; +typedef mcontext_t $Mcontext; +typedef ucontext_t $Ucontext; +typedef struct sigcontext $Sigcontext; diff --git a/src/lib/runtime/linux/defs2.c b/src/lib/runtime/linux/defs2.c new file mode 100644 index 000000000..aa0331a37 --- /dev/null +++ b/src/lib/runtime/linux/defs2.c @@ -0,0 +1,51 @@ +// 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. + +/* + * Input to godefs + godefs -f -m32 -f -I/home/rsc/pub/linux-2.6/arch/x86/include -f -I/home/rsc/pub/linux-2.6/include defs2.c >386/defs.h + + * The asm header tricks we have to use for Linux on amd64 + * (see defs.c and defs1.c) don't work here, so this is yet another + * file. Sigh. + */ + +#include <asm/signal.h> +#include <asm/mman.h> +#include <asm/sigframe.h> +#include <asm/ucontext.h> + +/* +#include <sys/signal.h> +#include <sys/mman.h> +#include <ucontext.h> +*/ + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANONYMOUS, + $MAP_PRIVATE = MAP_PRIVATE, + + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_RESTORER = SA_RESTORER, + $SA_SIGINFO = SA_SIGINFO, +}; + +typedef struct _fpreg $Fpreg; +typedef struct _fpxreg $Fpxreg; +typedef struct _xmmreg $Xmmreg; +typedef struct _fpstate $Fpstate; +typedef struct timespec $Timespec; +typedef struct timeval $Timeval; +typedef struct sigaction $Sigaction; +typedef siginfo_t $Siginfo; +typedef struct sigaltstack $Sigaltstack; +typedef struct sigcontext $Sigcontext; +typedef struct ucontext $Ucontext; + diff --git a/src/lib/runtime/linux/defs_arm.c b/src/lib/runtime/linux/defs_arm.c new file mode 100644 index 000000000..eaec05154 --- /dev/null +++ b/src/lib/runtime/linux/defs_arm.c @@ -0,0 +1,54 @@ +// 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. + +/* + * Input to godefs + godefs -carm-gcc -f -I/usr/local/google/src/linux-2.6.28/arch/arm/include -f + -I/usr/local/google/src/linux-2.6.28/include defs_arm.c >arm/defs.h + + * Another input file for ARM defs.h + */ + +#include <asm/signal.h> +#include <asm/mman.h> +#include <asm/sigcontext.h> +#include <asm/ucontext.h> + +/* +#include <sys/signal.h> +#include <sys/mman.h> +#include <ucontext.h> +*/ + +#include <time.h> + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANONYMOUS, + $MAP_PRIVATE = MAP_PRIVATE, + + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_RESTORER = SA_RESTORER, + $SA_SIGINFO = SA_SIGINFO +}; + + + + +//typedef struct _fpreg $Fpreg; +//typedef struct _fpxreg $Fpxreg; +//typedef struct _xmmreg $Xmmreg; +//typedef struct _fpstate $Fpstate; +typedef struct timespec $Timespec; +//typedef struct timeval $Timeval; +// typedef struct sigaction $Sigaction; +// typedef siginfo_t $Siginfo; +// typedef struct sigaltstack $Sigaltstack; +// typedef struct sigcontext $Sigcontext; +// typedef struct ucontext $Ucontext; diff --git a/src/lib/runtime/linux/os.h b/src/lib/runtime/linux/os.h new file mode 100644 index 000000000..c61619367 --- /dev/null +++ b/src/lib/runtime/linux/os.h @@ -0,0 +1,10 @@ +// 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. + +// Linux-specific system calls +int64 futex(uint32*, int32, uint32, Timespec*, uint32*, uint32); +int64 clone(int32, void*, M*, G*, void(*)(void)); + +struct Sigaction; +void rt_sigaction(int64, struct Sigaction*, void*, uint64); diff --git a/src/lib/runtime/linux/signals.h b/src/lib/runtime/linux/signals.h new file mode 100644 index 000000000..1fb49c513 --- /dev/null +++ b/src/lib/runtime/linux/signals.h @@ -0,0 +1,48 @@ +// 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. + + +#define C SigCatch +#define I SigIgnore +#define R SigRestart + +static SigTab sigtab[] = { + /* 0 */ 0, "SIGNONE: no trap", + /* 1 */ 0, "SIGHUP: terminal line hangup", + /* 2 */ 0, "SIGINT: interrupt", + /* 3 */ C, "SIGQUIT: quit", + /* 4 */ C, "SIGILL: illegal instruction", + /* 5 */ C, "SIGTRAP: trace trap", + /* 6 */ C, "SIGABRT: abort", + /* 7 */ C, "SIGBUS: bus error", + /* 8 */ C, "SIGFPE: floating-point exception", + /* 9 */ 0, "SIGKILL: kill", + /* 10 */ 0, "SIGUSR1: user-defined signal 1", + /* 11 */ C, "SIGSEGV: segmentation violation", + /* 12 */ 0, "SIGUSR2: user-defined signal 2", + /* 13 */ I, "SIGPIPE: write to broken pipe", + /* 14 */ 0, "SIGALRM: alarm clock", + /* 15 */ 0, "SIGTERM: termination", + /* 16 */ 0, "SIGSTKFLT: stack fault", + /* 17 */ I+R, "SIGCHLD: child status has changed", + /* 18 */ 0, "SIGCONT: continue", + /* 19 */ 0, "SIGSTOP: stop, unblockable", + /* 20 */ 0, "SIGTSTP: keyboard stop", + /* 21 */ 0, "SIGTTIN: background read from tty", + /* 22 */ 0, "SIGTTOU: background write to tty", + /* 23 */ 0, "SIGURG: urgent condition on socket", + /* 24 */ 0, "SIGXCPU: cpu limit exceeded", + /* 25 */ 0, "SIGXFSZ: file size limit exceeded", + /* 26 */ 0, "SIGVTALRM: virtual alarm clock", + /* 27 */ 0, "SIGPROF: profiling alarm clock", + /* 28 */ I+R, "SIGWINCH: window size change", + /* 29 */ 0, "SIGIO: i/o now possible", + /* 30 */ 0, "SIGPWR: power failure restart", + /* 31 */ C, "SIGSYS: bad system call", +}; +#undef C +#undef I +#undef R + +#define NSIG 32 diff --git a/src/lib/runtime/linux/thread.c b/src/lib/runtime/linux/thread.c new file mode 100644 index 000000000..cc9ba161b --- /dev/null +++ b/src/lib/runtime/linux/thread.c @@ -0,0 +1,282 @@ +// 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" +#include "defs.h" +#include "signals.h" +#include "os.h" + +// Linux futex. +// +// futexsleep(uint32 *addr, uint32 val) +// futexwakeup(uint32 *addr) +// +// Futexsleep atomically checks if *addr == val and if so, sleeps on addr. +// Futexwakeup wakes up one thread sleeping on addr. +// Futexsleep is allowed to wake up spuriously. + +enum +{ + FUTEX_WAIT = 0, + FUTEX_WAKE = 1, + + EINTR = 4, + EAGAIN = 11, +}; + +// TODO(rsc): I tried using 1<<40 here but futex woke up (-ETIMEDOUT). +// I wonder if the timespec that gets to the kernel +// actually has two 32-bit numbers in it, so that +// a 64-bit 1<<40 ends up being 0 seconds, +// 1<<8 nanoseconds. +static Timespec longtime = +{ + 1<<30, // 34 years + 0 +}; + +// Atomically, +// if(*addr == val) sleep +// Might be woken up spuriously; that's allowed. +static void +futexsleep(uint32 *addr, uint32 val) +{ + int64 ret; + + ret = futex(addr, FUTEX_WAIT, val, &longtime, nil, 0); + if(ret >= 0 || ret == -EAGAIN || ret == -EINTR) + return; + + prints("futexsleep addr="); + sys·printpointer(addr); + prints(" val="); + sys·printint(val); + prints(" returned "); + sys·printint(ret); + prints("\n"); + *(int32*)0x1005 = 0x1005; +} + +// If any procs are sleeping on addr, wake up at least one. +static void +futexwakeup(uint32 *addr) +{ + int64 ret; + + ret = futex(addr, FUTEX_WAKE, 1, nil, nil, 0); + + if(ret >= 0) + return; + + // I don't know that futex wakeup can return + // EAGAIN or EINTR, but if it does, it would be + // safe to loop and call futex again. + + prints("futexwakeup addr="); + sys·printpointer(addr); + prints(" returned "); + sys·printint(ret); + prints("\n"); + *(int32*)0x1006 = 0x1006; +} + + +// Lock and unlock. +// +// The lock state is a single 32-bit word that holds +// a 31-bit count of threads waiting for the lock +// and a single bit (the low bit) saying whether the lock is held. +// The uncontended case runs entirely in user space. +// When contention is detected, we defer to the kernel (futex). +// +// A reminder: compare-and-swap cas(addr, old, new) does +// if(*addr == old) { *addr = new; return 1; } +// else return 0; +// but atomically. + +static void +futexlock(Lock *l) +{ + uint32 v; + +again: + v = l->key; + if((v&1) == 0){ + if(cas(&l->key, v, v|1)){ + // Lock wasn't held; we grabbed it. + return; + } + goto again; + } + + // Lock was held; try to add ourselves to the waiter count. + if(!cas(&l->key, v, v+2)) + goto again; + + // We're accounted for, now sleep in the kernel. + // + // We avoid the obvious lock/unlock race because + // the kernel won't put us to sleep if l->key has + // changed underfoot and is no longer v+2. + // + // We only really care that (v&1) == 1 (the lock is held), + // and in fact there is a futex variant that could + // accomodate that check, but let's not get carried away.) + futexsleep(&l->key, v+2); + + // We're awake: remove ourselves from the count. + for(;;){ + v = l->key; + if(v < 2) + throw("bad lock key"); + if(cas(&l->key, v, v-2)) + break; + } + + // Try for the lock again. + goto again; +} + +static void +futexunlock(Lock *l) +{ + uint32 v; + + // Atomically get value and clear lock bit. +again: + v = l->key; + if((v&1) == 0) + throw("unlock of unlocked lock"); + if(!cas(&l->key, v, v&~1)) + goto again; + + // If there were waiters, wake one. + if(v & ~1) + futexwakeup(&l->key); +} + +void +lock(Lock *l) +{ + if(m->locks < 0) + throw("lock count"); + m->locks++; + futexlock(l); +} + +void +unlock(Lock *l) +{ + m->locks--; + if(m->locks < 0) + throw("lock count"); + futexunlock(l); +} + + +// One-time notifications. +// +// Since the lock/unlock implementation already +// takes care of sleeping in the kernel, we just reuse it. +// (But it's a weird use, so it gets its own interface.) +// +// We use a lock to represent the event: +// unlocked == event has happened. +// Thus the lock starts out locked, and to wait for the +// event you try to lock the lock. To signal the event, +// you unlock the lock. + +void +noteclear(Note *n) +{ + n->lock.key = 0; // memset(n, 0, sizeof *n) + futexlock(&n->lock); +} + +void +notewakeup(Note *n) +{ + futexunlock(&n->lock); +} + +void +notesleep(Note *n) +{ + futexlock(&n->lock); + futexunlock(&n->lock); // Let other sleepers find out too. +} + + +// Clone, the Linux rfork. +enum +{ + CLONE_VM = 0x100, + CLONE_FS = 0x200, + CLONE_FILES = 0x400, + CLONE_SIGHAND = 0x800, + CLONE_PTRACE = 0x2000, + CLONE_VFORK = 0x4000, + CLONE_PARENT = 0x8000, + CLONE_THREAD = 0x10000, + CLONE_NEWNS = 0x20000, + CLONE_SYSVSEM = 0x40000, + CLONE_SETTLS = 0x80000, + CLONE_PARENT_SETTID = 0x100000, + CLONE_CHILD_CLEARTID = 0x200000, + CLONE_UNTRACED = 0x800000, + CLONE_CHILD_SETTID = 0x1000000, + CLONE_STOPPED = 0x2000000, + CLONE_NEWUTS = 0x4000000, + CLONE_NEWIPC = 0x8000000, +}; + +void +newosproc(M *m, G *g, void *stk, void (*fn)(void)) +{ + int64 ret; + int32 flags; + + /* + * note: strace gets confused if we use CLONE_PTRACE here. + */ + flags = CLONE_PARENT /* getppid doesn't change in child */ + | CLONE_VM /* share memory */ + | CLONE_FS /* share cwd, etc */ + | CLONE_FILES /* share fd table */ + | CLONE_SIGHAND /* share sig handler table */ + | CLONE_THREAD /* revisit - okay for now */ + ; + + if(0){ + prints("newosproc stk="); + sys·printpointer(stk); + prints(" m="); + sys·printpointer(m); + prints(" g="); + sys·printpointer(g); + prints(" fn="); + sys·printpointer(fn); + prints(" clone="); + sys·printpointer(clone); + prints("\n"); + } + + ret = clone(flags, stk, m, g, fn); + if(ret < 0) + *(int32*)123 = 123; +} + +void +osinit(void) +{ +} + +// Called to initialize a new m (including the bootstrap m). +void +minit(void) +{ + // Initialize signal handling. + m->gsignal = malg(32*1024); // OS X wants >=8K, Linux >=2K + signalstack(m->gsignal->stackguard, 32*1024); +} diff --git a/src/lib/runtime/malloc.c b/src/lib/runtime/malloc.c new file mode 100644 index 000000000..81cdfb300 --- /dev/null +++ b/src/lib/runtime/malloc.c @@ -0,0 +1,308 @@ +// 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. + +// See malloc.h for overview. +// +// TODO(rsc): double-check stats. +// TODO(rsc): solve "stack overflow during malloc" problem. + +#include "runtime.h" +#include "malloc.h" +#include "defs.h" + +MHeap mheap; +MStats mstats; + +// Allocate an object of at least size bytes. +// Small objects are allocated from the per-thread cache's free lists. +// Large objects (> 32 kB) are allocated straight from the heap. +void* +malloc(uintptr size) +{ + int32 sizeclass; + MCache *c; + uintptr npages; + MSpan *s; + void *v; + uint32 *ref; + + if(m->mallocing) + throw("malloc/free - deadlock"); + m->mallocing = 1; + + if(size == 0) + size = 1; + + if(size <= MaxSmallSize) { + // Allocate from mcache free lists. + sizeclass = SizeToClass(size); + size = class_to_size[sizeclass]; + c = m->mcache; + v = MCache_Alloc(c, sizeclass, size); + if(v == nil) + throw("out of memory"); + mstats.alloc += size; + } else { + // TODO(rsc): Report tracebacks for very large allocations. + + // Allocate directly from heap. + npages = size >> PageShift; + if((size & PageMask) != 0) + npages++; + s = MHeap_Alloc(&mheap, npages, 0); + if(s == nil) + throw("out of memory"); + mstats.alloc += npages<<PageShift; + v = (void*)(s->start << PageShift); + } + + // setup for mark sweep + if(!mlookup(v, nil, nil, &ref)) { + printf("malloc %D; mlookup failed\n", (uint64)size); + throw("malloc mlookup"); + } + *ref = RefNone; + + m->mallocing = 0; + return v; +} + +void* +mallocgc(uintptr size) +{ + void *v; + + v = malloc(size); + if(mstats.inuse_pages > mstats.next_gc) + gc(0); + return v; +} + +// Free the object whose base pointer is v. +void +free(void *v) +{ + int32 sizeclass, size; + uintptr page, tmp; + MSpan *s; + MCache *c; + uint32 *ref; + + if(v == nil) + return; + + if(m->mallocing) + throw("malloc/free - deadlock"); + m->mallocing = 1; + + if(!mlookup(v, nil, nil, &ref)) + throw("free mlookup"); + *ref = RefFree; + + // Find size class for v. + page = (uintptr)v >> PageShift; + sizeclass = MHeapMapCache_GET(&mheap.mapcache, page, tmp); + if(sizeclass == 0) { + // Missed in cache. + s = MHeap_Lookup(&mheap, page); + if(s == nil) + throw("free - invalid pointer"); + sizeclass = s->sizeclass; + if(sizeclass == 0) { + // Large object. + mstats.alloc -= s->npages<<PageShift; + sys_memclr(v, s->npages<<PageShift); + MHeap_Free(&mheap, s); + goto out; + } + MHeapMapCache_SET(&mheap.mapcache, page, sizeclass); + } + + // Small object. + c = m->mcache; + size = class_to_size[sizeclass]; + sys_memclr(v, size); + mstats.alloc -= size; + MCache_Free(c, v, sizeclass, size); + +out: + m->mallocing = 0; +} + +int32 +mlookup(void *v, byte **base, uintptr *size, uint32 **ref) +{ + uintptr n, nobj, i; + byte *p; + MSpan *s; + + s = MHeap_LookupMaybe(&mheap, (uintptr)v>>PageShift); + if(s == nil) { + if(base) + *base = nil; + if(size) + *size = 0; + if(ref) + *ref = 0; + return 0; + } + + p = (byte*)((uintptr)s->start<<PageShift); + if(s->sizeclass == 0) { + // Large object. + if(base) + *base = p; + if(size) + *size = s->npages<<PageShift; + if(ref) + *ref = &s->gcref0; + return 1; + } + + if((byte*)v >= (byte*)s->gcref) { + // pointers into the gc ref counts + // do not count as pointers. + return 0; + } + + n = class_to_size[s->sizeclass]; + i = ((byte*)v - p)/n; + if(base) + *base = p + i*n; + if(size) + *size = n; + nobj = (s->npages << PageShift) / (n + RefcountOverhead); + if((byte*)s->gcref < p || (byte*)(s->gcref+nobj) > p+(s->npages<<PageShift)) { + printf("odd span state=%d span=%p base=%p sizeclass=%d n=%D size=%D npages=%D\n", + s->state, s, p, s->sizeclass, (uint64)nobj, (uint64)n, (uint64)s->npages); + printf("s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%D size=%D end=%p end=%p\n", + s->sizeclass, v, p, s->gcref, (uint64)s->npages<<PageShift, + (uint64)nobj, (uint64)n, s->gcref + nobj, p+(s->npages<<PageShift)); + throw("bad gcref"); + } + if(ref) + *ref = &s->gcref[i]; + + return 1; +} + +MCache* +allocmcache(void) +{ + return FixAlloc_Alloc(&mheap.cachealloc); +} + +void +mallocinit(void) +{ + InitSizes(); + MHeap_Init(&mheap, SysAlloc); + m->mcache = allocmcache(); + + // See if it works. + free(malloc(1)); +} + +void* +SysAlloc(uintptr n) +{ + mstats.sys += n; + return sys_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); +} + +void +SysUnused(void *v, uintptr n) +{ + USED(v); + USED(n); + // TODO(rsc): call madvise MADV_DONTNEED +} + +void +SysFree(void *v, uintptr n) +{ + USED(v); + USED(n); + // TODO(rsc): call munmap +} + +// Runtime stubs. + +extern void *oldmal(uint32); + +void* +mal(uint32 n) +{ +//return oldmal(n); + void *v; + + v = mallocgc(n); + + if(0) { + byte *p; + uint32 i; + p = v; + for(i=0; i<n; i++) { + if(p[i] != 0) { + printf("mal %d => %p: byte %d is non-zero\n", n, v, i); + throw("mal"); + } + } + } + +//printf("mal %d %p\n", n, v); // |checkmal to check for overlapping returns. + return v; +} + +// Stack allocator uses malloc/free most of the time, +// but if we're in the middle of malloc and need stack, +// we have to do something else to avoid deadlock. +// In that case, we fall back on a fixed-size free-list +// allocator, assuming that inside malloc all the stack +// frames are small, so that all the stack allocations +// will be a single size, the minimum (right now, 5k). +struct { + Lock; + FixAlloc; +} stacks; + +void* +stackalloc(uint32 n) +{ + void *v; + uint32 *ref; + +//return oldmal(n); + if(m->mallocing) { + lock(&stacks); + if(stacks.size == 0) + FixAlloc_Init(&stacks, n, SysAlloc, nil, nil); + if(stacks.size != n) { + printf("stackalloc: in malloc, size=%D want %d", (uint64)stacks.size, n); + throw("stackalloc"); + } + v = FixAlloc_Alloc(&stacks); + unlock(&stacks); + return v; + } + v = malloc(n); + if(!mlookup(v, nil, nil, &ref)) + throw("stackalloc mlookup"); + *ref = RefStack; + return v; +} + +void +stackfree(void *v) +{ +//return; + + if(m->mallocing) { + lock(&stacks); + FixAlloc_Free(&stacks, v); + unlock(&stacks); + return; + } + free(v); +} diff --git a/src/lib/runtime/malloc.h b/src/lib/runtime/malloc.h new file mode 100644 index 000000000..9b3d6b811 --- /dev/null +++ b/src/lib/runtime/malloc.h @@ -0,0 +1,308 @@ +// 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. + +// Memory allocator, based on tcmalloc. +// http://goog-perftools.sourceforge.net/doc/tcmalloc.html + +// The main allocator works in runs of pages. +// Small allocation sizes (up to and including 32 kB) are +// rounded to one of about 100 size classes, each of which +// has its own free list of objects of exactly that size. +// Any free page of memory can be split into a set of objects +// of one size class, which are then managed using free list +// allocators. +// +// The allocator's data structures are: +// +// FixAlloc: a free-list allocator for fixed-size objects, +// used to manage storage used by the allocator. +// MHeap: the malloc heap, managed at page (4096-byte) granularity. +// MSpan: a run of pages managed by the MHeap. +// MHeapMap: a mapping from page IDs to MSpans. +// MHeapMapCache: a small cache of MHeapMap mapping page IDs +// to size classes for pages used for small objects. +// MCentral: a shared free list for a given size class. +// MCache: a per-thread (in Go, per-M) cache for small objects. +// MStats: allocation statistics. +// +// Allocating a small object proceeds up a hierarchy of caches: +// +// 1. Round the size up to one of the small size classes +// and look in the corresponding MCache free list. +// If the list is not empty, allocate an object from it. +// This can all be done without acquiring a lock. +// +// 2. If the MCache free list is empty, replenish it by +// taking a bunch of objects from the MCentral free list. +// Moving a bunch amortizes the cost of acquiring the MCentral lock. +// +// 3. If the MCentral free list is empty, replenish it by +// allocating a run of pages from the MHeap and then +// chopping that memory into a objects of the given size. +// Allocating many objects amortizes the cost of locking +// the heap. +// +// 4. If the MHeap is empty or has no page runs large enough, +// allocate a new group of pages (at least 1MB) from the +// operating system. Allocating a large run of pages +// amortizes the cost of talking to the operating system. +// +// Freeing a small object proceeds up the same hierarchy: +// +// 1. Look up the size class for the object and add it to +// the MCache free list. +// +// 2. If the MCache free list is too long or the MCache has +// too much memory, return some to the MCentral free lists. +// +// 3. If all the objects in a given span have returned to +// the MCentral list, return that span to the page heap. +// +// 4. If the heap has too much memory, return some to the +// operating system. +// +// TODO(rsc): Step 4 is not implemented. +// +// Allocating and freeing a large object uses the page heap +// directly, bypassing the MCache and MCentral free lists. +// +// This C code was written with an eye toward translating to Go +// in the future. Methods have the form Type_Method(Type *t, ...). + + +typedef struct FixAlloc FixAlloc; +typedef struct MCentral MCentral; +typedef struct MHeap MHeap; +typedef struct MHeapMap MHeapMap; +typedef struct MHeapMapCache MHeapMapCache; +typedef struct MSpan MSpan; +typedef struct MStats MStats; +typedef struct MLink MLink; + +enum +{ + PageShift = 12, + PageSize = 1<<PageShift, + PageMask = PageSize - 1, +}; +typedef uintptr PageID; // address >> PageShift + +enum +{ + // Tunable constants. + NumSizeClasses = 67, // Number of size classes (must match msize.c) + MaxSmallSize = 32<<10, + + FixAllocChunk = 128<<10, // Chunk size for FixAlloc + MaxMCacheListLen = 256, // Maximum objects on MCacheList + MaxMCacheSize = 2<<20, // Maximum bytes in one MCache + MaxMHeapList = 1<<(20 - PageShift), // Maximum page length for fixed-size list in MHeap. + HeapAllocChunk = 1<<20, // Chunk size for heap growth +}; + +#ifdef _64BIT +#include "mheapmap64.h" +#else +#include "mheapmap32.h" +#endif + +// A generic linked list of blocks. (Typically the block is bigger than sizeof(MLink).) +struct MLink +{ + MLink *next; +}; + +// SysAlloc obtains a large chunk of memory from the operating system, +// typically on the order of a hundred kilobytes or a megabyte. +// +// SysUnused notifies the operating system that the contents +// of the memory region are no longer needed and can be reused +// for other purposes. The program reserves the right to start +// accessing those pages in the future. +// +// SysFree returns it unconditionally; this is only used if +// an out-of-memory error has been detected midway through +// an allocation. It is okay if SysFree is a no-op. + +void* SysAlloc(uintptr nbytes); +void SysFree(void *v, uintptr nbytes); +void SysUnused(void *v, uintptr nbytes); + + +// FixAlloc is a simple free-list allocator for fixed size objects. +// Malloc uses a FixAlloc wrapped around SysAlloc to manages its +// MCache and MSpan objects. +// +// Memory returned by FixAlloc_Alloc is not zeroed. +// The caller is responsible for locking around FixAlloc calls. +// Callers can keep state in the object but the first word is +// smashed by freeing and reallocating. +struct FixAlloc +{ + uintptr size; + void *(*alloc)(uintptr); + void (*first)(void *arg, byte *p); // called first time p is returned + void *arg; + MLink *list; + byte *chunk; + uint32 nchunk; +}; + +void FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg); +void* FixAlloc_Alloc(FixAlloc *f); +void FixAlloc_Free(FixAlloc *f, void *p); + + +// Statistics. +// Shared with Go: if you edit this structure, also edit ../lib/malloc.go. +struct MStats +{ + uint64 alloc; + uint64 sys; + uint64 stacks; + uint64 inuse_pages; // protected by mheap.Lock + uint64 next_gc; // protected by mheap.Lock + bool enablegc; +}; +extern MStats mstats; + + +// Size classes. Computed and initialized by InitSizes. +// +// SizeToClass(0 <= n <= MaxSmallSize) returns the size class, +// 1 <= sizeclass < NumSizeClasses, for n. +// Size class 0 is reserved to mean "not small". +// +// class_to_size[i] = largest size in class i +// class_to_allocnpages[i] = number of pages to allocate when +// making new objects in class i +// class_to_transfercount[i] = number of objects to move when +// taking a bunch of objects out of the central lists +// and putting them in the thread free list. + +int32 SizeToClass(int32); +extern int32 class_to_size[NumSizeClasses]; +extern int32 class_to_allocnpages[NumSizeClasses]; +extern int32 class_to_transfercount[NumSizeClasses]; +extern void InitSizes(void); + + +// Per-thread (in Go, per-M) cache for small objects. +// No locking needed because it is per-thread (per-M). +typedef struct MCacheList MCacheList; +struct MCacheList +{ + MLink *list; + uint32 nlist; + uint32 nlistmin; +}; + +struct MCache +{ + MCacheList list[NumSizeClasses]; + uint64 size; +}; + +void* MCache_Alloc(MCache *c, int32 sizeclass, uintptr size); +void MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size); + + +// An MSpan is a run of pages. +enum +{ + MSpanInUse = 0, + MSpanFree, + MSpanListHead, + MSpanDead, +}; +struct MSpan +{ + MSpan *next; // in a span linked list + MSpan *prev; // in a span linked list + MSpan *allnext; // in the list of all spans + PageID start; // starting page number + uintptr npages; // number of pages in span + MLink *freelist; // list of free objects + uint32 ref; // number of allocated objects in this span + uint32 sizeclass; // size class + uint32 state; // MSpanInUse etc + union { + uint32 *gcref; // sizeclass > 0 + uint32 gcref0; // sizeclass == 0 + }; +}; + +void MSpan_Init(MSpan *span, PageID start, uintptr npages); + +// Every MSpan is in one doubly-linked list, +// either one of the MHeap's free lists or one of the +// MCentral's span lists. We use empty MSpan structures as list heads. +void MSpanList_Init(MSpan *list); +bool MSpanList_IsEmpty(MSpan *list); +void MSpanList_Insert(MSpan *list, MSpan *span); +void MSpanList_Remove(MSpan *span); // from whatever list it is in + + +// Central list of free objects of a given size. +struct MCentral +{ + Lock; + int32 sizeclass; + MSpan nonempty; + MSpan empty; + int32 nfree; +}; + +void MCentral_Init(MCentral *c, int32 sizeclass); +int32 MCentral_AllocList(MCentral *c, int32 n, MLink **first); +void MCentral_FreeList(MCentral *c, int32 n, MLink *first); + +// Main malloc heap. +// The heap itself is the "free[]" and "large" arrays, +// but all the other global data is here too. +struct MHeap +{ + Lock; + MSpan free[MaxMHeapList]; // free lists of given length + MSpan large; // free lists length >= MaxMHeapList + MSpan *allspans; + + // span lookup + MHeapMap map; + MHeapMapCache mapcache; + + // central free lists for small size classes. + // the union makes sure that the MCentrals are + // spaced 64 bytes apart, so that each MCentral.Lock + // gets its own cache line. + union { + MCentral; + byte pad[64]; + } central[NumSizeClasses]; + + FixAlloc spanalloc; // allocator for Span* + FixAlloc cachealloc; // allocator for MCache* +}; +extern MHeap mheap; + +void MHeap_Init(MHeap *h, void *(*allocator)(uintptr)); +MSpan* MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass); +void MHeap_Free(MHeap *h, MSpan *s); +MSpan* MHeap_Lookup(MHeap *h, PageID p); +MSpan* MHeap_LookupMaybe(MHeap *h, PageID p); + +int32 mlookup(void *v, byte **base, uintptr *size, uint32 **ref); +void gc(int32 force); + +enum +{ + RefcountOverhead = 4, // one uint32 per object + + RefFree = 0, // must be zero + RefManual, // manual allocation - don't free + RefStack, // stack segment - don't free and don't scan for pointers + RefNone, // no references + RefSome, // some references +}; + diff --git a/src/lib/runtime/malloc_go.cgo b/src/lib/runtime/malloc_go.cgo new file mode 100644 index 000000000..6dcdaece2 --- /dev/null +++ b/src/lib/runtime/malloc_go.cgo @@ -0,0 +1,28 @@ +// 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. + +package malloc +#include "runtime.h" +#include "malloc.h" + +func Alloc(n uintptr) (p *byte) { + p = malloc(n); +} + +func Free(p *byte) { + free(p); +} + +func Lookup(p *byte) (base *byte, size uintptr) { + mlookup(p, &base, &size, nil); +} + +func GetStats() (s *MStats) { + s = &mstats; +} + +func GC() { + gc(1); +} + diff --git a/src/lib/runtime/mcache.c b/src/lib/runtime/mcache.c new file mode 100644 index 000000000..ae2594023 --- /dev/null +++ b/src/lib/runtime/mcache.c @@ -0,0 +1,105 @@ +// 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. + +// Per-thread (in Go, per-M) malloc cache for small objects. +// +// See malloc.h for an overview. + +#include "runtime.h" +#include "malloc.h" + +void* +MCache_Alloc(MCache *c, int32 sizeclass, uintptr size) +{ + MCacheList *l; + MLink *first, *v; + int32 n; + + // Allocate from list. + l = &c->list[sizeclass]; + if(l->list == nil) { + // Replenish using central lists. + n = MCentral_AllocList(&mheap.central[sizeclass], + class_to_transfercount[sizeclass], &first); + l->list = first; + l->nlist = n; + c->size += n*size; + } + v = l->list; + l->list = v->next; + l->nlist--; + if(l->nlist < l->nlistmin) + l->nlistmin = l->nlist; + c->size -= size; + + // v is zeroed except for the link pointer + // that we used above; zero that. + v->next = nil; + return v; +} + +// Take n elements off l and return them to the central free list. +static void +ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass) +{ + MLink *first, **lp; + int32 i; + + // Cut off first n elements. + first = l->list; + lp = &l->list; + for(i=0; i<n; i++) + lp = &(*lp)->next; + l->list = *lp; + *lp = nil; + l->nlist -= n; + if(l->nlist < l->nlistmin) + l->nlistmin = l->nlist; + c->size -= n*class_to_size[sizeclass]; + + // Return them to central free list. + MCentral_FreeList(&mheap.central[sizeclass], n, first); +} + +void +MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size) +{ + int32 i, n; + MCacheList *l; + MLink *p; + + // Put back on list. + l = &c->list[sizeclass]; + p = v; + p->next = l->list; + l->list = p; + l->nlist++; + c->size += size; + + if(l->nlist >= MaxMCacheListLen) { + // Release a chunk back. + ReleaseN(c, l, class_to_transfercount[sizeclass], sizeclass); + } + + if(c->size >= MaxMCacheSize) { + // Scavenge. + for(i=0; i<NumSizeClasses; i++) { + l = &c->list[i]; + n = l->nlistmin; + + // n is the minimum number of elements we've seen on + // the list since the last scavenge. If n > 0, it means that + // we could have gotten by with n fewer elements + // without needing to consult the central free list. + // Move toward that situation by releasing n/2 of them. + if(n > 0) { + if(n > 1) + n /= 2; + ReleaseN(c, l, n, i); + } + l->nlistmin = l->nlist; + } + } +} + diff --git a/src/lib/runtime/mcentral.c b/src/lib/runtime/mcentral.c new file mode 100644 index 000000000..5c9f720c0 --- /dev/null +++ b/src/lib/runtime/mcentral.c @@ -0,0 +1,192 @@ +// 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. + +// Central free lists. +// +// See malloc.h for an overview. +// +// The MCentral doesn't actually contain the list of free objects; the MSpan does. +// Each MCentral is two lists of MSpans: those with free objects (c->nonempty) +// and those that are completely allocated (c->empty). +// +// TODO(rsc): tcmalloc uses a "transfer cache" to split the list +// into sections of class_to_transfercount[sizeclass] objects +// so that it is faster to move those lists between MCaches and MCentrals. + +#include "runtime.h" +#include "malloc.h" + +static bool MCentral_Grow(MCentral *c); +static void* MCentral_Alloc(MCentral *c); +static void MCentral_Free(MCentral *c, void *v); + +// Initialize a single central free list. +void +MCentral_Init(MCentral *c, int32 sizeclass) +{ + c->sizeclass = sizeclass; + MSpanList_Init(&c->nonempty); + MSpanList_Init(&c->empty); +} + +// Allocate up to n objects from the central free list. +// Return the number of objects allocated. +// The objects are linked together by their first words. +// On return, *pstart points at the first object and *pend at the last. +int32 +MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) +{ + MLink *first, *last, *v; + int32 i; + + + lock(c); + // Replenish central list if empty. + if(MSpanList_IsEmpty(&c->nonempty)) { + if(!MCentral_Grow(c)) { + unlock(c); + *pfirst = nil; + return 0; + } + } + + // Copy from list, up to n. + // First one is guaranteed to work, because we just grew the list. + first = MCentral_Alloc(c); + last = first; + for(i=1; i<n && (v = MCentral_Alloc(c)) != nil; i++) { + last->next = v; + last = v; + } + last->next = nil; + c->nfree -= i; + + unlock(c); + *pfirst = first; + return i; +} + +// Helper: allocate one object from the central free list. +static void* +MCentral_Alloc(MCentral *c) +{ + MSpan *s; + MLink *v; + + if(MSpanList_IsEmpty(&c->nonempty)) + return nil; + s = c->nonempty.next; + s->ref++; + v = s->freelist; + s->freelist = v->next; + if(s->freelist == nil) { + MSpanList_Remove(s); + MSpanList_Insert(&c->empty, s); + } + return v; +} + +// Free n objects back into the central free list. +// Return the number of objects allocated. +// The objects are linked together by their first words. +// On return, *pstart points at the first object and *pend at the last. +void +MCentral_FreeList(MCentral *c, int32 n, MLink *start) +{ + MLink *v, *next; + + // Assume next == nil marks end of list. + // n and end would be useful if we implemented + // the transfer cache optimization in the TODO above. + USED(n); + + lock(c); + for(v=start; v; v=next) { + next = v->next; + MCentral_Free(c, v); + } + unlock(c); +} + +// Helper: free one object back into the central free list. +static void +MCentral_Free(MCentral *c, void *v) +{ + MSpan *s; + PageID page; + MLink *p, *next; + + // Find span for v. + page = (uintptr)v >> PageShift; + s = MHeap_Lookup(&mheap, page); + if(s == nil || s->ref == 0) + throw("invalid free"); + + // Move to nonempty if necessary. + if(s->freelist == nil) { + MSpanList_Remove(s); + MSpanList_Insert(&c->nonempty, s); + } + + // Add v back to s's free list. + p = v; + p->next = s->freelist; + s->freelist = p; + c->nfree++; + + // If s is completely freed, return it to the heap. + if(--s->ref == 0) { + MSpanList_Remove(s); + // Freed blocks are zeroed except for the link pointer. + // Zero the link pointers so that the page is all zero. + for(p=s->freelist; p; p=next) { + next = p->next; + p->next = nil; + } + s->freelist = nil; + c->nfree -= (s->npages << PageShift) / class_to_size[c->sizeclass]; + unlock(c); + MHeap_Free(&mheap, s); + lock(c); + } +} + +// Fetch a new span from the heap and +// carve into objects for the free list. +static bool +MCentral_Grow(MCentral *c) +{ + int32 i, n, npages, size; + MLink **tailp, *v; + byte *p; + MSpan *s; + + unlock(c); + npages = class_to_allocnpages[c->sizeclass]; + s = MHeap_Alloc(&mheap, npages, c->sizeclass); + if(s == nil) { + // TODO(rsc): Log out of memory + lock(c); + return false; + } + + // Carve span into sequence of blocks. + tailp = &s->freelist; + p = (byte*)(s->start << PageShift); + size = class_to_size[c->sizeclass]; + n = (npages << PageShift) / (size + RefcountOverhead); + s->gcref = (uint32*)(p + size*n); + for(i=0; i<n; i++) { + v = (MLink*)p; + *tailp = v; + tailp = &v->next; + p += size; + } + *tailp = nil; + + lock(c); + c->nfree += n; + MSpanList_Insert(&c->nonempty, s); + return true; +} diff --git a/src/lib/runtime/mem.c b/src/lib/runtime/mem.c new file mode 100644 index 000000000..7ed299eb0 --- /dev/null +++ b/src/lib/runtime/mem.c @@ -0,0 +1,75 @@ +// 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" +#include "defs.h" + +// Stubs for memory management. +// In a separate file so they can be overridden during testing of gc. + +enum +{ + NHUNK = 20<<20, +}; + +// Convenient wrapper around mmap. +static void* +brk(uint32 n) +{ + byte *v; + + v = sys_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); + m->mem.nmmap += n; + return v; +} + +// Allocate n bytes of memory. Note that this gets used +// to allocate new stack segments, so at each call to a function +// you have to ask yourself "would it be okay to call mal recursively +// right here?" The answer is yes unless we're in the middle of +// editing the malloc state in m->mem. +void* +oldmal(uint32 n) +{ + byte* v; + + // round to keep everything 64-bit aligned + n = rnd(n, 8); + + // be careful. calling any function might invoke + // mal to allocate more stack. + if(n > NHUNK) { + v = brk(n); + } else { + // allocate a new hunk if this one is too small + if(n > m->mem.nhunk) { + // here we're in the middle of editing m->mem + // (we're about to overwrite m->mem.hunk), + // so we can't call brk - it might call mal to grow the + // stack, and the recursive call would allocate a new + // hunk, and then once brk returned we'd immediately + // overwrite that hunk with our own. + // (the net result would be a memory leak, not a crash.) + // 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|PROT_EXEC, + MAP_ANON|MAP_PRIVATE, 0, 0); + m->mem.nhunk = NHUNK; + m->mem.nmmap += NHUNK; + } + v = m->mem.hunk; + m->mem.hunk += n; + m->mem.nhunk -= n; + } + m->mem.nmal += n; + return v; +} + +void +sys_mal(uint32 n, uint8 *ret) +{ + ret = mal(n); + FLUSH(&ret); +} diff --git a/src/lib/runtime/mfixalloc.c b/src/lib/runtime/mfixalloc.c new file mode 100644 index 000000000..dd4f3f251 --- /dev/null +++ b/src/lib/runtime/mfixalloc.c @@ -0,0 +1,56 @@ +// 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. + +// Fixed-size object allocator. Returned memory is not zeroed. +// +// See malloc.h for overview. + +#include "runtime.h" +#include "malloc.h" + +// Initialize f to allocate objects of the given size, +// using the allocator to obtain chunks of memory. +void +FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg) +{ + f->size = size; + f->alloc = alloc; + f->first = first; + f->arg = arg; + f->list = nil; + f->chunk = nil; + f->nchunk = 0; +} + +void* +FixAlloc_Alloc(FixAlloc *f) +{ + void *v; + + if(f->list) { + v = f->list; + f->list = *(void**)f->list; + return v; + } + if(f->nchunk < f->size) { + f->chunk = f->alloc(FixAllocChunk); + if(f->chunk == nil) + throw("out of memory (FixAlloc)"); + f->nchunk = FixAllocChunk; + } + v = f->chunk; + if(f->first) + f->first(f->arg, v); + f->chunk += f->size; + f->nchunk -= f->size; + return v; +} + +void +FixAlloc_Free(FixAlloc *f, void *p) +{ + *(void**)p = f->list; + f->list = p; +} + diff --git a/src/lib/runtime/mgc0.c b/src/lib/runtime/mgc0.c new file mode 100644 index 000000000..d58d6ce44 --- /dev/null +++ b/src/lib/runtime/mgc0.c @@ -0,0 +1,231 @@ +// 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. + +// Garbage collector -- step 0. +// +// Stop the world, mark and sweep garbage collector. +// NOT INTENDED FOR PRODUCTION USE. +// +// A mark and sweep collector provides a way to exercise +// and test the memory allocator and the stack walking machinery +// without also needing to get reference counting +// exactly right. + +#include "runtime.h" +#include "malloc.h" + +enum { + Debug = 0 +}; + +extern byte etext[]; +extern byte end[]; + +enum { + PtrSize = sizeof(void*) +}; + +static void +scanblock(int32 depth, byte *b, int64 n) +{ + int32 off; + void *obj; + uintptr size; + uint32 *ref; + void **vp; + int64 i; + + if(Debug) + printf("%d scanblock %p %D\n", depth, b, n); + off = (uint32)(uintptr)b & (PtrSize-1); + if(off) { + b += PtrSize - off; + n -= PtrSize - off; + } + + vp = (void**)b; + n /= PtrSize; + for(i=0; i<n; i++) { + if(mlookup(vp[i], &obj, &size, &ref)) { + if(*ref == RefFree || *ref == RefStack) + continue; + if(*ref == RefNone) { + if(Debug) + printf("%d found at %p: ", depth, &vp[i]); + *ref = RefSome; + scanblock(depth+1, obj, size); + } + } + } +} + +static void +scanstack(G *g) +{ + Stktop *stk; + byte *sp; + + sp = g->sched.SP; + stk = (Stktop*)g->stackbase; + while(stk) { + scanblock(0, sp, (byte*)stk - sp); + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + } +} + +static void +mark(void) +{ + G *gp; + + // mark data+bss + scanblock(0, etext, end - etext); + + // mark stacks + for(gp=allg; gp!=nil; gp=gp->alllink) { + switch(gp->status){ + default: + printf("unexpected G.status %d\n", gp->status); + throw("mark - bad status"); + case Gdead: + break; + case Grunning: + if(gp != g) + throw("mark - world not stopped"); + scanstack(gp); + break; + case Grunnable: + case Gsyscall: + case Gwaiting: + scanstack(gp); + break; + } + } +} + +static void +sweepspan(MSpan *s) +{ + int32 i, n, npages, size; + byte *p; + + if(s->state != MSpanInUse) + return; + + p = (byte*)(s->start << PageShift); + if(s->sizeclass == 0) { + // Large block. + switch(s->gcref0) { + default: + throw("bad 'ref count'"); + case RefFree: + case RefManual: + case RefStack: + break; + case RefNone: + if(Debug) + printf("free %D at %p\n", (uint64)s->npages<<PageShift, p); + free(p); + break; + case RefSome: + s->gcref0 = RefNone; // set up for next mark phase + break; + } + return; + } + + // Chunk full of small blocks. + // Must match computation in MCentral_Grow. + size = class_to_size[s->sizeclass]; + npages = class_to_allocnpages[s->sizeclass]; + n = (npages << PageShift) / (size + RefcountOverhead); + for(i=0; i<n; i++) { + switch(s->gcref[i]) { + default: + throw("bad 'ref count'"); + case RefFree: + case RefManual: + case RefStack: + break; + case RefNone: + if(Debug) + printf("free %d at %p\n", size, p+i*size); + free(p + i*size); + break; + case RefSome: + s->gcref[i] = RefNone; // set up for next mark phase + break; + } + } +} + +static void +sweep(void) +{ + MSpan *s; + + // Sweep all the spans. + for(s = mheap.allspans; s != nil; s = s->allnext) + sweepspan(s); +} + +// Semaphore, not Lock, so that the goroutine +// reschedules when there is contention rather +// than spinning. +static uint32 gcsema = 1; + +// Initialized from $GOGC. GOGC=off means no gc. +// +// Next gc is after we've allocated an extra amount of +// memory proportional to the amount already in use. +// If gcpercent=100 and we're using 4M, we'll gc again +// when we get to 8M. This keeps the gc cost in linear +// proportion to the allocation cost. Adjusting gcpercent +// just changes the linear constant (and also the amount of +// extra memory used). +static int32 gcpercent = -2; + +void +gc(int32 force) +{ + byte *p; + + // The gc is turned off (via enablegc) until + // the bootstrap has completed. + // Also, malloc gets called in the guts + // of a number of libraries that might be + // holding locks. To avoid priority inversion + // problems, don't bother trying to run gc + // while holding a lock. The next mallocgc + // without a lock will do the gc instead. + if(!mstats.enablegc || m->locks > 0 || panicking) + return; + + if(gcpercent == -2) { // first time through + p = getenv("GOGC"); + if(p == nil || p[0] == '\0') + gcpercent = 100; + else if(strcmp(p, (byte*)"off") == 0) + gcpercent = -1; + else + gcpercent = atoi(p); + } + if(gcpercent < 0) + return; + + semacquire(&gcsema); + gosave(&g->sched); // update g's stack pointer for scanstack + stoptheworld(); + if(mheap.Lock.key != 0) + throw("mheap locked during gc"); + if(force || mstats.inuse_pages >= mstats.next_gc) { + mark(); + sweep(); + mstats.next_gc = mstats.inuse_pages+mstats.inuse_pages*gcpercent/100; + } + starttheworld(); + gosave(&g->sched); // update g's stack pointer for debugging + semrelease(&gcsema); +} diff --git a/src/lib/runtime/mheap.c b/src/lib/runtime/mheap.c new file mode 100644 index 000000000..d0cf2237b --- /dev/null +++ b/src/lib/runtime/mheap.c @@ -0,0 +1,333 @@ +// 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. + +// Page heap. +// +// See malloc.h for overview. +// +// When a MSpan is in the heap free list, state == MSpanFree +// and heapmap(s->start) == span, heapmap(s->start+s->npages-1) == span. +// +// When a MSpan is allocated, state == MSpanInUse +// and heapmap(i) == span for all s->start <= i < s->start+s->npages. + +#include "runtime.h" +#include "malloc.h" + +static MSpan *MHeap_AllocLocked(MHeap*, uintptr, int32); +static bool MHeap_Grow(MHeap*, uintptr); +static void MHeap_FreeLocked(MHeap*, MSpan*); +static MSpan *MHeap_AllocLarge(MHeap*, uintptr); +static MSpan *BestFit(MSpan*, uintptr, MSpan*); + +static void +RecordSpan(void *vh, byte *p) +{ + MHeap *h; + MSpan *s; + + h = vh; + s = (MSpan*)p; + s->allnext = h->allspans; + h->allspans = s; +} + +// Initialize the heap; fetch memory using alloc. +void +MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) +{ + uint32 i; + + FixAlloc_Init(&h->spanalloc, sizeof(MSpan), alloc, RecordSpan, h); + FixAlloc_Init(&h->cachealloc, sizeof(MCache), alloc, nil, nil); + MHeapMap_Init(&h->map, alloc); + // h->mapcache needs no init + for(i=0; i<nelem(h->free); i++) + MSpanList_Init(&h->free[i]); + MSpanList_Init(&h->large); + for(i=0; i<nelem(h->central); i++) + MCentral_Init(&h->central[i], i); +} + +// Allocate a new span of npage pages from the heap +// and record its size class in the HeapMap and HeapMapCache. +MSpan* +MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass) +{ + MSpan *s; + + lock(h); + s = MHeap_AllocLocked(h, npage, sizeclass); + if(s != nil) + mstats.inuse_pages += npage; + unlock(h); + return s; +} + +static MSpan* +MHeap_AllocLocked(MHeap *h, uintptr npage, int32 sizeclass) +{ + uintptr n; + MSpan *s, *t; + + // Try in fixed-size lists up to max. + for(n=npage; n < nelem(h->free); n++) { + if(!MSpanList_IsEmpty(&h->free[n])) { + s = h->free[n].next; + goto HaveSpan; + } + } + + // Best fit in list of large spans. + if((s = MHeap_AllocLarge(h, npage)) == nil) { + if(!MHeap_Grow(h, npage)) + return nil; + if((s = MHeap_AllocLarge(h, npage)) == nil) + return nil; + } + +HaveSpan: + // Mark span in use. + if(s->state != MSpanFree) + throw("MHeap_AllocLocked - MSpan not free"); + if(s->npages < npage) + throw("MHeap_AllocLocked - bad npages"); + MSpanList_Remove(s); + s->state = MSpanInUse; + + if(s->npages > npage) { + // Trim extra and put it back in the heap. + t = FixAlloc_Alloc(&h->spanalloc); + MSpan_Init(t, s->start + npage, s->npages - npage); + s->npages = npage; + MHeapMap_Set(&h->map, t->start - 1, s); + MHeapMap_Set(&h->map, t->start, t); + MHeapMap_Set(&h->map, t->start + t->npages - 1, t); + t->state = MSpanInUse; + MHeap_FreeLocked(h, t); + } + + // If span is being used for small objects, cache size class. + // No matter what, cache span info, because gc needs to be + // able to map interior pointer to containing span. + s->sizeclass = sizeclass; + for(n=0; n<npage; n++) + MHeapMap_Set(&h->map, s->start+n, s); + if(sizeclass == 0) { + uintptr tmp; + + // If there are entries for this span, invalidate them, + // but don't blow out cache entries about other spans. + for(n=0; n<npage; n++) + if(MHeapMapCache_GET(&h->mapcache, s->start+n, tmp) != 0) + MHeapMapCache_SET(&h->mapcache, s->start+n, 0); + } else { + // Save cache entries for this span. + // If there's a size class, there aren't that many pages. + for(n=0; n<npage; n++) + MHeapMapCache_SET(&h->mapcache, s->start+n, sizeclass); + } + + return s; +} + +// Allocate a span of exactly npage pages from the list of large spans. +static MSpan* +MHeap_AllocLarge(MHeap *h, uintptr npage) +{ + return BestFit(&h->large, npage, nil); +} + +// Search list for smallest span with >= npage pages. +// If there are multiple smallest spans, take the one +// with the earliest starting address. +static MSpan* +BestFit(MSpan *list, uintptr npage, MSpan *best) +{ + MSpan *s; + + for(s=list->next; s != list; s=s->next) { + if(s->npages < npage) + continue; + if(best == nil + || s->npages < best->npages + || (s->npages == best->npages && s->start < best->start)) + best = s; + } + return best; +} + +// Try to add at least npage pages of memory to the heap, +// returning whether it worked. +static bool +MHeap_Grow(MHeap *h, uintptr npage) +{ + uintptr ask; + void *v; + MSpan *s; + + // Ask for a big chunk, to reduce the number of mappings + // the operating system needs to track; also amortizes + // the overhead of an operating system mapping. + ask = npage<<PageShift; + if(ask < HeapAllocChunk) + ask = HeapAllocChunk; + + v = SysAlloc(ask); + if(v == nil) { + if(ask > (npage<<PageShift)) { + ask = npage<<PageShift; + v = SysAlloc(ask); + } + if(v == nil) + return false; + } + + // NOTE(rsc): In tcmalloc, if we've accumulated enough + // system allocations, the heap map gets entirely allocated + // in 32-bit mode. (In 64-bit mode that's not practical.) + + if(!MHeapMap_Preallocate(&h->map, ((uintptr)v>>PageShift) - 1, (ask>>PageShift) + 2)) { + SysFree(v, ask); + return false; + } + + // Create a fake "in use" span and free it, so that the + // right coalescing happens. + s = FixAlloc_Alloc(&h->spanalloc); + MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift); + MHeapMap_Set(&h->map, s->start, s); + MHeapMap_Set(&h->map, s->start + s->npages - 1, s); + s->state = MSpanInUse; + MHeap_FreeLocked(h, s); + return true; +} + +// Look up the span at the given page number. +// Page number is guaranteed to be in map +// and is guaranteed to be start or end of span. +MSpan* +MHeap_Lookup(MHeap *h, PageID p) +{ + return MHeapMap_Get(&h->map, p); +} + +// Look up the span at the given page number. +// Page number is *not* guaranteed to be in map +// and may be anywhere in the span. +// Map entries for the middle of a span are only +// valid for allocated spans. Free spans may have +// other garbage in their middles, so we have to +// check for that. +MSpan* +MHeap_LookupMaybe(MHeap *h, PageID p) +{ + MSpan *s; + + s = MHeapMap_GetMaybe(&h->map, p); + if(s == nil || p < s->start || p - s->start >= s->npages) + return nil; + if(s->state != MSpanInUse) + return nil; + return s; +} + +// Free the span back into the heap. +void +MHeap_Free(MHeap *h, MSpan *s) +{ + lock(h); + mstats.inuse_pages -= s->npages; + MHeap_FreeLocked(h, s); + unlock(h); +} + +static void +MHeap_FreeLocked(MHeap *h, MSpan *s) +{ + MSpan *t; + + if(s->state != MSpanInUse || s->ref != 0) { + printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d\n", s, s->start<<PageShift, s->state, s->ref); + throw("MHeap_FreeLocked - invalid free"); + } + s->state = MSpanFree; + MSpanList_Remove(s); + + // Coalesce with earlier, later spans. + if((t = MHeapMap_Get(&h->map, s->start - 1)) != nil && t->state != MSpanInUse) { + s->start = t->start; + s->npages += t->npages; + MHeapMap_Set(&h->map, s->start, s); + MSpanList_Remove(t); + t->state = MSpanDead; + FixAlloc_Free(&h->spanalloc, t); + } + if((t = MHeapMap_Get(&h->map, s->start + s->npages)) != nil && t->state != MSpanInUse) { + s->npages += t->npages; + MHeapMap_Set(&h->map, s->start + s->npages - 1, s); + MSpanList_Remove(t); + t->state = MSpanDead; + FixAlloc_Free(&h->spanalloc, t); + } + + // Insert s into appropriate list. + if(s->npages < nelem(h->free)) + MSpanList_Insert(&h->free[s->npages], s); + else + MSpanList_Insert(&h->large, s); + + // TODO(rsc): IncrementalScavenge() to return memory to OS. +} + +// Initialize a new span with the given start and npages. +void +MSpan_Init(MSpan *span, PageID start, uintptr npages) +{ + span->next = nil; + span->prev = nil; + span->start = start; + span->npages = npages; + span->freelist = nil; + span->ref = 0; + span->sizeclass = 0; + span->state = 0; +} + +// Initialize an empty doubly-linked list. +void +MSpanList_Init(MSpan *list) +{ + list->state = MSpanListHead; + list->next = list; + list->prev = list; +} + +void +MSpanList_Remove(MSpan *span) +{ + if(span->prev == nil && span->next == nil) + return; + span->prev->next = span->next; + span->next->prev = span->prev; + span->prev = nil; + span->next = nil; +} + +bool +MSpanList_IsEmpty(MSpan *list) +{ + return list->next == list; +} + +void +MSpanList_Insert(MSpan *list, MSpan *span) +{ + if(span->next != nil || span->prev != nil) + throw("MSpanList_Insert"); + span->next = list->next; + span->prev = list; + span->next->prev = span; + span->prev->next = span; +} diff --git a/src/lib/runtime/mheapmap32.c b/src/lib/runtime/mheapmap32.c new file mode 100644 index 000000000..420ca2d83 --- /dev/null +++ b/src/lib/runtime/mheapmap32.c @@ -0,0 +1,96 @@ +// 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. + +// Heap map, 32-bit version +// See malloc.h and mheap.c for overview. + +#include "runtime.h" +#include "malloc.h" + +// 3-level radix tree mapping page ids to Span*. +void +MHeapMap_Init(MHeapMap *m, void *(*allocator)(size_t)) +{ + m->allocator = allocator; +} + +MSpan* +MHeapMap_Get(MHeapMap *m, PageID k) +{ + int32 i1, i2; + + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + return m->p[i1]->s[i2]; +} + +MSpan* +MHeapMap_GetMaybe(MHeapMap *m, PageID k) +{ + int32 i1, i2; + MHeapMapNode2 *p2; + + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + p2 = m->p[i1]; + if(p2 == nil) + return nil; + return p2->s[i2]; +} + +void +MHeapMap_Set(MHeapMap *m, PageID k, MSpan *s) +{ + int32 i1, i2; + + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Set"); + + m->p[i1]->s[i2] = s; +} + +// Allocate the storage required for entries [k, k+1, ..., k+len-1] +// so that Get and Set calls need not check for nil pointers. +bool +MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr len) +{ + uintptr end; + int32 i1; + MHeapMapNode2 *p2; + + end = k+len; + while(k < end) { + if((k >> MHeapMap_TotalBits) != 0) + return false; + i1 = (k >> MHeapMap_Level2Bits) & MHeapMap_Level1Mask; + + // first-level pointer + if(m->p[i1] == nil) { + p2 = m->allocator(sizeof *p2); + if(p2 == nil) + return false; + sys_memclr((byte*)p2, sizeof *p2); + m->p[i1] = p2; + } + + // advance key past this leaf node + k = ((k >> MHeapMap_Level2Bits) + 1) << MHeapMap_Level2Bits; + } + return true; +} + diff --git a/src/lib/runtime/mheapmap32.h b/src/lib/runtime/mheapmap32.h new file mode 100644 index 000000000..0a16ccd10 --- /dev/null +++ b/src/lib/runtime/mheapmap32.h @@ -0,0 +1,76 @@ +// 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. + +// Free(v) must be able to determine the MSpan containing v. +// The MHeapMap is a 2-level radix tree mapping page numbers to MSpans. + +typedef struct MHeapMapNode2 MHeapMapNode2; + +enum +{ + // 32 bit address - 12 bit page size = 20 bits to map + MHeapMap_Level1Bits = 10, + MHeapMap_Level2Bits = 10, + + MHeapMap_TotalBits = + MHeapMap_Level1Bits + + MHeapMap_Level2Bits, + + MHeapMap_Level1Mask = (1<<MHeapMap_Level1Bits) - 1, + MHeapMap_Level2Mask = (1<<MHeapMap_Level2Bits) - 1, +}; + +struct MHeapMap +{ + void *(*allocator)(uintptr); + MHeapMapNode2 *p[1<<MHeapMap_Level1Bits]; +}; + +struct MHeapMapNode2 +{ + MSpan *s[1<<MHeapMap_Level2Bits]; +}; + +void MHeapMap_Init(MHeapMap *m, void *(*allocator)(uintptr)); +bool MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr npages); +MSpan* MHeapMap_Get(MHeapMap *m, PageID k); +MSpan* MHeapMap_GetMaybe(MHeapMap *m, PageID k); +void MHeapMap_Set(MHeapMap *m, PageID k, MSpan *v); + + +// Much of the time, free(v) needs to know only the size class for v, +// not which span it came from. The MHeapMap finds the size class +// by looking up the span. +// +// An MHeapMapCache is a simple direct-mapped cache translating +// page numbers to size classes. It avoids the expensive MHeapMap +// lookup for hot pages. +// +// The cache entries are 32 bits, with the page number in the low part +// and the value at the top. +// +// NOTE(rsc): On a machine with 32-bit addresses (= 20-bit page numbers), +// we can use a 16-bit cache entry by not storing the redundant 12 bits +// of the key that are used as the entry index. For now, keep it simple. +enum +{ + MHeapMapCache_HashBits = 12 +}; + +struct MHeapMapCache +{ + uint32 array[1<<MHeapMapCache_HashBits]; +}; + +// All macros for speed (sorry). +#define HMASK ((1<<MHeapMapCache_HashBits)-1) +#define KBITS MHeapMap_TotalBits +#define KMASK ((1LL<<KBITS)-1) + +#define MHeapMapCache_SET(cache, key, value) \ + ((cache)->array[(key) & HMASK] = (key) | ((uintptr)(value) << KBITS)) + +#define MHeapMapCache_GET(cache, key, tmp) \ + (tmp = (cache)->array[(key) & HMASK], \ + (tmp & KMASK) == (key) ? (tmp >> KBITS) : 0) diff --git a/src/lib/runtime/mheapmap64.c b/src/lib/runtime/mheapmap64.c new file mode 100644 index 000000000..1886ba529 --- /dev/null +++ b/src/lib/runtime/mheapmap64.c @@ -0,0 +1,117 @@ +// 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. + +// Heap map, 64-bit version +// See malloc.h and mheap.c for overview. + +#include "runtime.h" +#include "malloc.h" + +// 3-level radix tree mapping page ids to Span*. +void +MHeapMap_Init(MHeapMap *m, void *(*allocator)(size_t)) +{ + m->allocator = allocator; +} + +MSpan* +MHeapMap_Get(MHeapMap *m, PageID k) +{ + int32 i1, i2, i3; + + i3 = k & MHeapMap_Level3Mask; + k >>= MHeapMap_Level3Bits; + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + return m->p[i1]->p[i2]->s[i3]; +} + +MSpan* +MHeapMap_GetMaybe(MHeapMap *m, PageID k) +{ + int32 i1, i2, i3; + MHeapMapNode2 *p2; + MHeapMapNode3 *p3; + + i3 = k & MHeapMap_Level3Mask; + k >>= MHeapMap_Level3Bits; + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + p2 = m->p[i1]; + if(p2 == nil) + return nil; + p3 = p2->p[i2]; + if(p3 == nil) + return nil; + return p3->s[i3]; +} + +void +MHeapMap_Set(MHeapMap *m, PageID k, MSpan *s) +{ + int32 i1, i2, i3; + + i3 = k & MHeapMap_Level3Mask; + k >>= MHeapMap_Level3Bits; + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Set"); + + m->p[i1]->p[i2]->s[i3] = s; +} + +// Allocate the storage required for entries [k, k+1, ..., k+len-1] +// so that Get and Set calls need not check for nil pointers. +bool +MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr len) +{ + uintptr end; + int32 i1, i2; + MHeapMapNode2 *p2; + MHeapMapNode3 *p3; + + end = k+len; + while(k < end) { + if((k >> MHeapMap_TotalBits) != 0) + return false; + i2 = (k >> MHeapMap_Level3Bits) & MHeapMap_Level2Mask; + i1 = (k >> (MHeapMap_Level3Bits + MHeapMap_Level2Bits)) & MHeapMap_Level1Mask; + + // first-level pointer + if((p2 = m->p[i1]) == nil) { + p2 = m->allocator(sizeof *p2); + if(p2 == nil) + return false; + sys_memclr((byte*)p2, sizeof *p2); + m->p[i1] = p2; + } + + // second-level pointer + if(p2->p[i2] == nil) { + p3 = m->allocator(sizeof *p3); + if(p3 == nil) + return false; + sys_memclr((byte*)p3, sizeof *p3); + p2->p[i2] = p3; + } + + // advance key past this leaf node + k = ((k >> MHeapMap_Level3Bits) + 1) << MHeapMap_Level3Bits; + } + return true; +} + diff --git a/src/lib/runtime/mheapmap64.h b/src/lib/runtime/mheapmap64.h new file mode 100644 index 000000000..127b773f7 --- /dev/null +++ b/src/lib/runtime/mheapmap64.h @@ -0,0 +1,96 @@ +// 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. + +// Free(v) must be able to determine the MSpan containing v. +// The MHeapMap is a 3-level radix tree mapping page numbers to MSpans. +// +// NOTE(rsc): On a 32-bit platform (= 20-bit page numbers), +// we can swap in a 2-level radix tree. +// +// NOTE(rsc): We use a 3-level tree because tcmalloc does, but +// having only three levels requires approximately 1 MB per node +// in the tree, making the minimum map footprint 3 MB. +// Using a 4-level tree would cut the minimum footprint to 256 kB. +// On the other hand, it's just virtual address space: most of +// the memory is never going to be touched, thus never paged in. + +typedef struct MHeapMapNode2 MHeapMapNode2; +typedef struct MHeapMapNode3 MHeapMapNode3; + +enum +{ + // 64 bit address - 12 bit page size = 52 bits to map + MHeapMap_Level1Bits = 18, + MHeapMap_Level2Bits = 18, + MHeapMap_Level3Bits = 16, + + MHeapMap_TotalBits = + MHeapMap_Level1Bits + + MHeapMap_Level2Bits + + MHeapMap_Level3Bits, + + MHeapMap_Level1Mask = (1<<MHeapMap_Level1Bits) - 1, + MHeapMap_Level2Mask = (1<<MHeapMap_Level2Bits) - 1, + MHeapMap_Level3Mask = (1<<MHeapMap_Level3Bits) - 1, +}; + +struct MHeapMap +{ + void *(*allocator)(uintptr); + MHeapMapNode2 *p[1<<MHeapMap_Level1Bits]; +}; + +struct MHeapMapNode2 +{ + MHeapMapNode3 *p[1<<MHeapMap_Level2Bits]; +}; + +struct MHeapMapNode3 +{ + MSpan *s[1<<MHeapMap_Level3Bits]; +}; + +void MHeapMap_Init(MHeapMap *m, void *(*allocator)(uintptr)); +bool MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr npages); +MSpan* MHeapMap_Get(MHeapMap *m, PageID k); +MSpan* MHeapMap_GetMaybe(MHeapMap *m, PageID k); +void MHeapMap_Set(MHeapMap *m, PageID k, MSpan *v); + + +// Much of the time, free(v) needs to know only the size class for v, +// not which span it came from. The MHeapMap finds the size class +// by looking up the span. +// +// An MHeapMapCache is a simple direct-mapped cache translating +// page numbers to size classes. It avoids the expensive MHeapMap +// lookup for hot pages. +// +// The cache entries are 64 bits, with the page number in the low part +// and the value at the top. +// +// NOTE(rsc): On a machine with 32-bit addresses (= 20-bit page numbers), +// we can use a 16-bit cache entry by not storing the redundant 12 bits +// of the key that are used as the entry index. Here in 64-bit land, +// that trick won't work unless the hash table has 2^28 entries. +enum +{ + MHeapMapCache_HashBits = 12 +}; + +struct MHeapMapCache +{ + uintptr array[1<<MHeapMapCache_HashBits]; +}; + +// All macros for speed (sorry). +#define HMASK ((1<<MHeapMapCache_HashBits)-1) +#define KBITS MHeapMap_TotalBits +#define KMASK ((1LL<<KBITS)-1) + +#define MHeapMapCache_SET(cache, key, value) \ + ((cache)->array[(key) & HMASK] = (key) | ((uintptr)(value) << KBITS)) + +#define MHeapMapCache_GET(cache, key, tmp) \ + (tmp = (cache)->array[(key) & HMASK], \ + (tmp & KMASK) == (key) ? (tmp >> KBITS) : 0) diff --git a/src/lib/runtime/msize.c b/src/lib/runtime/msize.c new file mode 100644 index 000000000..25e22637d --- /dev/null +++ b/src/lib/runtime/msize.c @@ -0,0 +1,165 @@ +// 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. + +// Malloc small size classes. +// +// See malloc.h for overview. +// +// The size classes are chosen so that rounding an allocation +// request up to the next size class wastes at most 12.5% (1.125x). +// +// Each size class has its own page count that gets allocated +// and chopped up when new objects of the size class are needed. +// That page count is chosen so that chopping up the run of +// pages into objects of the given size wastes at most 12.5% (1.125x) +// of the memory. It is not necessary that the cutoff here be +// the same as above. +// +// The two sources of waste multiply, so the worst possible case +// for the above constraints would be that allocations of some +// size might have a 26.6% (1.266x) overhead. +// In practice, only one of the wastes comes into play for a +// given size (sizes < 512 waste mainly on the round-up, +// sizes > 512 waste mainly on the page chopping). +// +// TODO(rsc): Compute max waste for any given size. + +#include "runtime.h" +#include "malloc.h" + +int32 class_to_size[NumSizeClasses]; +int32 class_to_allocnpages[NumSizeClasses]; +int32 class_to_transfercount[NumSizeClasses]; + +// The SizeToClass lookup is implemented using two arrays, +// one mapping sizes <= 1024 to their class and one mapping +// sizes >= 1024 and <= MaxSmallSize to their class. +// All objects are 8-aligned, so the first array is indexed by +// the size divided by 8 (rounded up). Objects >= 1024 bytes +// are 128-aligned, so the second array is indexed by the +// size divided by 128 (rounded up). The arrays are filled in +// by InitSizes. + +static int32 size_to_class8[1024/8 + 1]; +static int32 size_to_class128[(MaxSmallSize-1024)/128 + 1]; + +int32 +SizeToClass(int32 size) +{ + if(size > MaxSmallSize) + throw("SizeToClass - invalid size"); + if(size > 1024-8) + return size_to_class128[(size-1024+127) >> 7]; + return size_to_class8[(size+7)>>3]; +} + +void +InitSizes(void) +{ + int32 align, sizeclass, size, osize, nextsize, n; + uint32 i; + uintptr allocsize, npages; + + // Initialize the class_to_size table (and choose class sizes in the process). + class_to_size[0] = 0; + sizeclass = 1; // 0 means no class + align = 8; + for(size = align; size <= MaxSmallSize; size += align) { + if((size&(size-1)) == 0) { // bump alignment once in a while + if(size >= 2048) + align = 256; + else if(size >= 128) + align = size / 8; + else if(size >= 16) + align = 16; // required for x86 SSE instructions, if we want to use them + } + if((align&(align-1)) != 0) + throw("InitSizes - bug"); + + // Make the allocnpages big enough that + // the leftover is less than 1/8 of the total, + // so wasted space is at most 12.5%. + allocsize = PageSize; + osize = size + RefcountOverhead; + while(allocsize%osize > (allocsize/8)) + allocsize += PageSize; + npages = allocsize >> PageShift; + + // If the previous sizeclass chose the same + // allocation size and fit the same number of + // objects into the page, we might as well + // use just this size instead of having two + // different sizes. + if(sizeclass > 1 + && npages == class_to_allocnpages[sizeclass-1] + && allocsize/osize == allocsize/(class_to_size[sizeclass-1]+RefcountOverhead)) { + class_to_size[sizeclass-1] = size; + continue; + } + + class_to_allocnpages[sizeclass] = npages; + class_to_size[sizeclass] = size; + sizeclass++; + } + if(sizeclass != NumSizeClasses) { + printf("sizeclass=%d NumSizeClasses=%d\n", sizeclass, NumSizeClasses); + throw("InitSizes - bad NumSizeClasses"); + } + + // Initialize the size_to_class tables. + nextsize = 0; + for (sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { + for(; nextsize < 1024 && nextsize <= class_to_size[sizeclass]; nextsize+=8) + size_to_class8[nextsize/8] = sizeclass; + if(nextsize >= 1024) + for(; nextsize <= class_to_size[sizeclass]; nextsize += 128) + size_to_class128[(nextsize-1024)/128] = sizeclass; + } + + // Double-check SizeToClass. + if(0) { + for(n=0; n < MaxSmallSize; n++) { + sizeclass = SizeToClass(n); + if(sizeclass < 1 || sizeclass >= NumSizeClasses || class_to_size[sizeclass] < n) { + printf("size=%d sizeclass=%d class_to_size=%d\n", n, sizeclass, class_to_size[sizeclass]); + printf("incorrect SizeToClass"); + goto dump; + } + if(sizeclass > 1 && class_to_size[sizeclass-1] >= n) { + printf("size=%d sizeclass=%d class_to_size=%d\n", n, sizeclass, class_to_size[sizeclass]); + printf("SizeToClass too big"); + goto dump; + } + } + } + + // Initialize the class_to_transfercount table. + for(sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { + n = 64*1024 / class_to_size[sizeclass]; + if(n < 2) + n = 2; + if(n > 32) + n = 32; + class_to_transfercount[sizeclass] = n; + } + return; + +dump: + if(1){ + printf("NumSizeClasses=%d\n", NumSizeClasses); + printf("class_to_size:"); + for(sizeclass=0; sizeclass<NumSizeClasses; sizeclass++) + printf(" %d", class_to_size[sizeclass]); + printf("\n\n"); + printf("size_to_class8:"); + for(i=0; i<nelem(size_to_class8); i++) + printf(" %d=>%d(%d)\n", i*8, size_to_class8[i], class_to_size[size_to_class8[i]]); + printf("\n"); + printf("size_to_class128:"); + for(i=0; i<nelem(size_to_class128); i++) + printf(" %d=>%d(%d)\n", i*128, size_to_class128[i], class_to_size[size_to_class128[i]]); + printf("\n"); + } + throw("InitSizes failed"); +} diff --git a/src/lib/runtime/print.c b/src/lib/runtime/print.c new file mode 100644 index 000000000..5295e338d --- /dev/null +++ b/src/lib/runtime/print.c @@ -0,0 +1,268 @@ +// 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 +dump(byte *p, int32 n) +{ + int32 i; + + for(i=0; i<n; i++) { + sys·printpointer((byte*)(p[i]>>4)); + sys·printpointer((byte*)(p[i]&0xf)); + if((i&15) == 15) + prints("\n"); + else + prints(" "); + } + if(n & 15) + prints("\n"); +} + +void +prints(int8 *s) +{ + sys·write(1, s, findnull((byte*)s)); +} + +// Very simple printf. Only for debugging prints. +// Do not add to this without checking with Rob. +void +printf(int8 *s, ...) +{ + int8 *p, *lp; + byte *arg, *narg; + + lp = p = s; + arg = (byte*)(&s+1); + for(; *p; p++) { + if(*p != '%') + continue; + if(p > lp) + sys·write(1, lp, p-lp); + p++; + narg = nil; + switch(*p) { + case 'd': // 32-bit + case 'x': + narg = arg + 4; + break; + case 'D': // 64-bit + case 'X': + if(sizeof(uintptr) == 8 && ((uint32)(uint64)arg)&4) + arg += 4; + narg = arg + 8; + break; + case 'p': // pointer-sized + case 's': + if(sizeof(uintptr) == 8 && ((uint32)(uint64)arg)&4) + arg += 4; + narg = arg + sizeof(uintptr); + break; + case 'S': // pointer-aligned but bigger + if(sizeof(uintptr) == 8 && ((uint32)(uint64)arg)&4) + arg += 4; + narg = arg + sizeof(String); + break; + } + switch(*p) { + case 'd': + sys·printint(*(int32*)arg); + break; + case 'D': + sys·printint(*(int64*)arg); + break; + case 'x': + sys·printhex(*(int32*)arg); + break; + case 'X': + sys·printhex(*(int64*)arg); + break; + case 'p': + sys·printpointer(*(void**)arg); + break; + case 's': + prints(*(int8**)arg); + break; + case 'S': + sys·printstring(*(String*)arg); + break; + } + arg = narg; + lp = p+1; + } + if(p > lp) + sys·write(1, lp, p-lp); +} + + +void +sys·printpc(void *p) +{ + prints("PC="); + sys·printhex((uint64)sys·getcallerpc(p)); +} + +void +sys·printbool(bool v) +{ + if(v) { + sys·write(1, (byte*)"true", 4); + return; + } + sys·write(1, (byte*)"false", 5); +} + +void +sys·printfloat(float64 v) +{ + byte buf[20]; + int32 e, s, i, n; + float64 h; + + if(isNaN(v)) { + sys·write(1, "NaN", 3); + return; + } + if(isInf(v, 0)) { + sys·write(1, "+Inf", 4); + return; + } + if(isInf(v, -1)) { + sys·write(1, "+Inf", 4); + return; + } + + + n = 7; // digits printed + e = 0; // exp + s = 0; // sign + if(v != 0) { + // sign + if(v < 0) { + v = -v; + s = 1; + } + + // normalize + while(v >= 10) { + e++; + v /= 10; + } + while(v < 1) { + e--; + v *= 10; + } + + // round + h = 5; + for(i=0; i<n; i++) + h /= 10; + v += h; + if(v >= 10) { + e++; + v /= 10; + } + } + + // format +d.dddd+edd + buf[0] = '+'; + if(s) + buf[0] = '-'; + for(i=0; i<n; i++) { + s = v; + buf[i+2] = s+'0'; + v -= s; + v *= 10.; + } + buf[1] = buf[2]; + buf[2] = '.'; + + buf[n+2] = 'e'; + buf[n+3] = '+'; + if(e < 0) { + e = -e; + buf[n+3] = '-'; + } + + buf[n+4] = (e/100) + '0'; + buf[n+5] = (e/10)%10 + '0'; + buf[n+6] = (e%10) + '0'; + sys·write(1, buf, n+7); +} + +void +sys·printuint(uint64 v) +{ + byte buf[100]; + int32 i; + + for(i=nelem(buf)-1; i>0; i--) { + buf[i] = v%10 + '0'; + if(v < 10) + break; + v = v/10; + } + sys·write(1, buf+i, nelem(buf)-i); +} + +void +sys·printint(int64 v) +{ + if(v < 0) { + sys·write(1, "-", 1); + v = -v; + } + sys·printuint(v); +} + +void +sys·printhex(uint64 v) +{ + static int8 *dig = "0123456789abcdef"; + byte buf[100]; + int32 i; + + i=nelem(buf); + for(; v>0; v/=16) + buf[--i] = dig[v%16]; + if(i == nelem(buf)) + buf[--i] = '0'; + buf[--i] = 'x'; + buf[--i] = '0'; + sys·write(1, buf+i, nelem(buf)-i); +} + +void +sys·printpointer(void *p) +{ + sys·printhex((uint64)p); +} + +void +sys·printstring(String v) +{ + extern int32 maxstring; + + if(v.len > maxstring) { + sys·write(1, "[invalid string]", 16); + return; + } + if(v.len > 0) + sys·write(1, v.str, v.len); +} + +void +sys·printsp(void) +{ + sys·write(1, " ", 1); +} + +void +sys·printnl(void) +{ + sys·write(1, "\n", 1); +} diff --git a/src/lib/runtime/proc.c b/src/lib/runtime/proc.c new file mode 100644 index 000000000..1d065e6d2 --- /dev/null +++ b/src/lib/runtime/proc.c @@ -0,0 +1,858 @@ +// 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" +#include "malloc.h" + +typedef struct Sched Sched; + +M m0; +G g0; // idle goroutine for m0 + +static int32 debug = 0; +static Lock debuglock; + +// Go scheduler +// +// The go scheduler's job is to match ready-to-run goroutines (`g's) +// with waiting-for-work schedulers (`m's). If there are ready gs +// and no waiting ms, ready() will start a new m running in a new +// OS thread, so that all ready gs can run simultaneously, up to a limit. +// For now, ms never go away. +// +// The default maximum number of ms is one: go runs single-threaded. +// This is because some locking details have to be worked ou +// (select in particular is not locked properly) and because the low-level +// code hasn't been written yet for OS X. Setting the environmen +// variable $gomaxprocs changes sched.mmax for now. +// +// Even a program that can run without deadlock in a single process +// might use more ms if given the chance. For example, the prime +// sieve will use as many ms as there are primes (up to sched.mmax), +// allowing different stages of the pipeline to execute in parallel. +// We could revisit this choice, only kicking off new ms for blocking +// system calls, but that would limit the amount of parallel computation +// that go would try to do. +// +// In general, one could imagine all sorts of refinements to the +// scheduler, but the goal now is just to get something working on +// Linux and OS X. + +struct Sched { + Lock; + + G *gfree; // available gs (status == Gdead) + + G *ghead; // gs waiting to run + G *gtail; + int32 gwait; // number of gs waiting to run + int32 gcount; // number of gs that are alive + + M *mhead; // ms waiting for work + int32 mwait; // number of ms waiting for work + int32 mcount; // number of ms that have been created + int32 mcpu; // number of ms executing on cpu + int32 mcpumax; // max number of ms allowed on cpu + int32 gomaxprocs; + int32 msyscall; // number of ms in system calls + + int32 predawn; // running initialization, don't run new gs. + + Note stopped; // one g can wait here for ms to stop + int32 waitstop; // after setting this flag +}; + +Sched sched; + +// Scheduling helpers. Sched must be locked. +static void gput(G*); // put/get on ghead/gtail +static G* gget(void); +static void mput(M*); // put/get on mhead +static M* mget(void); +static void gfput(G*); // put/get on gfree +static G* gfget(void); +static void matchmg(void); // match ms to gs +static void readylocked(G*); // ready, but sched is locked + +// Scheduler loop. +static void scheduler(void); + +// The bootstrap sequence is: +// +// call osinit +// call schedinit +// make & queue new G +// call mstart +// +// The new G does: +// +// call main·init_function +// call initdone +// call main·main +void +schedinit(void) +{ + int32 n; + byte *p; + + mallocinit(); + goargs(); + + // Allocate internal symbol table representation now, + // so that we don't need to call malloc when we crash. + findfunc(0); + + sched.gomaxprocs = 1; + p = getenv("GOMAXPROCS"); + if(p != nil && (n = atoi(p)) != 0) + sched.gomaxprocs = n; + sched.mcpumax = sched.gomaxprocs; + sched.mcount = 1; + sched.predawn = 1; +} + +// Called after main·init_function; main·main will be called on return. +void +initdone(void) +{ + // Let's go. + sched.predawn = 0; + mstats.enablegc = 1; + + // If main·init_function started other goroutines, + // kick off new ms to handle them, like ready + // would have, had it not been pre-dawn. + lock(&sched); + matchmg(); + unlock(&sched); +} + +void +goexit(void) +{ + if(debug > 1){ + lock(&debuglock); + printf("goexit goid=%d\n", g->goid); + unlock(&debuglock); + } + g->status = Gmoribund; + gosched(); +} + +void +tracebackothers(G *me) +{ + G *g; + + for(g = allg; g != nil; g = g->alllink) { + if(g == me || g->status == Gdead) + continue; + printf("\ngoroutine %d:\n", g->goid); + traceback(g->sched.PC, g->sched.SP+sizeof(uintptr), g); // gogo adjusts SP by one word + } +} + +// Put on `g' queue. Sched must be locked. +static void +gput(G *g) +{ + g->schedlink = nil; + if(sched.ghead == nil) + sched.ghead = g; + else + sched.gtail->schedlink = g; + sched.gtail = g; + sched.gwait++; +} + +// Get from `g' queue. Sched must be locked. +static G* +gget(void) +{ + G *g; + + g = sched.ghead; + if(g){ + sched.ghead = g->schedlink; + if(sched.ghead == nil) + sched.gtail = nil; + sched.gwait--; + } + return g; +} + +// Put on `m' list. Sched must be locked. +static void +mput(M *m) +{ + m->schedlink = sched.mhead; + sched.mhead = m; + sched.mwait++; +} + +// Get from `m' list. Sched must be locked. +static M* +mget(void) +{ + M *m; + + m = sched.mhead; + if(m){ + sched.mhead = m->schedlink; + sched.mwait--; + } + return m; +} + +// Put on gfree list. Sched must be locked. +static void +gfput(G *g) +{ + g->schedlink = sched.gfree; + sched.gfree = g; +} + +// Get from gfree list. Sched must be locked. +static G* +gfget(void) +{ + G *g; + + g = sched.gfree; + if(g) + sched.gfree = g->schedlink; + return g; +} + +// Mark g ready to run. +void +ready(G *g) +{ + lock(&sched); + readylocked(g); + unlock(&sched); +} + +// Mark g ready to run. Sched is already locked. +// G might be running already and about to stop. +// The sched lock protects g->status from changing underfoot. +static void +readylocked(G *g) +{ + if(g->m){ + // Running on another machine. + // Ready it when it stops. + g->readyonstop = 1; + return; + } + + // Mark runnable. + if(g->status == Grunnable || g->status == Grunning) + throw("bad g->status in ready"); + g->status = Grunnable; + + gput(g); + if(!sched.predawn) + matchmg(); +} + +// Get the next goroutine that m should run. +// Sched must be locked on entry, is unlocked on exit. +// Makes sure that at most $GOMAXPROCS gs are +// running on cpus (not in system calls) at any given time. +static G* +nextgandunlock(void) +{ + G *gp; + + // On startup, each m is assigned a nextg and + // has already been accounted for in mcpu. + if(m->nextg != nil) { + gp = m->nextg; + m->nextg = nil; + unlock(&sched); + if(debug > 1) { + lock(&debuglock); + printf("m%d nextg found g%d\n", m->id, gp->goid); + unlock(&debuglock); + } + return gp; + } + + // Otherwise, look for work. + if(sched.mcpu < sched.mcpumax && (gp=gget()) != nil) { + sched.mcpu++; + unlock(&sched); + if(debug > 1) { + lock(&debuglock); + printf("m%d nextg got g%d\n", m->id, gp->goid); + unlock(&debuglock); + } + return gp; + } + + // Otherwise, sleep. + mput(m); + if(sched.mcpu == 0 && sched.msyscall == 0) + throw("all goroutines are asleep - deadlock!"); + m->nextg = nil; + noteclear(&m->havenextg); + if(sched.waitstop && sched.mcpu <= sched.mcpumax) { + sched.waitstop = 0; + notewakeup(&sched.stopped); + } + unlock(&sched); + + notesleep(&m->havenextg); + if((gp = m->nextg) == nil) + throw("bad m->nextg in nextgoroutine"); + m->nextg = nil; + if(debug > 1) { + lock(&debuglock); + printf("m%d nextg woke g%d\n", m->id, gp->goid); + unlock(&debuglock); + } + return gp; +} + +// TODO(rsc): Remove. This is only temporary, +// for the mark and sweep collector. +void +stoptheworld(void) +{ + lock(&sched); + sched.mcpumax = 1; + while(sched.mcpu > 1) { + noteclear(&sched.stopped); + sched.waitstop = 1; + unlock(&sched); + notesleep(&sched.stopped); + lock(&sched); + } + unlock(&sched); +} + +// TODO(rsc): Remove. This is only temporary, +// for the mark and sweep collector. +void +starttheworld(void) +{ + lock(&sched); + sched.mcpumax = sched.gomaxprocs; + matchmg(); + unlock(&sched); +} + +// Called to start an M. +void +mstart(void) +{ + if(m->mcache == nil) + m->mcache = allocmcache(); + minit(); + scheduler(); +} + +// Kick of new ms as needed (up to mcpumax). +// There are already `other' other cpus that will +// start looking for goroutines shortly. +// Sched is locked. +static void +matchmg(void) +{ + M *m; + G *g; + + if(debug > 1 && sched.ghead != nil) { + lock(&debuglock); + printf("matchmg mcpu=%d mcpumax=%d gwait=%d\n", sched.mcpu, sched.mcpumax, sched.gwait); + unlock(&debuglock); + } + + while(sched.mcpu < sched.mcpumax && (g = gget()) != nil){ + sched.mcpu++; + if((m = mget()) != nil){ + if(debug > 1) { + lock(&debuglock); + printf("wakeup m%d g%d\n", m->id, g->goid); + unlock(&debuglock); + } + m->nextg = g; + notewakeup(&m->havenextg); + }else{ + m = malloc(sizeof(M)); + m->g0 = malg(8192); + m->nextg = g; + m->id = sched.mcount++; + if(debug) { + lock(&debuglock); + printf("alloc m%d g%d\n", m->id, g->goid); + unlock(&debuglock); + } + newosproc(m, m->g0, m->g0->stackbase, mstart); + } + } +} + +// Scheduler loop: find g to run, run it, repeat. +static void +scheduler(void) +{ + G* gp; + + lock(&sched); + if(gosave(&m->sched)){ + // Jumped here via gosave/gogo, so didn't + // execute lock(&sched) above. + lock(&sched); + + if(sched.predawn) + throw("init sleeping"); + + // Just finished running m->curg. + gp = m->curg; + gp->m = nil; + sched.mcpu--; + if(debug > 1) { + lock(&debuglock); + printf("m%d sched g%d status %d\n", m->id, gp->goid, gp->status); + unlock(&debuglock); + } + switch(gp->status){ + case Grunnable: + case Gdead: + // Shouldn't have been running! + throw("bad gp->status in sched"); + case Grunning: + gp->status = Grunnable; + gput(gp); + break; + case Gmoribund: + gp->status = Gdead; + if(--sched.gcount == 0) + exit(0); + break; + } + if(gp->readyonstop){ + gp->readyonstop = 0; + readylocked(gp); + } + } + + // Find (or wait for) g to run. Unlocks sched. + gp = nextgandunlock(); + gp->readyonstop = 0; + gp->status = Grunning; + if(debug > 1) { + lock(&debuglock); + printf("m%d run g%d at %p\n", m->id, gp->goid, gp->sched.PC); + traceback(gp->sched.PC, gp->sched.SP+8, gp); + unlock(&debuglock); + } + m->curg = gp; + gp->m = m; + g = gp; + gogo(&gp->sched); +} + +// Enter scheduler. If g->status is Grunning, +// re-queues g and runs everyone else who is waiting +// before running g again. If g->status is Gmoribund, +// kills off g. +void +gosched(void) +{ + if(g == m->g0) + throw("gosched of g0"); + if(gosave(&g->sched) == 0){ + g = m->g0; + gogo(&m->sched); + } +} + +// The goroutine g is about to enter a system call. +// Record that it's not using the cpu anymore. +// This is called only from the go syscall library, not +// from the low-level system calls used by the runtime. +// The "arguments" are syscall.Syscall's stack frame +void +sys·entersyscall(uint64 callerpc, int64 trap) +{ + USED(callerpc); + + if(debug > 1) { + lock(&debuglock); + printf("m%d g%d enter syscall %D\n", m->id, g->goid, trap); + unlock(&debuglock); + } + lock(&sched); + g->status = Gsyscall; + sched.mcpu--; + sched.msyscall++; + if(sched.gwait != 0) + matchmg(); + if(sched.waitstop && sched.mcpu <= sched.mcpumax) { + sched.waitstop = 0; + notewakeup(&sched.stopped); + } + unlock(&sched); + // leave SP around for gc and traceback + gosave(&g->sched); +} + +// The goroutine g exited its system call. +// Arrange for it to run on a cpu again. +// This is called only from the go syscall library, not +// from the low-level system calls used by the runtime. +void +sys·exitsyscall(void) +{ + if(debug > 1) { + lock(&debuglock); + printf("m%d g%d exit syscall mcpu=%d mcpumax=%d\n", m->id, g->goid, sched.mcpu, sched.mcpumax); + unlock(&debuglock); + } + + lock(&sched); + g->status = Grunning; + sched.msyscall--; + sched.mcpu++; + // Fast path - if there's room for this m, we're done. + if(sched.mcpu <= sched.mcpumax) { + unlock(&sched); + return; + } + unlock(&sched); + + // Slow path - all the cpus are taken. + // The scheduler will ready g and put this m to sleep. + // When the scheduler takes g awa from m, + // it will undo the sched.mcpu++ above. + gosched(); +} + +/* + * stack layout parameters. + * known to linkers. + * + * g->stackguard is set to point StackGuard bytes + * above the bottom of the stack. each function + * compares its stack pointer against g->stackguard + * to check for overflow. to cut one instruction from + * the check sequence for functions with tiny frames, + * the stack is allowed to protrude StackSmall bytes + * below the stack guard. functions with large frames + * don't bother with the check and always call morestack. + * the sequences are: + * + * guard = g->stackguard + * frame = function's stack frame size + * argsize = size of function arguments (call + return) + * + * stack frame size <= StackSmall: + * CMPQ guard, SP + * JHI 3(PC) + * MOVQ m->morearg, $(argsize << 32) + * CALL sys.morestack(SB) + * + * stack frame size > StackSmall but < StackBig + * LEAQ (frame-StackSmall)(SP), R0 + * CMPQ guard, R0 + * JHI 3(PC) + * MOVQ m->morearg, $(argsize << 32) + * CALL sys.morestack(SB) + * + * stack frame size >= StackBig: + * MOVQ m->morearg, $((argsize << 32) | frame) + * CALL sys.morestack(SB) + * + * the bottom StackGuard - StackSmall bytes are important: + * there has to be enough room to execute functions that + * refuse to check for stack overflow, either because they + * need to be adjacent to the actual caller's frame (sys.deferproc) + * or because they handle the imminent stack overflow (sys.morestack). + * + * for example, sys.deferproc might call malloc, + * which does one of the above checks (without allocating a full frame), + * which might trigger a call to sys.morestack. + * this sequence needs to fit in the bottom section of the stack. + * on amd64, sys.morestack's frame is 40 bytes, and + * sys.deferproc's frame is 56 bytes. that fits well within + * the StackGuard - StackSmall = 128 bytes at the bottom. + * there may be other sequences lurking or yet to be written + * that require more stack. sys.morestack checks to make sure + * the stack has not completely overflowed and should + * catch such sequences. + */ +enum +{ + // byte offset of stack guard (g->stackguard) above bottom of stack. + StackGuard = 256, + + // checked frames are allowed to protrude below the guard by + // this many bytes. this saves an instruction in the checking + // sequence when the stack frame is tiny. + StackSmall = 128, + + // extra space in the frame (beyond the function for which + // the frame is allocated) is assumed not to be much bigger + // than this amount. it may not be used efficiently if it is. + StackBig = 4096, +}; + +void +oldstack(void) +{ + Stktop *top; + uint32 args; + byte *sp; + uintptr oldsp, oldpc, oldbase, oldguard; + +// printf("oldstack m->cret=%p\n", m->cret); + + top = (Stktop*)m->curg->stackbase; + + args = (top->magic>>32) & 0xffffLL; + + sp = (byte*)top; + if(args > 0) { + args = (args+7) & ~7; + sp -= args; + mcpy(top->oldsp+2*sizeof(uintptr), sp, args); + } + + oldsp = (uintptr)top->oldsp + sizeof(uintptr); + oldpc = *(uintptr*)oldsp; + oldbase = (uintptr)top->oldbase; + oldguard = (uintptr)top->oldguard; + + stackfree((byte*)m->curg->stackguard - StackGuard); + + m->curg->stackbase = (byte*)oldbase; + m->curg->stackguard = (byte*)oldguard; + m->morestack.SP = (byte*)oldsp; + m->morestack.PC = (byte*)oldpc; + + // These two lines must happen in sequence; + // once g has been changed, must switch to g's stack + // before calling any non-assembly functions. + // TODO(rsc): Perhaps make the new g a parameter + // to gogoret and setspgoto, so that g is never + // explicitly assigned to without also setting + // the stack pointer. + g = m->curg; + gogoret(&m->morestack, m->cret); +} + +#pragma textflag 7 +void +lessstack(void) +{ + g = m->g0; + setspgoto(m->sched.SP, oldstack, nil); +} + +void +newstack(void) +{ + int32 frame, args; + Stktop *top; + byte *stk, *sp; + void (*fn)(void); + + frame = m->morearg & 0xffffffffLL; + args = (m->morearg>>32) & 0xffffLL; + +// printf("newstack frame=%d args=%d moresp=%p morepc=%p\n", frame, args, m->moresp, *(uintptr*)m->moresp); + + if(frame < StackBig) + frame = StackBig; + frame += 1024; // for more functions, Stktop. + stk = stackalloc(frame); + + top = (Stktop*)(stk+frame-sizeof(*top)); + + top->oldbase = m->curg->stackbase; + top->oldguard = m->curg->stackguard; + top->oldsp = m->moresp; + top->magic = m->morearg; + + m->curg->stackbase = (byte*)top; + m->curg->stackguard = stk + StackGuard; + + sp = (byte*)top; + + if(args > 0) { + // Copy args. There have been two function calls + // since they got pushed, so skip over those return + // addresses. + args = (args+7) & ~7; + sp -= args; + mcpy(sp, m->moresp+2*sizeof(uintptr), args); + } + + g = m->curg; + + // sys.morestack's return address + fn = (void(*)(void))(*(uintptr*)m->moresp); + +// printf("fn=%p\n", fn); + + setspgoto(sp, fn, retfromnewstack); + + *(int32*)345 = 123; // never return +} + +#pragma textflag 7 +void +sys·morestack(uintptr u) +{ + while(g == m->g0) { + // very bad news + *(int32*)0x1001 = 123; + } + + // Morestack's frame is about 0x30 bytes on amd64. + // If that the frame ends below the stack bottom, we've already + // overflowed. Stop right now. + while((byte*)&u - 0x30 < m->curg->stackguard - StackGuard) { + // very bad news + *(int32*)0x1002 = 123; + } + + g = m->g0; + m->moresp = (byte*)(&u-1); + setspgoto(m->sched.SP, newstack, nil); + + *(int32*)0x1003 = 123; // never return +} + +G* +malg(int32 stacksize) +{ + G *g; + byte *stk; + + g = malloc(sizeof(G)); + stk = stackalloc(stacksize + StackGuard); + g->stack0 = stk; + g->stackguard = stk + StackGuard; + g->stackbase = stk + StackGuard + stacksize; + return g; +} + +/* + * Newproc and deferproc need to be textflag 7 + * (no possible stack split when nearing overflow) + * because they assume that the arguments to fn + * are available sequentially beginning at &arg0. + * If a stack split happened, only the one word + * arg0 would be copied. It's okay if any functions + * they call split the stack below the newproc frame. + */ +#pragma textflag 7 +void +sys·newproc(int32 siz, byte* fn, byte* arg0) +{ + byte *stk, *sp; + G *newg; + +//printf("newproc siz=%d fn=%p", siz, fn); + + siz = (siz+7) & ~7; + if(siz > 1024) + throw("sys·newproc: too many args"); + + lock(&sched); + + if((newg = gfget()) != nil){ + newg->status = Gwaiting; + } else { + newg = malg(4096); + newg->status = Gwaiting; + newg->alllink = allg; + allg = newg; + } + stk = newg->stack0; + + newg->stackguard = stk+StackGuard; + + sp = stk + 4096 - 4*8; + newg->stackbase = sp; + + sp -= siz; + mcpy(sp, (byte*)&arg0, siz); + + sp -= sizeof(uintptr); + *(byte**)sp = (byte*)goexit; + + sp -= sizeof(uintptr); // retpc used by gogo + newg->sched.SP = sp; + newg->sched.PC = fn; + + sched.gcount++; + goidgen++; + newg->goid = goidgen; + + readylocked(newg); + unlock(&sched); + +//printf(" goid=%d\n", newg->goid); +} + +#pragma textflag 7 +void +sys·deferproc(int32 siz, byte* fn, byte* arg0) +{ + Defer *d; + + d = malloc(sizeof(*d) + siz - sizeof(d->args)); + d->fn = fn; + d->sp = (byte*)&arg0; + d->siz = siz; + mcpy(d->args, d->sp, d->siz); + + d->link = g->defer; + g->defer = d; +} + +#pragma textflag 7 +void +sys·deferreturn(uintptr arg0) +{ + Defer *d; + byte *sp, *fn; + uintptr *caller; + + d = g->defer; + if(d == nil) + return; + sp = (byte*)&arg0; + if(d->sp != sp) + return; + mcpy(d->sp, d->args, d->siz); + g->defer = d->link; + fn = d->fn; + free(d); + jmpdefer(fn, sp); + } + +void +runtime·Breakpoint(void) +{ + breakpoint(); +} + +void +runtime·Goexit(void) +{ + goexit(); +} + +void +runtime·Gosched(void) +{ + gosched(); +} + diff --git a/src/lib/runtime/rune.c b/src/lib/runtime/rune.c new file mode 100644 index 000000000..ca4f9ac6a --- /dev/null +++ b/src/lib/runtime/rune.c @@ -0,0 +1,238 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Portions Copyright 2009 The Go Authors. All rights reserved. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * This code is copied, with slight editing due to type differences, + * from a subset of ../lib9/utf/rune.c + */ + +#include "runtime.h" + +enum +{ + Bit1 = 7, + Bitx = 6, + Bit2 = 5, + Bit3 = 4, + Bit4 = 3, + Bit5 = 2, + + T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ + Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ + T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ + T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ + T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ + T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ + Rune4 = (1<<(Bit4+3*Bitx))-1, + /* 0001 1111 1111 1111 1111 1111 */ + + Maskx = (1<<Bitx)-1, /* 0011 1111 */ + Testx = Maskx ^ 0xFF, /* 1100 0000 */ + + Runeerror = 0xFFFD, + Runeself = 0x80, + + Bad = Runeerror, + + Runemax = 0x10FFFF, /* maximum rune value */ +}; + +/* + * Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24 + * This is a slower but "safe" version of the old chartorune + * that works on strings that are not necessarily null-terminated. + * + * If you know for sure that your string is null-terminated, + * chartorune will be a bit faster. + * + * It is guaranteed not to attempt to access "length" + * past the incoming pointer. This is to avoid + * possible access violations. If the string appears to be + * well-formed but incomplete (i.e., to get the whole Rune + * we'd need to read past str+length) then we'll set the Rune + * to Bad and return 0. + * + * Note that if we have decoding problems for other + * reasons, we return 1 instead of 0. + */ +int32 +charntorune(int32 *rune, uint8 *str, int32 length) +{ + int32 c, c1, c2, c3, l; + + /* When we're not allowed to read anything */ + if(length <= 0) { + goto badlen; + } + + /* + * one character sequence (7-bit value) + * 00000-0007F => T1 + */ + c = *(uint8*)str; + if(c < Tx) { + *rune = c; + return 1; + } + + // If we can't read more than one character we must stop + if(length <= 1) { + goto badlen; + } + + /* + * two character sequence (11-bit value) + * 0080-07FF => T2 Tx + */ + c1 = *(uint8*)(str+1) ^ Tx; + if(c1 & Testx) + goto bad; + if(c < T3) { + if(c < T2) + goto bad; + l = ((c << Bitx) | c1) & Rune2; + if(l <= Rune1) + goto bad; + *rune = l; + return 2; + } + + // If we can't read more than two characters we must stop + if(length <= 2) { + goto badlen; + } + + /* + * three character sequence (16-bit value) + * 0800-FFFF => T3 Tx Tx + */ + c2 = *(uint8*)(str+2) ^ Tx; + if(c2 & Testx) + goto bad; + if(c < T4) { + l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; + if(l <= Rune2) + goto bad; + *rune = l; + return 3; + } + + if (length <= 3) + goto badlen; + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + c3 = *(uint8*)(str+3) ^ Tx; + if (c3 & Testx) + goto bad; + if (c < T5) { + l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; + if (l <= Rune3 || l > Runemax) + goto bad; + *rune = l; + return 4; + } + + // Support for 5-byte or longer UTF-8 would go here, but + // since we don't have that, we'll just fall through to bad. + + /* + * bad decoding + */ +bad: + *rune = Bad; + return 1; +badlen: + *rune = Bad; + return 0; + +} + +int32 +runetochar(byte *str, int32 rune) /* note: in original, arg2 was pointer */ +{ + /* Runes are signed, so convert to unsigned for range check. */ + uint32 c; + + /* + * one character sequence + * 00000-0007F => 00-7F + */ + c = rune; + if(c <= Rune1) { + str[0] = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | (c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * If the Rune is out of range, convert it to the error rune. + * Do this test here because the error rune encodes to three bytes. + * Doing it earlier would duplicate work, since an out of range + * Rune wouldn't have fit in one or two bytes. + */ + if (c > Runemax) + c = Runeerror; + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + if (c <= Rune3) { + str[0] = T3 | (c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + str[0] = T4 | (c >> 3*Bitx); + str[1] = Tx | ((c >> 2*Bitx) & Maskx); + str[2] = Tx | ((c >> 1*Bitx) & Maskx); + str[3] = Tx | (c & Maskx); + return 4; +} diff --git a/src/lib/runtime/runtime.c b/src/lib/runtime/runtime.c new file mode 100644 index 000000000..c5ba3e6a5 --- /dev/null +++ b/src/lib/runtime/runtime.c @@ -0,0 +1,462 @@ +// 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" + +int32 panicking = 0; +int32 maxround = sizeof(uintptr); + +int32 +gotraceback(void) +{ + byte *p; + + p = getenv("GOTRACEBACK"); + if(p == nil || p[0] == '\0') + return 1; // default is on + return atoi(p); +} + +void +sys·panicl(int32 lno) +{ + uint8 *sp; + + if(panicking) { + printf("double panic\n"); + exit(3); + } + panicking++; + + printf("\npanic PC=%X\n", (uint64)(uintptr)&lno); + sp = (uint8*)&lno; + if(gotraceback()){ + traceback(sys·getcallerpc(&lno), sp, g); + tracebackothers(g); + } + breakpoint(); // so we can grab it in a debugger + exit(2); +} + +void +sys·throwindex(void) +{ + throw("index out of range"); +} + +void +sys·throwreturn(void) +{ + throw("no return at end of a typed function"); +} + +void +sys·throwinit(void) +{ + throw("recursive call during initialization"); +} + +void +throw(int8 *s) +{ + printf("throw: %s\n", s); + sys·panicl(-1); + *(int32*)0 = 0; // not reached + exit(1); // even more not reached +} + +void +mcpy(byte *t, byte *f, uint32 n) +{ + while(n > 0) { + *t = *f; + t++; + f++; + n--; + } +} + +int32 +mcmp(byte *s1, byte *s2, uint32 n) +{ + uint32 i; + byte c1, c2; + + for(i=0; i<n; i++) { + c1 = s1[i]; + c2 = s2[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + } + return 0; +} + + +void +mmov(byte *t, byte *f, uint32 n) +{ + if(t < f) { + while(n > 0) { + *t = *f; + t++; + f++; + n--; + } + } else { + t += n; + f += n; + while(n > 0) { + t--; + f--; + *t = *f; + n--; + } + } +} + +byte* +mchr(byte *p, byte c, byte *ep) +{ + for(; p < ep; p++) + if(*p == c) + return p; + return nil; +} + +uint32 +rnd(uint32 n, uint32 m) +{ + uint32 r; + + if(m > maxround) + m = maxround; + r = n % m; + if(r) + n += m-r; + return n; +} + +static int32 argc; +static uint8** argv; + +Array os·Args; +Array os·Envs; + +void +args(int32 c, uint8 **v) +{ + argc = c; + argv = v; +} + +void +goargs(void) +{ + String *gargv; + String *genvv; + int32 i, envc; + + for(envc=0; argv[argc+1+envc] != 0; envc++) + ; + + gargv = malloc(argc*sizeof gargv[0]); + genvv = malloc(envc*sizeof genvv[0]); + + for(i=0; i<argc; i++) + gargv[i] = gostring(argv[i]); + os·Args.array = (byte*)gargv; + os·Args.nel = argc; + os·Args.cap = argc; + + for(i=0; i<envc; i++) + genvv[i] = gostring(argv[argc+1+i]); + os·Envs.array = (byte*)genvv; + os·Envs.nel = envc; + os·Envs.cap = envc; +} + +byte* +getenv(int8 *s) +{ + int32 i, j, len; + byte *v, *bs; + String* envv; + int32 envc; + + bs = (byte*)s; + len = findnull(bs); + envv = (String*)os·Envs.array; + envc = os·Envs.nel; + for(i=0; i<envc; i++){ + if(envv[i].len <= len) + continue; + v = envv[i].str; + for(j=0; j<len; j++) + if(bs[j] != v[j]) + goto nomatch; + if(v[len] != '=') + goto nomatch; + return v+len+1; + nomatch:; + } + return nil; +} + + +int32 +atoi(byte *p) +{ + int32 n; + + n = 0; + while('0' <= *p && *p <= '9') + n = n*10 + *p++ - '0'; + return n; +} + +void +check(void) +{ + int8 a; + uint8 b; + int16 c; + uint16 d; + int32 e; + uint32 f; + int64 g; + uint64 h; + float32 i; + float64 j; + void* k; + uint16* l; + + if(sizeof(a) != 1) throw("bad a"); + if(sizeof(b) != 1) throw("bad b"); + if(sizeof(c) != 2) throw("bad c"); + if(sizeof(d) != 2) throw("bad d"); + if(sizeof(e) != 4) throw("bad e"); + if(sizeof(f) != 4) throw("bad f"); + if(sizeof(g) != 8) throw("bad g"); + if(sizeof(h) != 8) throw("bad h"); + if(sizeof(i) != 4) throw("bad i"); + if(sizeof(j) != 8) throw("bad j"); + if(sizeof(k) != sizeof(uintptr)) throw("bad k"); + if(sizeof(l) != sizeof(uintptr)) throw("bad l"); +// prints(1"check ok\n"); + + uint32 z; + z = 1; + if(!cas(&z, 1, 2)) + throw("cas1"); + if(z != 2) + throw("cas2"); + + z = 4; + if(cas(&z, 5, 6)) + throw("cas3"); + if(z != 4) + throw("cas4"); + + initsig(); +} + +/* + * map and chan helpers for + * dealing with unknown types + */ +static uintptr +memhash(uint32 s, void *a) +{ + byte *b; + uintptr hash; + + b = a; + if(sizeof(hash) == 4) + hash = 2860486313U; + else + hash = 33054211828000289ULL; + while(s > 0) { + if(sizeof(hash) == 4) + hash = (hash ^ *b) * 3267000013UL; + else + hash = (hash ^ *b) * 23344194077549503ULL; + b++; + s--; + } + return hash; +} + +static uint32 +memequal(uint32 s, void *a, void *b) +{ + byte *ba, *bb; + uint32 i; + + ba = a; + bb = b; + for(i=0; i<s; i++) + if(ba[i] != bb[i]) + return 0; + return 1; +} + +static void +memprint(uint32 s, void *a) +{ + uint64 v; + + v = 0xbadb00b; + switch(s) { + case 1: + v = *(uint8*)a; + break; + case 2: + v = *(uint16*)a; + break; + case 4: + v = *(uint32*)a; + break; + case 8: + v = *(uint64*)a; + break; + } + sys·printint(v); +} + +static void +memcopy(uint32 s, void *a, void *b) +{ + byte *ba, *bb; + uint32 i; + + ba = a; + bb = b; + if(bb == nil) { + for(i=0; i<s; i++) + ba[i] = 0; + return; + } + for(i=0; i<s; i++) + ba[i] = bb[i]; +} + +static uintptr +strhash(uint32 s, String *a) +{ + USED(s); + return memhash((*a).len, (*a).str); +} + +static uint32 +strequal(uint32 s, String *a, String *b) +{ + USED(s); + return cmpstring(*a, *b) == 0; +} + +static void +strprint(uint32 s, String *a) +{ + USED(s); + sys·printstring(*a); +} + +static uintptr +interhash(uint32 s, Iface *a) +{ + USED(s); + return ifacehash(*a); +} + +static void +interprint(uint32 s, Iface *a) +{ + USED(s); + sys·printiface(*a); +} + +static uint32 +interequal(uint32 s, Iface *a, Iface *b) +{ + USED(s); + return ifaceeq(*a, *b); +} + +static uintptr +nilinterhash(uint32 s, Eface *a) +{ + USED(s); + return efacehash(*a); +} + +static void +nilinterprint(uint32 s, Eface *a) +{ + USED(s); + sys·printeface(*a); +} + +static uint32 +nilinterequal(uint32 s, Eface *a, Eface *b) +{ + USED(s); + return efaceeq(*a, *b); +} + +uintptr +nohash(uint32 s, void *a) +{ + USED(s); + USED(a); + throw("hash of unhashable type"); + return 0; +} + +uint32 +noequal(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); + throw("comparing uncomparable types"); + return 0; +} + +static void +noprint(uint32 s, void *a) +{ + USED(s); + USED(a); + throw("print of unprintable type"); +} + +static void +nocopy(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); + throw("copy of uncopyable type"); +} + +Alg +algarray[] = +{ +[AMEM] { memhash, memequal, memprint, memcopy }, +[ANOEQ] { nohash, noequal, memprint, memcopy }, +[ASTRING] { strhash, strequal, strprint, memcopy }, +[AINTER] { interhash, interequal, interprint, memcopy }, +[ANILINTER] { nilinterhash, nilinterequal, nilinterprint, memcopy }, +[AFAKE] { nohash, noequal, noprint, nocopy }, +}; + +#pragma textflag 7 +void +FLUSH(void *v) +{ + USED(v); +} + diff --git a/src/lib/runtime/runtime.h b/src/lib/runtime/runtime.h new file mode 100644 index 000000000..749364f95 --- /dev/null +++ b/src/lib/runtime/runtime.h @@ -0,0 +1,464 @@ +// 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. + +/* + * basic types + */ +typedef signed char int8; +typedef unsigned char uint8; +typedef signed short int16; +typedef unsigned short uint16; +typedef signed int int32; +typedef unsigned int uint32; +typedef signed long long int int64; +typedef unsigned long long int uint64; +typedef float float32; +typedef double float64; + +#ifdef _64BIT +typedef uint64 uintptr; +#else +typedef uint32 uintptr; +#endif + +/* + * get rid of C types + * the / / / forces a syntax error immediately, + * which will show "last name: XXunsigned". + */ +#define unsigned XXunsigned / / / +#define signed XXsigned / / / +#define char XXchar / / / +#define short XXshort / / / +#define int XXint / / / +#define long XXlong / / / +#define float XXfloat / / / +#define double XXdouble / / / + +/* + * defined types + */ +typedef uint8 bool; +typedef uint8 byte; +typedef struct Alg Alg; +typedef struct Array Array; +typedef struct Func Func; +typedef struct G G; +typedef struct Gobuf Gobuf; +typedef struct Lock Lock; +typedef struct M M; +typedef struct Mem Mem; +typedef union Note Note; +typedef struct Stktop Stktop; +typedef struct String String; +typedef struct Usema Usema; +typedef struct SigTab SigTab; +typedef struct MCache MCache; +typedef struct Iface Iface; +typedef struct Itype Itype; +typedef struct Eface Eface; +typedef struct Sigt Sigt; +typedef struct Defer Defer; + +/* + * per cpu declaration + */ +extern register G* g; // R15 +extern register M* m; // R14 + +/* + * defined constants + */ +enum +{ + // G status + Gidle, + Grunnable, + Grunning, + Gsyscall, + Gwaiting, + Gmoribund, + Gdead, +}; +enum +{ + true = 1, + false = 0, +}; + +/* + * structures + */ +struct Lock +{ + uint32 key; + uint32 sema; // for OS X +}; +struct Usema +{ + uint32 u; + uint32 k; +}; +union Note +{ + struct { // Linux + Lock lock; + }; + struct { // OS X + int32 wakeup; + Usema sema; + }; +}; +struct String +{ + byte* str; + int32 len; +}; +struct Iface +{ + Itype* type; + void* data; +}; +struct Eface +{ + Sigt* type; + void* data; +}; + +struct Array +{ // must not move anything + byte* array; // actual data + uint32 nel; // number of elements + uint32 cap; // allocated number of elements +}; +struct Gobuf +{ + byte* SP; + byte* PC; +}; +struct G +{ + byte* stackguard; // must not move + byte* stackbase; // must not move + Defer* defer; // must not move + byte* stack0; // first stack segment + Gobuf sched; + G* alllink; // on allg + void* param; // passed parameter on wakeup + int16 status; + int32 goid; + int32 selgen; // valid sudog pointer + G* schedlink; + bool readyonstop; + M* m; // for debuggers +}; +struct Mem +{ + uint8* hunk; + uint32 nhunk; + uint64 nmmap; + uint64 nmal; +}; +struct M +{ + G* g0; // g0 w interrupt stack - must not move + uint64 morearg; // arg to morestack - must not move + uint64 cret; // return value from C - must not move + uint64 procid; // for debuggers - must not move + G* gsignal; // signal-handling G - must not move + G* curg; // current running goroutine - must not move + G* lastg; // last running goroutine - to emulate fifo - must not move + uint32 tls[8]; // thread-local storage (for 386 extern register) - must not move + Gobuf sched; + Gobuf morestack; + byte* moresp; + int32 siz1; + int32 siz2; + int32 id; + int32 mallocing; + int32 locks; + Note havenextg; + G* nextg; + M* schedlink; + Mem mem; + uint32 machport; // Return address for Mach IPC (OS X) + MCache *mcache; +}; +struct Stktop +{ + uint8* oldbase; + uint8* oldsp; + uint64 magic; + uint8* oldguard; +}; +struct Alg +{ + uintptr (*hash)(uint32, void*); + uint32 (*equal)(uint32, void*, void*); + void (*print)(uint32, void*); + void (*copy)(uint32, void*, void*); +}; +struct SigTab +{ + int32 flags; + int8 *name; +}; +enum +{ + SigCatch = 1<<0, + SigIgnore = 1<<1, + SigRestart = 1<<2, +}; + +// (will be) shared with go; edit ../cmd/6g/sys.go too. +// should move out of sys.go eventually. +// also eventually, the loaded symbol table should +// be closer to this form. +struct Func +{ + String name; + String type; // go type string + String src; // src file name + uint64 entry; // entry pc + int64 frame; // stack frame size + Array pcln; // pc/ln tab for this func + int64 pc0; // starting pc, ln for table + int32 ln0; + int32 args; // number of 32-bit in/out args + int32 locals; // number of 32-bit locals +}; + +/* + * defined macros + * you need super-goru privilege + * to add this list. + */ +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define nil ((void*)0) + +/* + * known to compiler + */ +enum +{ + AMEM, + ANOEQ, + ASTRING, + AINTER, + ANILINTER, + AFAKE, + Amax +}; + +/* + * deferred subroutine calls + */ +struct Defer +{ + int32 siz; + byte* sp; + byte* fn; + Defer* link; + byte args[8]; // padded to actual size +}; + +/* + * external data + */ +extern Alg algarray[Amax]; +extern String emptystring; +G* allg; +int32 goidgen; +extern int32 gomaxprocs; +extern int32 panicking; +extern int32 maxround; + +/* + * common functions and data + */ +int32 strcmp(byte*, byte*); +int32 findnull(byte*); +void dump(byte*, int32); +int32 runetochar(byte*, int32); +int32 charntorune(int32*, uint8*, int32); + +/* + * very low level c-called + */ +int32 gogo(Gobuf*); +int32 gosave(Gobuf*); +int32 gogoret(Gobuf*, uint64); +void retfromnewstack(void); +void goargs(void); +void setspgoto(byte*, void(*)(void), void(*)(void)); +void FLUSH(void*); +void* getu(void); +void throw(int8*); +uint32 rnd(uint32, uint32); +void prints(int8*); +void printf(int8*, ...); +byte* mchr(byte*, byte, byte*); +void mcpy(byte*, byte*, uint32); +int32 mcmp(byte*, byte*, uint32); +void mmov(byte*, byte*, uint32); +void* mal(uint32); +uint32 cmpstring(String, String); +String gostring(byte*); +void initsig(void); +int32 gotraceback(void); +void traceback(uint8 *pc, uint8 *sp, G* gp); +void tracebackothers(G*); +int32 open(byte*, int32, ...); +int32 read(int32, void*, int32); +int32 write(int32, void*, int32); +void close(int32); +int32 fstat(int32, void*); +bool cas(uint32*, uint32, uint32); +void jmpdefer(byte*, void*); +void exit1(int32); +void ready(G*); +byte* getenv(int8*); +int32 atoi(byte*); +void newosproc(M *m, G *g, void *stk, void (*fn)(void)); +void sigaltstack(void*, void*); +void signalstack(byte*, int32); +G* malg(int32); +void minit(void); +Func* findfunc(uintptr); +int32 funcline(Func*, uint64); +void* stackalloc(uint32); +void stackfree(void*); +MCache* allocmcache(void); +void mallocinit(void); +bool ifaceeq(Iface, Iface); +bool efaceeq(Eface, Eface); +uintptr ifacehash(Iface); +uintptr efacehash(Eface); +uintptr nohash(uint32, void*); +uint32 noequal(uint32, void*, void*); +void* malloc(uintptr size); +void* mallocgc(uintptr size); +void free(void *v); +void exit(int32); +void breakpoint(void); +void gosched(void); +void goexit(void); + +#pragma varargck argpos printf 1 + +#pragma varargck type "d" int32 +#pragma varargck type "d" uint32 +#pragma varargck type "D" int64 +#pragma varargck type "D" uint64 +#pragma varargck type "x" int32 +#pragma varargck type "x" uint32 +#pragma varargck type "X" int64 +#pragma varargck type "X" uint64 +#pragma varargck type "p" void* +#pragma varargck type "p" uintptr +#pragma varargck type "s" int8* +#pragma varargck type "s" uint8* +#pragma varargck type "S" String + +// TODO(rsc): Remove. These are only temporary, +// for the mark and sweep collector. +void stoptheworld(void); +void starttheworld(void); + +/* + * mutual exclusion locks. in the uncontended case, + * as fast as spin locks (just a few user-level instructions), + * but on the contention path they sleep in the kernel. + * a zeroed Lock is unlocked (no need to initialize each lock). + */ +void lock(Lock*); +void unlock(Lock*); + +/* + * sleep and wakeup on one-time events. + * before any calls to notesleep or notewakeup, + * must call noteclear to initialize the Note. + * then, any number of threads can call notesleep + * and exactly one thread can call notewakeup (once). + * once notewakeup has been called, all the notesleeps + * will return. future notesleeps will return immediately. + */ +void noteclear(Note*); +void notesleep(Note*); +void notewakeup(Note*); + +/* + * Redefine methods for the benefit of gcc, which does not support + * UTF-8 characters in identifiers. + */ +#ifndef __GNUC__ +#define sys_memclr sys·memclr +#define sys_write sys·write +#define sys_catstring sys·catstring +#define sys_cmpstring sys·cmpstring +#define sys_getcallerpc sys·getcallerpc +#define sys_indexstring sys·indexstring +#define sys_intstring sys·intstring +#define sys_mal sys·mal +#define sys_mmap sys·mmap +#define sys_printarray sys·printarray +#define sys_printbool sys·printbool +#define sys_printfloat sys·printfloat +#define sys_printhex sys·printhex +#define sys_printint sys·printint +#define sys_printiface sys·printiface +#define sys_printeface sys·printeface +#define sys_printpc sys·printpc +#define sys_printpointer sys·printpointer +#define sys_printstring sys·printstring +#define sys_printuint sys·printuint +#define sys_setcallerpc sys·setcallerpc +#define sys_slicestring sys·slicestring +#endif + +/* + * low level go-called + */ +void sys_write(int32, void*, int32); +uint8* sys_mmap(byte*, uint32, int32, int32, int32, uint32); +void sys_memclr(byte*, uint32); +void sys_setcallerpc(void*, void*); +void* sys_getcallerpc(void*); + +/* + * runtime go-called + */ +void sys_printbool(bool); +void sys_printfloat(float64); +void sys_printint(int64); +void sys_printiface(Iface); +void sys_printeface(Eface); +void sys_printstring(String); +void sys_printpc(void*); +void sys_printpointer(void*); +void sys_printuint(uint64); +void sys_printhex(uint64); +void sys_printarray(Array); +void sys_catstring(String, String, String); +void sys_cmpstring(String, String, int32); +void sys_slicestring(String, int32, int32, String); +void sys_indexstring(String, int32, byte); +void sys_intstring(int64, String); + +/* + * wrapped for go users + */ +float64 Inf(int32 sign); +float64 NaN(void); +float32 float32frombits(uint32 i); +uint32 float32tobits(float32 f); +float64 float64frombits(uint64 i); +uint64 float64tobits(float64 f); +float64 frexp(float64 d, int32 *ep); +bool isInf(float64 f, int32 sign); +bool isNaN(float64 f); +float64 ldexp(float64 d, int32 e); +float64 modf(float64 d, float64 *ip); +void semacquire(uint32*); +void semrelease(uint32*); diff --git a/src/lib/runtime/sema.c b/src/lib/runtime/sema.c new file mode 100644 index 000000000..5e5b07aa6 --- /dev/null +++ b/src/lib/runtime/sema.c @@ -0,0 +1,176 @@ +// 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. + +// Semaphore implementation exposed to Go. +// Intended use is provide a sleep and wakeup +// primitive that can be used in the contended case +// of other synchronization primitives. +// Thus it targets the same goal as Linux's futex, +// but it has much simpler semantics. +// +// That is, don't think of these as semaphores. +// Think of them as a way to implement sleep and wakeup +// such that every sleep is paired with a single wakeup, +// even if, due to races, the wakeup happens before the sleep. +// +// See Mullender and Cox, ``Semaphores in Plan 9,'' +// http://swtch.com/semaphore.pdf + +#include "runtime.h" + +typedef struct Sema Sema; +struct Sema +{ + uint32 *addr; + G *g; + Sema *prev; + Sema *next; +}; + +// TODO: For now, a linked list; maybe a hash table of linked lists later. +static Sema *semfirst, *semlast; +static Lock semlock; + +static void +semqueue(uint32 *addr, Sema *s) +{ + s->addr = addr; + s->g = nil; + + lock(&semlock); + s->prev = semlast; + s->next = nil; + if(semlast) + semlast->next = s; + else + semfirst = s; + semlast = s; + unlock(&semlock); +} + +static void +semdequeue(Sema *s) +{ + lock(&semlock); + if(s->next) + s->next->prev = s->prev; + else + semlast = s->prev; + if(s->prev) + s->prev->next = s->next; + else + semfirst = s->next; + s->prev = nil; + s->next = nil; + unlock(&semlock); +} + +static void +semwakeup(uint32 *addr) +{ + Sema *s; + + lock(&semlock); + for(s=semfirst; s; s=s->next) { + if(s->addr == addr && s->g) { + ready(s->g); + s->g = nil; + break; + } + } + unlock(&semlock); +} + +// Step 1 of sleep: make ourselves available for wakeup. +// TODO(rsc): Maybe we can write a version without +// locks by using cas on s->g. Maybe not: I need to +// think more about whether it would be correct. +static void +semsleep1(Sema *s) +{ + lock(&semlock); + s->g = g; + unlock(&semlock); +} + +// Decided not to go through with it: undo step 1. +static void +semsleepundo1(Sema *s) +{ + lock(&semlock); + if(s->g != nil) { + s->g = nil; // back ourselves out + } else { + // If s->g == nil already, semwakeup + // already readied us. Since we never stopped + // running, readying us just set g->readyonstop. + // Clear it. + if(g->readyonstop == 0) + *(int32*)0x555 = 555; + g->readyonstop = 0; + } + unlock(&semlock); +} + +// Step 2: wait for the wakeup. +static void +semsleep2(Sema *s) +{ + USED(s); + g->status = Gwaiting; + gosched(); +} + +static int32 +cansemacquire(uint32 *addr) +{ + uint32 v; + + while((v = *addr) > 0) + if(cas(addr, v, v-1)) + return 1; + return 0; +} + +// For now has no return value. +// Might return an ok (not interrupted) bool in the future? +void +semacquire(uint32 *addr) +{ + Sema s; + + // Easy case. + if(cansemacquire(addr)) + return; + + // Harder case: + // queue + // try semacquire one more time, sleep if failed + // dequeue + // wake up one more guy to avoid races (TODO(rsc): maybe unnecessary?) + semqueue(addr, &s); + for(;;) { + semsleep1(&s); + if(cansemacquire(addr)) { + semsleepundo1(&s); + break; + } + semsleep2(&s); + } + semdequeue(&s); + semwakeup(addr); +} + +void +semrelease(uint32 *addr) +{ + uint32 v; + + for(;;) { + v = *addr; + if(cas(addr, v, v+1)) + break; + } + semwakeup(addr); +} diff --git a/src/lib/runtime/sema_go.cgo b/src/lib/runtime/sema_go.cgo new file mode 100644 index 000000000..eb4082a0d --- /dev/null +++ b/src/lib/runtime/sema_go.cgo @@ -0,0 +1,15 @@ +// 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. + +package sync +#include "runtime.h" + +func semacquire(addr *uint32) { + semacquire(addr); +} + +func semrelease(addr *uint32) { + semrelease(addr); +} + diff --git a/src/lib/runtime/string.c b/src/lib/runtime/string.c new file mode 100644 index 000000000..5bfe8196f --- /dev/null +++ b/src/lib/runtime/string.c @@ -0,0 +1,263 @@ +// 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" + +String emptystring; + +int32 +findnull(byte *s) +{ + int32 l; + + if(s == nil) + return 0; + for(l=0; s[l]!=0; l++) + ; + return l; +} + +int32 maxstring; + +String +gostringsize(int32 l) +{ + String s; + + if(l == 0) + return emptystring; + s.str = mal(l+1); // leave room for NUL for C runtime (e.g., callers of getenv) + s.len = l; + if(l > maxstring) + maxstring = l; + return s; +} + +String +gostring(byte *str) +{ + int32 l; + String s; + + l = findnull(str); + s = gostringsize(l); + mcpy(s.str, str, l); + return s; +} + +void +sys·catstring(String s1, String s2, String s3) +{ + if(s1.len == 0) { + s3 = s2; + goto out; + } + if(s2.len == 0) { + s3 = s1; + goto out; + } + + s3 = gostringsize(s1.len + s2.len); + mcpy(s3.str, s1.str, s1.len); + mcpy(s3.str+s1.len, s2.str, s2.len); + +out: + FLUSH(&s3); +} + +static void +prbounds(int8* s, int32 a, int32 b, int32 c) +{ + prints(s); + prints(" "); + sys·printint(a); + prints("<"); + sys·printint(b); + prints(">"); + sys·printint(c); + prints("\n"); + throw("string bounds"); +} + +uint32 +cmpstring(String s1, String s2) +{ + uint32 i, l; + byte c1, c2; + + l = s1.len; + if(s2.len < l) + l = s2.len; + for(i=0; i<l; i++) { + c1 = s1.str[i]; + c2 = s2.str[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + } + if(s1.len < s2.len) + return -1; + if(s1.len > s2.len) + return +1; + return 0; +} + +void +sys·cmpstring(String s1, String s2, int32 v) +{ + v = cmpstring(s1, s2); + FLUSH(&v); +} + +int32 +strcmp(byte *s1, byte *s2) +{ + uint32 i; + byte c1, c2; + + for(i=0;; i++) { + c1 = s1[i]; + c2 = s2[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + if(c1 == 0) + return 0; + } +} + +void +sys·slicestring(String si, int32 lindex, int32 hindex, String so) +{ + int32 l; + + if(lindex < 0 || lindex > si.len || + hindex < lindex || hindex > si.len) { + sys·printpc(&si); + prints(" "); + prbounds("slice", lindex, si.len, hindex); + } + + l = hindex-lindex; + so.str = si.str + lindex; + so.len = l; + +// alternate to create a new string +// so = gostringsize(l); +// mcpy(so.str, si.str+lindex, l); + + FLUSH(&so); +} + +void +sys·indexstring(String s, int32 i, byte b) +{ + if(i < 0 || i >= s.len) { + sys·printpc(&s); + prints(" "); + prbounds("index", 0, i, s.len); + } + + b = s.str[i]; + FLUSH(&b); +} + +void +sys·intstring(int64 v, String s) +{ + s = gostringsize(8); + s.len = runetochar(s.str, v); + FLUSH(&s); +} + +void +sys·arraystring(Array b, String s) +{ + s = gostringsize(b.nel); + mcpy(s.str, b.array, s.len); + FLUSH(&s); +} + +void +sys·arraystringi(Array b, String s) +{ + int32 siz1, siz2, i; + int32 *a; + byte dum[8]; + + a = (int32*)b.array; + siz1 = 0; + for(i=0; i<b.nel; i++) { + siz1 += runetochar(dum, a[i]); + } + + s = gostringsize(siz1+4); + siz2 = 0; + for(i=0; i<b.nel; i++) { + // check for race + if(siz2 >= siz1) + break; + siz2 += runetochar(s.str+siz2, a[i]); + } + s.len = siz2; + + FLUSH(&s); +} + +enum +{ + Runeself = 0x80, +}; + +// func stringiter(string, int) (retk int); +void +sys·stringiter(String s, int32 k, int32 retk) +{ + int32 l; + + if(k >= s.len) { + // retk=0 is end of iteration + retk = 0; + goto out; + } + + l = s.str[k]; + if(l < Runeself) { + retk = k+1; + goto out; + } + + // multi-char rune + retk = k + charntorune(&l, s.str+k, s.len-k); + +out: + FLUSH(&retk); +} + +// func stringiter2(string, int) (retk int, retv any); +void +sys·stringiter2(String s, int32 k, int32 retk, int32 retv) +{ + if(k >= s.len) { + // retk=0 is end of iteration + retk = 0; + retv = 0; + goto out; + } + + retv = s.str[k]; + if(retv < Runeself) { + retk = k+1; + goto out; + } + + // multi-char rune + retk = k + charntorune(&retv, s.str+k, s.len-k); + +out: + FLUSH(&retk); + FLUSH(&retv); +} diff --git a/src/lib/runtime/symtab.c b/src/lib/runtime/symtab.c new file mode 100644 index 000000000..b4802715e --- /dev/null +++ b/src/lib/runtime/symtab.c @@ -0,0 +1,377 @@ +// 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. + +// Runtime symbol table access. Work in progress. +// The Plan 9 symbol table is not in a particularly convenient form. +// The routines here massage it into a more usable form; eventually +// we'll change 6l to do this for us, but it is easier to experiment +// here than to change 6l and all the other tools. +// +// The symbol table also needs to be better integrated with the type +// strings table in the future. This is just a quick way to get started +// and figure out exactly what we want. + +#include "runtime.h" + +// TODO(rsc): Move this *under* the text segment. +// Then define names for these addresses instead of hard-coding magic ones. +#ifdef _64BIT +#define SYMCOUNTS ((int32*)(0x99LL<<32)) // known to 6l +#define SYMDATA ((byte*)(0x99LL<<32) + 8) +#else +#define SYMCOUNTS ((int32*)(0x99LL<<24)) // known to 8l +#define SYMDATA ((byte*)(0x99LL<<24) + 8) +#endif + + +// Return a pointer to a byte array containing the symbol table segment. +void +sys·symdat(Array *symtab, Array *pclntab) +{ + Array *a; + int32 *v; + + v = SYMCOUNTS; + + a = mal(sizeof *a); + a->nel = v[0]; + a->cap = a->nel; + a->array = SYMDATA; + symtab = a; + FLUSH(&symtab); + + a = mal(sizeof *a); + a->nel = v[1]; + a->cap = a->nel; + a->array = SYMDATA + v[0]; + pclntab = a; + FLUSH(&pclntab); +} + +typedef struct Sym Sym; +struct Sym +{ + uintptr value; + byte symtype; + byte *name; + byte *gotype; +}; + +// Walk over symtab, calling fn(&s) for each symbol. +static void +walksymtab(void (*fn)(Sym*)) +{ + int32 *v; + byte *p, *ep, *q; + Sym s; + + v = SYMCOUNTS; + p = SYMDATA; + ep = p + v[0]; + while(p < ep) { + if(p + 7 > ep) + break; + s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]); + if(!(p[4]&0x80)) + break; + s.symtype = p[4] & ~0x80; + p += 5; + s.name = p; + if(s.symtype == 'z' || s.symtype == 'Z') { + // path reference string - skip first byte, + // then 2-byte pairs ending at two zeros. + q = p+1; + for(;;) { + if(q+2 > ep) + return; + if(q[0] == '\0' && q[1] == '\0') + break; + q += 2; + } + p = q+2; + }else{ + q = mchr(p, '\0', ep); + if(q == nil) + break; + p = q+1; + } + q = mchr(p, '\0', ep); + if(q == nil) + break; + s.gotype = p; + p = q+1; + fn(&s); + } +} + +// Symtab walker; accumulates info about functions. + +static Func *func; +static int32 nfunc; + +static byte **fname; +static int32 nfname; + +static void +dofunc(Sym *sym) +{ + Func *f; + + switch(sym->symtype) { + case 't': + case 'T': + if(strcmp(sym->name, (byte*)"etext") == 0) + break; + if(func == nil) { + nfunc++; + break; + } + f = &func[nfunc++]; + f->name = gostring(sym->name); + f->entry = sym->value; + break; + case 'm': + if(nfunc > 0 && func != nil) + func[nfunc-1].frame = sym->value; + break; + case 'p': + if(nfunc > 0 && func != nil) { + f = &func[nfunc-1]; + // args counts 32-bit words. + // sym->value is the arg's offset. + // don't know width of this arg, so assume it is 64 bits. + if(f->args < sym->value/4 + 2) + f->args = sym->value/4 + 2; + } + break; + case 'f': + if(fname == nil) { + if(sym->value >= nfname) + nfname = sym->value+1; + break; + } + fname[sym->value] = sym->name; + break; + } +} + +// put together the path name for a z entry. +// the f entries have been accumulated into fname already. +static void +makepath(byte *buf, int32 nbuf, byte *path) +{ + int32 n, len; + byte *p, *ep, *q; + + if(nbuf <= 0) + return; + + p = buf; + ep = buf + nbuf; + *p = '\0'; + for(;;) { + if(path[0] == 0 && path[1] == 0) + break; + n = (path[0]<<8) | path[1]; + path += 2; + if(n >= nfname) + break; + q = fname[n]; + len = findnull(q); + if(p+1+len >= ep) + break; + if(p > buf && p[-1] != '/') + *p++ = '/'; + mcpy(p, q, len+1); + p += len; + } +} + +// walk symtab accumulating path names for use by pc/ln table. +// don't need the full generality of the z entry history stack because +// there are no includes in go (and only sensible includes in our c). +static void +dosrcline(Sym *sym) +{ + static byte srcbuf[1000]; + static String srcstring; + static int32 lno, incstart; + static int32 nf, nhist; + Func *f; + + switch(sym->symtype) { + case 't': + case 'T': + if(strcmp(sym->name, (byte*)"etext") == 0) + break; + f = &func[nf++]; + f->src = srcstring; + f->ln0 += lno; + break; + case 'z': + if(sym->value == 1) { + // entry for main source file for a new object. + makepath(srcbuf, sizeof srcbuf, sym->name+1); + srcstring = gostring(srcbuf); + lno = 0; + nhist = 0; + } else { + // push or pop of included file. + makepath(srcbuf, sizeof srcbuf, sym->name+1); + if(srcbuf[0] != '\0') { + if(nhist++ == 0) + incstart = sym->value; + }else{ + if(--nhist == 0) + lno -= sym->value - incstart; + } + } + } +} + +enum { PcQuant = 1 }; + +// Interpret pc/ln table, saving the subpiece for each func. +static void +splitpcln(void) +{ + int32 line; + uintptr pc; + byte *p, *ep; + Func *f, *ef; + int32 *v; + + // pc/ln table bounds + v = SYMCOUNTS; + p = SYMDATA; + p += v[0]; + ep = p+v[1]; + + f = func; + ef = func + nfunc; + pc = func[0].entry; // text base + f->pcln.array = p; + f->pc0 = pc - PcQuant; + line = 0; + for(; p < ep; p++) { + if(f < ef && pc >= (f+1)->entry) { + f->pcln.nel = p - f->pcln.array; + f->pcln.cap = f->pcln.nel; + f++; + f->pcln.array = p; + f->pc0 = pc; + f->ln0 = line; + } + if(*p == 0) { + // 4 byte add to line + line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; + p += 4; + } else if(*p <= 64) { + line += *p; + } else if(*p <= 128) { + line -= *p - 64; + } else { + pc += PcQuant*(*p - 129); + } + pc += PcQuant; + } + if(f < ef) { + f->pcln.nel = p - f->pcln.array; + f->pcln.cap = f->pcln.nel; + } +} + + +// Return actual file line number for targetpc in func f. +// (Source file is f->src.) +int32 +funcline(Func *f, uint64 targetpc) +{ + byte *p, *ep; + uintptr pc; + int32 line; + + p = f->pcln.array; + ep = p + f->pcln.nel; + pc = f->pc0; + line = f->ln0; + for(; p < ep; p++) { + if(pc >= targetpc) + return line; + if(*p == 0) { + line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; + p += 4; + } else if(*p <= 64) { + line += *p; + } else if(*p <= 128) { + line -= *p - 64; + } else { + pc += PcQuant*(*p - 129); + } + pc += PcQuant; + } + return line; +} + +static void +buildfuncs(void) +{ + extern byte etext[]; + + if(func != nil) + return; + // count funcs, fnames + nfunc = 0; + nfname = 0; + walksymtab(dofunc); + + // initialize tables + func = mal((nfunc+1)*sizeof func[0]); + func[nfunc].entry = (uint64)etext; + fname = mal(nfname*sizeof fname[0]); + nfunc = 0; + walksymtab(dofunc); + + // split pc/ln table by func + splitpcln(); + + // record src file and line info for each func + walksymtab(dosrcline); +} + +Func* +findfunc(uintptr addr) +{ + Func *f; + int32 nf, n; + + if(func == nil) + buildfuncs(); + if(nfunc == 0) + return nil; + if(addr < func[0].entry || addr >= func[nfunc].entry) + return nil; + + // binary search to find func with entry <= addr. + f = func; + nf = nfunc; + while(nf > 0) { + n = nf/2; + if(f[n].entry <= addr && addr < f[n+1].entry) + return &f[n]; + else if(addr < f[n].entry) + nf = n; + else { + f += n+1; + nf -= n+1; + } + } + + // can't get here -- we already checked above + // that the address was in the table bounds. + // this can only happen if the table isn't sorted + // by address or if the binary search above is buggy. + prints("findfunc unreachable\n"); + return nil; +} |