summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2009-06-06 22:04:39 -0700
committerRuss Cox <rsc@golang.org>2009-06-06 22:04:39 -0700
commit8257be5f9d4a4bdbb0ff096756a0e48401d620c8 (patch)
tree1195452c472b99bfd365ee1cc057e870178e63e0 /src/lib
parentaf9643dcc1336bcb6e6ed0817e68a9c2db2c5613 (diff)
downloadgolang-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')
-rw-r--r--src/lib/runtime/386/asm.s217
-rw-r--r--src/lib/runtime/386/closure.c104
-rw-r--r--src/lib/runtime/386/traceback.c148
-rwxr-xr-xsrc/lib/runtime/386/vlop.s48
-rwxr-xr-xsrc/lib/runtime/386/vlrt.c815
-rw-r--r--src/lib/runtime/amd64/asm.s207
-rw-r--r--src/lib/runtime/amd64/closure.c121
-rw-r--r--src/lib/runtime/amd64/traceback.c146
-rw-r--r--src/lib/runtime/arm/asm.s83
-rw-r--r--src/lib/runtime/arm/closure.c4
-rw-r--r--src/lib/runtime/arm/traceback.s0
-rw-r--r--src/lib/runtime/array.c175
-rw-r--r--src/lib/runtime/cgo2c.c602
-rw-r--r--src/lib/runtime/chan.c1024
-rw-r--r--src/lib/runtime/darwin/386/defs.h229
-rwxr-xr-xsrc/lib/runtime/darwin/386/rt0.s8
-rw-r--r--src/lib/runtime/darwin/386/signal.c103
-rw-r--r--src/lib/runtime/darwin/386/sys.s278
-rw-r--r--src/lib/runtime/darwin/amd64/defs.h244
-rw-r--r--src/lib/runtime/darwin/amd64/rt0.s9
-rw-r--r--src/lib/runtime/darwin/amd64/signal.c111
-rw-r--r--src/lib/runtime/darwin/amd64/sys.s263
-rw-r--r--src/lib/runtime/darwin/defs.c104
-rw-r--r--src/lib/runtime/darwin/os.h24
-rw-r--r--src/lib/runtime/darwin/signals.h48
-rw-r--r--src/lib/runtime/darwin/thread.c427
-rw-r--r--src/lib/runtime/extern.go (renamed from src/lib/runtime/runtime.go)0
-rw-r--r--src/lib/runtime/float.c173
-rw-r--r--src/lib/runtime/float_go.cgo52
-rw-r--r--src/lib/runtime/hashmap.c954
-rw-r--r--src/lib/runtime/hashmap.h161
-rw-r--r--src/lib/runtime/iface.c906
-rwxr-xr-xsrc/lib/runtime/linux/386/defs.h136
-rwxr-xr-xsrc/lib/runtime/linux/386/rt0.s8
-rw-r--r--src/lib/runtime/linux/386/signal.c102
-rwxr-xr-xsrc/lib/runtime/linux/386/sys.s222
-rw-r--r--src/lib/runtime/linux/amd64/defs.h175
-rw-r--r--src/lib/runtime/linux/amd64/rt0.s9
-rw-r--r--src/lib/runtime/linux/amd64/signal.c112
-rw-r--r--src/lib/runtime/linux/amd64/sys.s193
-rw-r--r--src/lib/runtime/linux/arm/defs.h27
-rw-r--r--src/lib/runtime/linux/arm/rt0.s6
-rw-r--r--src/lib/runtime/linux/arm/signal.c4
-rw-r--r--src/lib/runtime/linux/arm/sys.s15
-rw-r--r--src/lib/runtime/linux/defs.c40
-rw-r--r--src/lib/runtime/linux/defs1.c25
-rw-r--r--src/lib/runtime/linux/defs2.c51
-rw-r--r--src/lib/runtime/linux/defs_arm.c54
-rw-r--r--src/lib/runtime/linux/os.h10
-rw-r--r--src/lib/runtime/linux/signals.h48
-rw-r--r--src/lib/runtime/linux/thread.c282
-rw-r--r--src/lib/runtime/malloc.c308
-rw-r--r--src/lib/runtime/malloc.h308
-rw-r--r--src/lib/runtime/malloc_go.cgo28
-rw-r--r--src/lib/runtime/mcache.c105
-rw-r--r--src/lib/runtime/mcentral.c192
-rw-r--r--src/lib/runtime/mem.c75
-rw-r--r--src/lib/runtime/mfixalloc.c56
-rw-r--r--src/lib/runtime/mgc0.c231
-rw-r--r--src/lib/runtime/mheap.c333
-rw-r--r--src/lib/runtime/mheapmap32.c96
-rw-r--r--src/lib/runtime/mheapmap32.h76
-rw-r--r--src/lib/runtime/mheapmap64.c117
-rw-r--r--src/lib/runtime/mheapmap64.h96
-rw-r--r--src/lib/runtime/msize.c165
-rw-r--r--src/lib/runtime/print.c268
-rw-r--r--src/lib/runtime/proc.c858
-rw-r--r--src/lib/runtime/rune.c238
-rw-r--r--src/lib/runtime/runtime.c462
-rw-r--r--src/lib/runtime/runtime.h464
-rw-r--r--src/lib/runtime/sema.c176
-rw-r--r--src/lib/runtime/sema_go.cgo15
-rw-r--r--src/lib/runtime/string.c263
-rw-r--r--src/lib/runtime/symtab.c377
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, &params, &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;
+}