runtime: scan register backing store on ia64
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 2 Feb 2018 00:16:43 +0000 (00:16 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 2 Feb 2018 00:16:43 +0000 (00:16 +0000)
    On ia64, a separate stack is used for saving/restoring register frames,
    occupying the other end of the stack mapping. This must also be scanned
    for pointers into the heap.

    Reviewed-on: https://go-review.googlesource.com/85276

From-SVN: r257323

gcc/go/gofrontend/MERGE
libgo/go/runtime/runtime2.go
libgo/runtime/proc.c
libgo/runtime/runtime.h
libgo/runtime/stack.c

index cc64fad98ce9e9ebe65632374ad73e001e1813d4..46bdc3c67da6fc3e1e7dacc903659522286d8197 100644 (file)
@@ -1,4 +1,4 @@
-e148068360699f24118950b728f23a5c98e1f85e
+5e8a91bf239c253d7b5c84bd2c1dd3ecb18980e9
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 543086d09aa531a33b9f81bc944dedfd0f44ece5..0299d5a788f38eb32b5bfb2158e5851006f318e1 100644 (file)
@@ -409,11 +409,15 @@ type g struct {
        // gcnextsegment: unused
        // gcnextsp: current SP while executing a syscall
        // gcinitialsp: g0: top of stack; others: start of stack memory
+       // gcnextsp2: current secondary stack pointer (if present)
+       // gcinitialsp2: start of secondary stack (if present)
        gcstack       uintptr
        gcstacksize   uintptr
        gcnextsegment uintptr
        gcnextsp      uintptr
        gcinitialsp   unsafe.Pointer
+       gcnextsp2     uintptr
+       gcinitialsp2  unsafe.Pointer
 
        // gcregs holds the register values while executing a syscall.
        // This is set by getcontext and scanned by the garbage collector.
index 556d86fb7d6c8ab5f218a3ba65f15e10ff52a862..12c055d71ad79e728ac2e16a2eebda84bbee52db 100644 (file)
@@ -308,6 +308,7 @@ runtime_mcall(FuncVal *fv)
        // Ensure that all registers are on the stack for the garbage
        // collector.
        __builtin_unwind_init();
+       flush_registers_to_secondary_stack();
 
        gp = g;
        mp = gp->m;
@@ -322,6 +323,7 @@ runtime_mcall(FuncVal *fv)
                // We have to point to an address on the stack that is
                // below the saved registers.
                gp->gcnextsp = (uintptr)(&afterregs);
+               gp->gcnextsp2 = (uintptr)(secondary_stack_pointer());
 #endif
                gp->fromgogo = false;
                getcontext(ucontext_arg(&gp->context[0]));
@@ -500,6 +502,8 @@ runtime_mstart(void *arg)
        // is the top of the stack, not the bottom.
        gp->gcstacksize = 0;
        gp->gcnextsp = (uintptr)(&arg);
+       gp->gcinitialsp2 = secondary_stack_pointer();
+       gp->gcnextsp2 = (uintptr)(gp->gcinitialsp2);
 #endif
 
        // Save the currently active context.  This will return
@@ -576,6 +580,8 @@ setGContext(void)
        gp->gcstack = 0;
        gp->gcstacksize = 0;
        gp->gcnextsp = (uintptr)(&val);
+       gp->gcinitialsp2 = secondary_stack_pointer();
+       gp->gcnextsp2 = (uintptr)(gp->gcinitialsp2);
 #endif
        getcontext(ucontext_arg(&gp->context[0]));
 
@@ -654,6 +660,7 @@ doentersyscall(uintptr pc, uintptr sp)
                void *v;
 
                g->gcnextsp = (uintptr)(&v);
+               g->gcnextsp2 = (uintptr)(secondary_stack_pointer());
        }
 #endif
 
@@ -694,6 +701,7 @@ doentersyscallblock(uintptr pc, uintptr sp)
                void *v;
 
                g->gcnextsp = (uintptr)(&v);
+               g->gcnextsp2 = (uintptr)(secondary_stack_pointer());
        }
 #endif
 
