Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libgo / runtime / proc.c
index 224dce9..9b563a5 100644 (file)
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 #include <limits.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <unistd.h>
@@ -17,6 +18,8 @@
 #include "arch.h"
 #include "defs.h"
 #include "malloc.h"
+#include "race.h"
+#include "go-type.h"
 #include "go-defer.h"
 
 #ifdef USING_SPLIT_STACK
@@ -81,7 +84,7 @@ fixcontext(ucontext_t *c __attribute__ ((unused)))
 {
 }
 
-# else
+#else
 
 # if defined(__x86_64__) && defined(__sun__)
 
@@ -109,6 +112,28 @@ fixcontext(ucontext_t* c)
        c->uc_mcontext.gregs[REG_FSBASE] = fs;
 }
 
+# elif defined(__NetBSD__)
+
+// NetBSD has a bug: setcontext clobbers tlsbase, we need to save
+// and restore it ourselves.
+
+static __thread __greg_t tlsbase;
+
+static inline void
+initcontext(void)
+{
+       ucontext_t c;
+
+       getcontext(&c);
+       tlsbase = c.uc_mcontext._mc_tlsbase;
+}
+
+static inline void
+fixcontext(ucontext_t* c)
+{
+       c->uc_mcontext._mc_tlsbase = tlsbase;
+}
+
 # else
 
 #  error unknown case for SETCONTEXT_CLOBBERS_TLS
@@ -143,6 +168,13 @@ runtime_m(void)
 
 int32  runtime_gcwaiting;
 
+G*     runtime_allg;
+G*     runtime_lastg;
+M*     runtime_allm;
+
+int8*  runtime_goos;
+int32  runtime_ncpu;
+
 // The static TLS size.  See runtime_newm.
 static int tlssize;
 
@@ -214,7 +246,7 @@ struct Sched {
        Lock;
 
        G *gfree;       // available g's (status == Gdead)
-       int32 goidgen;
+       int64 goidgen;
 
        G *ghead;       // g's waiting to run
        G *gtail;
@@ -330,6 +362,9 @@ kickoff(void)
 {
        void (*fn)(void*);
 
+       if(g->traceback != nil)
+               gtraceback(g);
+
        fn = (void (*)(void*))(g->entry);
        fn(g->param);
        runtime_goexit();
@@ -471,10 +506,13 @@ runtime_schedinit(void)
        // Can not enable GC until all roots are registered.
        // mstats.enablegc = 1;
        m->nomemprof--;
+
+       if(raceenabled)
+               runtime_raceinit();
 }
 
-extern void main_init(void) __asm__ ("__go_init_main");
-extern void main_main(void) __asm__ ("main.main");
+extern void main_init(void) __asm__ (GOSYM_PREFIX "__go_init_main");
+extern void main_main(void) __asm__ (GOSYM_PREFIX "main.main");
 
 // The main goroutine.
 void
@@ -491,6 +529,7 @@ runtime_main(void)
        setmcpumax(runtime_gomaxprocs);
        runtime_sched.init = true;
        scvg = __go_go(runtime_MHeap_Scavenger, nil);
+       scvg->issystem = true;
        main_init();
        runtime_sched.init = false;
        if(!runtime_sched.lockmain)
@@ -507,6 +546,8 @@ runtime_main(void)
        runtime_gosched();
 
        main_main();
+       if(raceenabled)
+               runtime_racefini();
        runtime_exit(0);
        for(;;)
                *(int32*)0 = 0;
@@ -523,13 +564,13 @@ schedlock(void)
 static void
 schedunlock(void)
 {
-       M *m;
+       M *mp;
 
-       m = mwakeup;
+       mp = mwakeup;
        mwakeup = nil;
        runtime_unlock(&runtime_sched);
-       if(m != nil)
-               runtime_notewakeup(&m->havenextg);
+       if(mp != nil)
+               runtime_notewakeup(&mp->havenextg);
 }
 
 void
@@ -540,11 +581,11 @@ runtime_goexit(void)
 }
 
 void