@@ -756,6 +764,7 @@ runtime_malg(bool allocatestack, bool signalstack, byte** ret_stack, uintptr* re
                *ret_stacksize = (uintptr)stacksize;
                newg->gcinitialsp = *ret_stack;
                newg->gcstacksize = (uintptr)stacksize;
+               newg->gcinitialsp2 = initial_secondary_stack_pointer(*ret_stack);
 #endif
        }
        return newg;
@@ -807,6 +816,7 @@ resetNewG(G *newg, void **sp, uintptr *spsize)
   if(*spsize == 0)
     runtime_throw("bad spsize in resetNewG");
   newg->gcnextsp = (uintptr)(*sp);
+  newg->gcnextsp2 = (uintptr)(newg->gcinitialsp2);
 #endif
 }
 
index 28d550deee510d291f80b92878067afaddf90126..0fafe82144189d743a5674f3ccdf05967fc9353f 100644 (file)
@@ -437,6 +437,23 @@ void       runtime_check(void)
 // the stacks are allocated by the splitstack library.
 extern uintptr runtime_stacks_sys;
 
+/*
+ * ia64's register file is spilled to a separate stack, the register backing
+ * store, on window overflow, and must also be scanned. This occupies the other
+ * end of the normal stack allocation, growing upwards.
+ * We also need to ensure all register windows are flushed to the backing
+ * store, as unlike SPARC, __builtin_unwind_init doesn't do this on ia64.
+ */
+#ifdef __ia64__
+# define secondary_stack_pointer() __builtin_ia64_bsp()
+# define initial_secondary_stack_pointer(stack_alloc) (stack_alloc)
+# define flush_registers_to_secondary_stack() __builtin_ia64_flushrs()
+#else
+# define secondary_stack_pointer() nil
+# define initial_secondary_stack_pointer(stack_alloc) nil
+# define flush_registers_to_secondary_stack()
+#endif
+
 struct backtrace_state;
 extern struct backtrace_state *__go_get_backtrace_state(void);
 extern _Bool __go_file_line(uintptr, int, String*, String*, intgo *);
index 900ca64b7f7d3f8b99faea92b3446c79b56ae2fc..a971e8f9266e73396fc0a15647b43b188ef30c21 100644 (file)
@@ -34,6 +34,7 @@ void doscanstack(G *gp, void* gcw) {
        // Save registers on the stack, so that if we are scanning our
        // own stack we will see them.
        __builtin_unwind_init();
+       flush_registers_to_secondary_stack();
 
        doscanstack1(gp, gcw);
 }
@@ -82,21 +83,32 @@ static void doscanstack1(G *gp, void *gcw) {
 #else
        byte* bottom;
        byte* top;
+       byte* nextsp2;
+       byte* initialsp2;
 
        if(gp == runtime_g()) {
                // Scanning our own stack.
                bottom = (byte*)&gp;
+               nextsp2 = secondary_stack_pointer();
        } else {
                // Scanning another goroutine's stack.
                // The goroutine is usually asleep (the world is stopped).
                bottom = (void*)gp->gcnextsp;
                if(bottom == nil)
                        return;
+               nextsp2 = (void*)gp->gcnextsp2;
        }
        top = (byte*)(void*)(gp->gcinitialsp) + gp->gcstacksize;
        if(top > bottom)
                scanstackblock(bottom, (uintptr)(top - bottom), gcw);
        else
                scanstackblock(top, (uintptr)(bottom - top), gcw);
+       if (nextsp2 != nil) {
+               initialsp2 = (byte*)(void*)(gp->gcinitialsp2);
+               if(initialsp2 > nextsp2)
+                       scanstackblock(nextsp2, (uintptr)(initialsp2 - nextsp2), gcw);
+               else
+                       scanstackblock(initialsp2, (uintptr)(nextsp2 - initialsp2), gcw);
+       }
 #endif
 }