-runtime_goroutineheader(G *g)
+runtime_goroutineheader(G *gp)
 {
        const char *status;
 
-       switch(g->status) {
+       switch(gp->status) {
        case Gidle:
                status = "idle";
                break;
@@ -558,8 +599,8 @@ runtime_goroutineheader(G *g)
                status = "syscall";
                break;
        case Gwaiting:
-               if(g->waitreason)
-                       status = g->waitreason;
+               if(gp->waitreason)
+                       status = gp->waitreason;
                else
                        status = "waiting";
                break;
@@ -570,20 +611,20 @@ runtime_goroutineheader(G *g)
                status = "???";
                break;
        }
-       runtime_printf("goroutine %d [%s]:\n", g->goid, status);
+       runtime_printf("goroutine %D [%s]:\n", gp->goid, status);
 }
 
 void
 runtime_goroutinetrailer(G *g)
 {
        if(g != nil && g->gopc != 0 && g->goid != 1) {
-               struct __go_string fn;
-               struct __go_string file;
-               int line;
+               String fn;
+               String file;
+               intgo line;
 
                if(__go_file_line(g->gopc - 1, &fn, &file, &line)) {
-                       runtime_printf("created by %s\n", fn.__data);
-                       runtime_printf("\t%s:%d\n", file.__data, line);
+                       runtime_printf("created by %S\n", fn);
+                       runtime_printf("\t%S:%D\n", file, (int64) line);
                }
        }
 }
@@ -591,22 +632,26 @@ runtime_goroutinetrailer(G *g)
 struct Traceback
 {
        G* gp;
-       uintptr pcbuf[100];
+       Location locbuf[100];
        int32 c;
 };
 
 void
 runtime_tracebackothers(G * volatile me)
 {
-       G * volatile g;
-       Traceback traceback;
+       G * volatile gp;
+       Traceback tb;
+       int32 traceback;
 
-       traceback.gp = me;
-       for(g = runtime_allg; g != nil; g = g->alllink) {
-               if(g == me || g->status == Gdead)
+       tb.gp = me;
+       traceback = runtime_gotraceback();
+       for(gp = runtime_allg; gp != nil; gp = gp->alllink) {
+               if(gp == me || gp->status == Gdead)
+                       continue;
+               if(gp->issystem && traceback < 2)
                        continue;
                runtime_printf("\n");
-               runtime_goroutineheader(g);
+               runtime_goroutineheader(gp);
 
                // Our only mechanism for doing a stack trace is
                // _Unwind_Backtrace.  And that only works for the
@@ -616,25 +661,25 @@ runtime_tracebackothers(G * volatile me)
 
                // This means that if g is running or in a syscall, we
                // can't reliably print a stack trace.  FIXME.
-               if(g->status == Gsyscall || g->status == Grunning) {
+               if(gp->status == Gsyscall || gp->status == Grunning) {
                        runtime_printf("no stack trace available\n");
-                       runtime_goroutinetrailer(g);
+                       runtime_goroutinetrailer(gp);
                        continue;
                }
 
-               g->traceback = &traceback;
+               gp->traceback = &tb;
 
 #ifdef USING_SPLIT_STACK
                __splitstack_getcontext(&me->stack_context[0]);
 #endif
                getcontext(&me->context);
 
-               if(g->traceback != nil) {
-                       runtime_gogo(g);
+               if(gp->traceback != nil) {
+                       runtime_gogo(gp);
                }
 
-               runtime_printtrace(traceback.pcbuf, traceback.c);
-               runtime_goroutinetrailer(g);
+               runtime_printtrace(tb.locbuf, tb.c, false);
+               runtime_goroutinetrailer(gp);
        }
 }
 
@@ -648,8 +693,8 @@ gtraceback(G* gp)
 
        traceback = gp->traceback;
        gp->traceback = nil;
-       traceback->c = runtime_callers(1, traceback->pcbuf,
-               sizeof traceback->pcbuf / sizeof traceback->pcbuf[0]);
+       traceback->c = runtime_callers(1, traceback->locbuf,
+               sizeof traceback->locbuf / sizeof traceback->locbuf[0]);
        runtime_gogo(traceback->gp);
 }
 
@@ -666,22 +711,22 @@ runtime_idlegoroutine(void)
 }
 
 static void
-mcommoninit(M *m)
+mcommoninit(M *mp)
 {
-       m->id = runtime_sched.mcount++;
-       m->fastrand = 0x49f6428aUL + m->id + runtime_cputicks();
+       mp->id = runtime_sched.mcount++;
+       mp->fastrand = 0x49f6428aUL + mp->id + runtime_cputicks();
 
-       if(m->mcache == nil)
-               m->mcache = runtime_allocmcache();
+       if(mp->mcache == nil)
+               mp->mcache = runtime_allocmcache();
 
-       runtime_callers(1, m->createstack, nelem(m->createstack));
+       runtime_callers(1, mp->createstack, nelem(mp->createstack));
 
        // Add to runtime_allm so garbage collector doesn't free m
        // when it is just in a register or thread-local storage.
-       m->alllink = runtime_allm;
+       mp->alllink = runtime_allm;
        // runtime_NumCgoCall() iterates over allm w/o schedlock,
        // so we need to publish it safely.
-       runtime_atomicstorep(&runtime_allm, m);
+       runtime_atomicstorep(&runtime_allm, mp);
 }
 
 // Try to increment mcpu.  Report whether succeeded.
@@ -701,34 +746,34 @@ canaddmcpu(void)
 
 // Put on `g' queue.  Sched must be locked.
 static void
-gput(G *g)
+gput(G *gp)
 {
-       M *m;
+       M *mp;
 
        // If g is wired, hand it off directly.
-       if((m = g->lockedm) != nil && canaddmcpu()) {
-               mnextg(m, g);
+       if((mp = gp->lockedm) != nil && canaddmcpu()) {
+               mnextg(mp, gp);
                return;
        }
 
        // If g is the idle goroutine for an m, hand it off.
-       if(g->idlem != nil) {
-               if(g->idlem->idleg != nil) {
-                       runtime_printf("m%d idle out of sync: g%d g%d\n",
-                               g->idlem->id,
-                               g->idlem->idleg->goid, g->goid);
+       if(gp->idlem != nil) {
+               if(gp->idlem->idleg != nil) {
+                       runtime_printf("m%d idle out of sync: g%D g%D\n",
+                               gp->idlem->id,
+                               gp->idlem->idleg->goid, gp->goid);
                        runtime_throw("runtime: double idle");
                }
-               g->idlem->idleg = g;
+               gp->idlem->idleg = gp;
                return;
        }
 
-       g->schedlink = nil;
+       gp->schedlink = nil;
        if(runtime_sched.ghead == nil)
-               runtime_sched.ghead = g;
+               runtime_sched.ghead = gp;
        else
-               runtime_sched.gtail->schedlink = g;
-       runtime_sched.gtail = g;
+               runtime_sched.gtail->schedlink = gp;
+       runtime_sched.gtail = gp;
 
        // increment gwait.
        // if it transitions to nonzero, set atomic gwaiting bit.
@@ -747,11 +792,11 @@ haveg(void)
 static G*
 gget(void)
 {
-       G *g;
+       G *gp;
 
-       g = runtime_sched.ghead;
-       if(g){
-               runtime_sched.ghead = g->schedlink;
+       gp = runtime_sched.ghead;
+       if(gp) {
+               runtime_sched.ghead = gp->schedlink;
                if(runtime_sched.ghead == nil)
                        runtime_sched.gtail = nil;
                // decrement gwait.
@@ -759,45 +804,45 @@ gget(void)
                if(--runtime_sched.gwait == 0)
                        runtime_xadd(&runtime_sched.atomic, -1<<gwaitingShift);
        } else if(m->idleg != nil) {
-               g = m->idleg;
+               gp = m->idleg;
                m->idleg = nil;
        }
-       return g;
+       return gp;
 }
 
 // Put on `m' list.  Sched must be locked.
 static void
-mput(M *m)
+mput(M *mp)
 {
-       m->schedlink = runtime_sched.mhead;
-       runtime_sched.mhead = m;
+       mp->schedlink = runtime_sched.mhead;
+       runtime_sched.mhead = mp;
        runtime_sched.mwait++;
 }
 
 // Get an `m' to run `g'.  Sched must be locked.
 static M*
-mget(G *g)
+mget(G *gp)
 {
-       M *m;
+       M *mp;
 
        // if g has its own m, use it.
-       if(g && (m = g->lockedm) != nil)
-               return m;
+       if(gp && (mp = gp->lockedm) != nil)
+               return mp;
 
        // otherwise use general m pool.
-       if((m = runtime_sched.mhead) != nil){
-               runtime_sched.mhead = m->schedlink;
+       if((mp = runtime_sched.mhead) != nil) {
+               runtime_sched.mhead = mp->schedlink;
                runtime_sched.mwait--;
        }
-       return m;
+       return mp;
 }
 
 // Mark g ready to run.
 void
-runtime_ready(G *g)
+runtime_ready(G *gp)
 {
        schedlock();
-       readylocked(g);
+       readylocked(gp);
        schedunlock();
 }
 
@@ -805,23 +850,23 @@ runtime_ready(G *g)
 // G might be running already and about to stop.
 // The sched lock protects g->status from changing underfoot.
 static void
-readylocked(G *g)
+readylocked(G *gp)
 {
-       if(g->m){
+       if(gp->m) {
                // Running on another machine.
                // Ready it when it stops.
-               g->readyonstop = 1;
+               gp->readyonstop = 1;
                return;
        }
 
        // Mark runnable.
-       if(g->status == Grunnable || g->status == Grunning) {
-               runtime_printf("goroutine %d has status %d\n", g->goid, g->status);
+       if(gp->status == Grunnable || gp->status == Grunning) {
+               runtime_printf("goroutine %D has status %d\n", gp->goid, gp->status);
                runtime_throw("bad g->status in ready");
        }
-       g->status = Grunnable;
+       gp->status = Grunnable;
 
-       gput(g);
+       gput(gp);
        matchmg();
 }
 
@@ -829,23 +874,23 @@ readylocked(G *g)
 // debuggers can set a breakpoint here and catch all
 // new goroutines.
 static void
-newprocreadylocked(G *g)
+newprocreadylocked(G *gp)
 {
-       readylocked(g);
+       readylocked(gp);
 }
 
 // Pass g to m for running.
 // Caller has already incremented mcpu.
 static void
-mnextg(M *m, G *g)
+mnextg(M *mp, G *gp)
 {
        runtime_sched.grunning++;
-       m->nextg = g;
-       if(m->waitnextg) {
-               m->waitnextg = 0;
+       mp->nextg = gp;
+       if(mp->waitnextg) {
+               mp->waitnextg = 0;
                if(mwakeup != nil)
                        runtime_notewakeup(&mwakeup->havenextg);
-               mwakeup = m;
+               mwakeup = mp;
        }
 }
 
@@ -936,6 +981,7 @@ top:
        if((scvg == nil && runtime_sched.grunning == 0) ||
           (scvg != nil && runtime_sched.grunning == 1 && runtime_sched.gwait == 0 &&
            (scvg->status == Grunning || scvg->status == Gsyscall))) {
+               m->throwing = -1;  // do not dump full stacks
                runtime_throw("all goroutines are asleep - deadlock!");
        }
 
@@ -969,35 +1015,38 @@ top:
 }
 
 int32
-runtime_helpgc(bool *extra)
+runtime_gcprocs(void)
 {
-       M *mp;
-       int32 n, max;
-
-       // Figure out how many CPUs to use.
+       int32 n;
+       
+       // Figure out how many CPUs to use during GC.
        // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc.
-       max = runtime_gomaxprocs;
-       if(max > runtime_ncpu)
-               max = runtime_ncpu > 0 ? runtime_ncpu : 1;
-       if(max > MaxGcproc)
-               max = MaxGcproc;
+       n = runtime_gomaxprocs;
+       if(n > runtime_ncpu)
+               n = runtime_ncpu > 0 ? runtime_ncpu : 1;
+       if(n > MaxGcproc)
+               n = MaxGcproc;
+       if(n > runtime_sched.mwait+1) // one M is currently running
+               n = runtime_sched.mwait+1;
+       return n;
+}
 
-       // We're going to use one CPU no matter what.
-       // Figure out the max number of additional CPUs.
-       max--;
+void
+runtime_helpgc(int32 nproc)
+{
+       M *mp;
+       int32 n;
 
        runtime_lock(&runtime_sched);
-       n = 0;
-       while(n < max && (mp = mget(nil)) != nil) {
-               n++;
+       for(n = 1; n < nproc; n++) { // one M is currently running
+               mp = mget(nil);
+               if(mp == nil)
+                       runtime_throw("runtime_gcprocs inconsistency");
                mp->helpgc = 1;
                mp->waitnextg = 0;
                runtime_notewakeup(&mp->havenextg);
        }
        runtime_unlock(&runtime_sched);
-       if(extra)
-               *extra = n != max;
-       return n;
 }
 
 void
@@ -1037,26 +1086,38 @@ runtime_stoptheworld(void)
 }
 
 void
-runtime_starttheworld(bool extra)
+runtime_starttheworld(void)
 {
-       M *m;
+       M *mp;
+       int32 max;
+       
+       // Figure out how many CPUs GC could possibly use.
+       max = runtime_gomaxprocs;
+       if(max > runtime_ncpu)
+               max = runtime_ncpu > 0 ? runtime_ncpu : 1;
+       if(max > MaxGcproc)
+               max = MaxGcproc;
 
        schedlock();
        runtime_gcwaiting = 0;
        setmcpumax(runtime_gomaxprocs);
        matchmg();
-       if(extra && canaddmcpu()) {
-               // Start a new m that will (we hope) be idle
-               // and so available to help when the next
-               // garbage collection happens.
+       if(runtime_gcprocs() < max && canaddmcpu()) {
+               // If GC could have used another helper proc, start one now,
+               // in the hope that it will be available next time.
+               // It would have been even better to start it before the collection,
+               // but doing so requires allocating memory, so it's tricky to
+               // coordinate.  This lazy approach works out in practice:
+               // we don't mind if the first couple gc rounds don't have quite
+               // the maximum number of procs.
                // canaddmcpu above did mcpu++
                // (necessary, because m will be doing various
                // initialization work so is definitely running),
                // but m is not running a specific goroutine,
                // so set the helpgc flag as a signal to m's
                // first schedule(nil) to mcpu-- and grunning--.
-               m = runtime_newm();
-               m->helpgc = 1;
+               mp = runtime_newm();
+               mp->helpgc = 1;
                runtime_sched.grunning++;
        }
        schedunlock();
@@ -1110,6 +1171,11 @@ runtime_mstart(void* mp)
                runtime_initsig();
 
        schedule(nil);
+
+       // TODO(brainman): This point is never reached, because scheduler
+       // does not release os threads at the moment. But once this path
+       // is enabled, we must remove our seh here.
+
        return nil;
 }
 
@@ -1148,14 +1214,26 @@ matchmg(void)
 M*
 runtime_newm(void)
 {
-       M *m;
+       M *mp;
        pthread_attr_t attr;
        pthread_t tid;
        size_t stacksize;
+       sigset_t clear;
+       sigset_t old;
+       int ret;
+
+#if 0
+       static const Type *mtype;  // The Go type M
+       if(mtype == nil) {
+               Eface e;
+               runtime_gc_m_ptr(&e);
+               mtype = ((const PtrType*)e.__type_descriptor)->__element_type;
+       }
+#endif
 
-       m = runtime_malloc(sizeof(M));
-       mcommoninit(m);
-       m->g0 = runtime_malg(-1, nil, nil);
+       mp = runtime_mal(sizeof *mp);
+       mcommoninit(mp);
+       mp->g0 = runtime_malg(-1, nil, nil);
 
        if(pthread_attr_init(&attr) != 0)
                runtime_throw("pthread_attr_init");
@@ -1175,10 +1253,24 @@ runtime_newm(void)
        if(pthread_attr_setstacksize(&attr, stacksize) != 0)
                runtime_throw("pthread_attr_setstacksize");
 
-       if(pthread_create(&tid, &attr, runtime_mstart, m) != 0)
+       // Block signals during pthread_create so that the new thread
+       // starts with signals disabled.  It will enable them in minit.
+       sigfillset(&clear);
+
+#ifdef SIGTRAP
+       // Blocking SIGTRAP reportedly breaks gdb on Alpha GNU/Linux.
+       sigdelset(&clear, SIGTRAP);
+#endif
+
+       sigemptyset(&old);
+       sigprocmask(SIG_BLOCK, &clear, &old);
+       ret = pthread_create(&tid, &attr, runtime_mstart, mp);
+       sigprocmask(SIG_SETMASK, &old, nil);
+
+       if (ret != 0)
                runtime_throw("pthread_create");
 
-       return m;
+       return mp;
 }
 
 // One round of scheduler: find a goroutine and run it.
@@ -1202,7 +1294,7 @@ schedule(G *gp)
                if(atomic_mcpu(v) > maxgomaxprocs)
                        runtime_throw("negative mcpu in scheduler");
 
-               switch(gp->status){
+               switch(gp->status) {
                case Grunnable:
                case Gdead:
                        // Shouldn't have been running!
@@ -1212,6 +1304,8 @@ schedule(G *gp)
                        gput(gp);
                        break;
                case Gmoribund:
+                       if(raceenabled)
+                               runtime_racegoend(gp->goid);
                        gp->status = Gdead;
                        if(gp->lockedm) {
                                gp->lockedm = nil;
@@ -1224,7 +1318,7 @@ schedule(G *gp)
                                runtime_exit(0);
                        break;
                }
-               if(gp->readyonstop){
+               if(gp->readyonstop) {
                        gp->readyonstop = 0;
                        readylocked(gp);
                }
@@ -1272,6 +1366,18 @@ runtime_gosched(void)
        runtime_mcall(schedule);
 }
 
+// Puts the current goroutine into a waiting state and unlocks the lock.
+// The goroutine can be made runnable again by calling runtime_ready(gp).
+void
+runtime_park(void (*unlockf)(Lock*), Lock *lock, const char *reason)
+{
+       g->status = Gwaiting;
+       g->waitreason = reason;
+       if(unlockf)
+               unlockf(lock);
+       runtime_gosched();
+}
+
 // 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 and cgocall,
@@ -1425,7 +1531,7 @@ runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize)
 /* For runtime package testing.  */
 
 void runtime_testing_entersyscall(void)
-  __asm__("runtime.entersyscall");
+  __asm__ (GOSYM_PREFIX "runtime.entersyscall");
 
 void
 runtime_testing_entersyscall()
@@ -1434,7 +1540,7 @@ runtime_testing_entersyscall()
 }
 
 void runtime_testing_exitsyscall(void)
-  __asm__("runtime.exitsyscall");
+  __asm__ (GOSYM_PREFIX "runtime.exitsyscall");
 
 void
 runtime_testing_exitsyscall()
@@ -1448,10 +1554,15 @@ __go_go(void (*fn)(void*), void* arg)
        byte *sp;
        size_t spsize;
        G *newg;
+       int64 goid;
+
+       goid = runtime_xadd64((uint64*)&runtime_sched.goidgen, 1);
+       if(raceenabled)
+               runtime_racegostart(goid, runtime_getcallerpc(&fn));
 
        schedlock();
 
-       if((newg = gfget()) != nil){
+       if((newg = gfget()) != nil) {
 #ifdef USING_SPLIT_STACK
                int dont_block_signals = 0;
 
@@ -1482,8 +1593,7 @@ __go_go(void (*fn)(void*), void* arg)
        newg->gopc = (uintptr)__builtin_return_address(0);
 
        runtime_sched.gcount++;
-       runtime_sched.goidgen++;
-       newg->goid = runtime_sched.goidgen;
+       newg->goid = goid;
 
        if(sp == nil)
                runtime_throw("nil g->stack0");
@@ -1512,52 +1622,25 @@ __go_go(void (*fn)(void*), void* arg)
 
 // Put on gfree list.  Sched must be locked.
 static void
-gfput(G *g)
+gfput(G *gp)
 {
-       g->schedlink = runtime_sched.gfree;
-       runtime_sched.gfree = g;
+       gp->schedlink = runtime_sched.gfree;
+       runtime_sched.gfree = gp;
 }
 
 // Get from gfree list.  Sched must be locked.
 static G*
 gfget(void)
 {
-       G *g;
-
-       g = runtime_sched.gfree;
-       if(g)
-               runtime_sched.gfree = g->schedlink;
-       return g;
-}
-
-// Run all deferred functions for the current goroutine.
-static void
-rundefer(void)
-{
-       Defer *d;
-
-       while((d = g->defer) != nil) {
-               void (*pfn)(void*);
-
-               pfn = d->__pfn;
-               d->__pfn = nil;
-               if (pfn != nil)
-                       (*pfn)(d->__arg);
-               g->defer = d->__next;
-               runtime_free(d);
-       }
-}
-
-void runtime_Goexit (void) asm ("runtime.Goexit");
+       G *gp;
 
-void
-runtime_Goexit(void)
-{
-       rundefer();
-       runtime_goexit();
+       gp = runtime_sched.gfree;
+       if(gp)
+               runtime_sched.gfree = gp->schedlink;
+       return gp;
 }
 
-void runtime_Gosched (void) asm ("runtime.Gosched");
+void runtime_Gosched (void) __asm__ (GOSYM_PREFIX "runtime.Gosched");
 
 void
 runtime_Gosched(void)
@@ -1636,7 +1719,7 @@ runtime_lockedOSThread(void)
 // for testing of callbacks
 
 _Bool runtime_golockedOSThread(void)
-  asm("runtime.golockedOSThread");
+  __asm__ (GOSYM_PREFIX "runtime.golockedOSThread");
 
 _Bool
 runtime_golockedOSThread(void)
@@ -1651,10 +1734,10 @@ runtime_mid()
        return m->id;
 }
 
-int32 runtime_NumGoroutine (void)
-  __asm__ ("runtime.NumGoroutine");
+intgo runtime_NumGoroutine (void)
+  __asm__ (GOSYM_PREFIX "runtime.NumGoroutine");
 
-int32
+intgo
 runtime_NumGoroutine()
 {
        return runtime_sched.gcount;
@@ -1677,13 +1760,14 @@ static struct {
        void (*fn)(uintptr*, int32);
        int32 hz;
        uintptr pcbuf[100];
+       Location locbuf[100];
 } prof;
 
 // Called if we receive a SIGPROF signal.
 void
 runtime_sigprof()
 {
-       int32 n;
+       int32 n, i;
 
        if(prof.fn == nil || prof.hz == 0)
                return;
@@ -1693,7 +1777,9 @@ runtime_sigprof()
                runtime_unlock(&prof);
                return;
        }
-       n = runtime_callers(0, prof.pcbuf, nelem(prof.pcbuf));
+       n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf));
+       for(i = 0; i < n; i++)
+               prof.pcbuf[i] = prof.locbuf[i].pc;
        if(n > 0)
                prof.fn(prof.pcbuf, n);
        runtime_unlock(&prof);