runtime: add a constant for the smallest possible stack frame

Shared libraries on ppc64le will require a larger minimum stack frame (because
the ABI mandates that the TOC pointer is available at 24(R1)). So to prepare
for this, make a constant for the fixed part of a stack and use that where
necessary.

Change-Id: I447949f4d725003bb82e7d2cf7991c1bca5aa887
Reviewed-on: https://go-review.googlesource.com/15523
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/runtime/arch_386.go b/src/runtime/arch_386.go
index 4ab00c3..75e94ec 100644
--- a/src/runtime/arch_386.go
+++ b/src/runtime/arch_386.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 1
 	_Int64Align    = 4
 	hugePageSize   = 1 << 21
+	minFrameSize   = 0
 )
 
 type uintreg uint32
diff --git a/src/runtime/arch_amd64.go b/src/runtime/arch_amd64.go
index b2ca077..d7721f7 100644
--- a/src/runtime/arch_amd64.go
+++ b/src/runtime/arch_amd64.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 1
 	_Int64Align    = 8
 	hugePageSize   = 1 << 21
+	minFrameSize   = 0
 )
 
 type uintreg uint64
diff --git a/src/runtime/arch_amd64p32.go b/src/runtime/arch_amd64p32.go
index 3f66822..aa8343a 100644
--- a/src/runtime/arch_amd64p32.go
+++ b/src/runtime/arch_amd64p32.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 1
 	_Int64Align    = 8
 	hugePageSize   = 1 << 21
+	minFrameSize   = 0
 )
 
 type uintreg uint64
diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go
index d5d5770..aa3e180 100644
--- a/src/runtime/arch_arm.go
+++ b/src/runtime/arch_arm.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 4
 	_Int64Align    = 4
 	hugePageSize   = 0
+	minFrameSize   = 4
 )
 
 type uintreg uint32
diff --git a/src/runtime/arch_arm64.go b/src/runtime/arch_arm64.go
index f26227a..f01c26d 100644
--- a/src/runtime/arch_arm64.go
+++ b/src/runtime/arch_arm64.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 4
 	_Int64Align    = 8
 	hugePageSize   = 0
+	minFrameSize   = 8
 )
 
 type uintreg uint64
diff --git a/src/runtime/arch_ppc64.go b/src/runtime/arch_ppc64.go
index a2cd85c..273cc56 100644
--- a/src/runtime/arch_ppc64.go
+++ b/src/runtime/arch_ppc64.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 4
 	_Int64Align    = 8
 	hugePageSize   = 0
+	minFrameSize   = 8
 )
 
 type uintreg uint64
diff --git a/src/runtime/arch_ppc64le.go b/src/runtime/arch_ppc64le.go
index 4f89da3..e4eb9e5 100644
--- a/src/runtime/arch_ppc64le.go
+++ b/src/runtime/arch_ppc64le.go
@@ -12,6 +12,7 @@
 	_PCQuantum     = 4
 	_Int64Align    = 8
 	hugePageSize   = 0
+	minFrameSize   = 8
 )
 
 type uintreg uint64
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index f09a66a..d39e660 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -237,10 +237,22 @@
 		// On 386, stack frame is three words, plus caller PC.
 		cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
 	case "ppc64", "ppc64le":
-		// On ppc64, stack frame is two words and there's a
-		// saved LR between SP and the stack frame and between
-		// the stack frame and the arguments.
-		cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
+		// On ppc64, the callback arguments are in the arguments area of
+		// cgocallback's stack frame. The stack looks like this:
+		// +--------------------+------------------------------+
+		// |                    | ...                          |
+		// | cgoexp_$fn         +------------------------------+
+		// |                    | fixed frame area             |
+		// +--------------------+------------------------------+
+		// |                    | arguments area               |
+		// | cgocallback        +------------------------------+ <- sp + 2*minFrameSize + 2*ptrSize
+		// |                    | fixed frame area             |
+		// +--------------------+------------------------------+ <- sp + minFrameSize + 2*ptrSize
+		// |                    | local variables (2 pointers) |
+		// | cgocallback_gofunc +------------------------------+ <- sp + minFrameSize
+		// |                    | fixed frame area             |
+		// +--------------------+------------------------------+ <- sp
+		cb = (*args)(unsafe.Pointer(sp + 2*minFrameSize + 2*ptrSize))
 	}
 
 	// Invoke callback.
@@ -271,14 +283,10 @@
 	switch GOARCH {
 	default:
 		throw("unwindm not implemented")
-	case "386", "amd64":
-		sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp))
-	case "arm":
-		sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 4))
+	case "386", "amd64", "arm", "ppc64", "ppc64le":
+		sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + minFrameSize))
 	case "arm64":
 		sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16))
-	case "ppc64", "ppc64le":
-		sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 8))
 	}
 	releasem(mp)
 }
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 498c355..95586dc 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -496,12 +496,10 @@
 	size := frame.varp - frame.sp
 	var minsize uintptr
 	switch thechar {
-	case '6', '8':
-		minsize = 0
 	case '7':
 		minsize = spAlign
 	default:
-		minsize = ptrSize
+		minsize = minFrameSize
 	}
 	if size > minsize {
 		stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 320c174..24431c8 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -531,8 +531,6 @@
 //uint32 runtime·panicking;
 var paniclk mutex
 
-const hasLinkRegister = GOARCH == "arm" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le"
-
 // Unwind the stack after a deferred function calls recover
 // after a panic.  Then arrange to continue running as though
 // the caller of the deferred function returned normally.
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 614de51..ef28467 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -2256,17 +2256,14 @@
 		throw("newproc1: new g is not Gdead")
 	}
 
-	totalSize := 4*regSize + uintptr(siz) // extra space in case of reads slightly beyond frame
-	if hasLinkRegister {
-		totalSize += ptrSize
-	}
-	totalSize += -totalSize & (spAlign - 1) // align to spAlign
+	totalSize := 4*regSize + uintptr(siz) + minFrameSize // extra space in case of reads slightly beyond frame
+	totalSize += -totalSize & (spAlign - 1)              // align to spAlign
 	sp := newg.stack.hi - totalSize
 	spArg := sp
-	if hasLinkRegister {
+	if usesLR {
 		// caller's LR
 		*(*unsafe.Pointer)(unsafe.Pointer(sp)) = nil
-		spArg += ptrSize
+		spArg += minFrameSize
 	}
 	memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
 
diff --git a/src/runtime/signal_ppc64x.go b/src/runtime/signal_ppc64x.go
index bad9fe6..71055b6 100644
--- a/src/runtime/signal_ppc64x.go
+++ b/src/runtime/signal_ppc64x.go
@@ -82,7 +82,7 @@
 		// functions are correctly handled. This smashes
 		// the stack frame but we're not going back there
 		// anyway.
-		sp := c.sp() - ptrSize
+		sp := c.sp() - minFrameSize
 		c.set_sp(sp)
 		*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
 
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 24d3727..1809a4d9a 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -578,12 +578,10 @@
 	size := frame.varp - frame.sp
 	var minsize uintptr
 	switch thechar {
-	case '6', '8':
-		minsize = 0
 	case '7':
 		minsize = spAlign
 	default:
-		minsize = ptrSize
+		minsize = minFrameSize
 	}
 	if size > minsize {
 		var bv bitvector
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 544ce27..2d223ce 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -26,9 +26,10 @@
 // stores an 8-byte return PC onto the stack. To accommodate this, we use regSize
 // as the size of the architecture-pushed return PC.
 //
-// usesLR is defined below. ptrSize and regSize are defined in stubs.go.
+// usesLR is defined below in terms of minFrameSize, which is defined in
+// arch_$GOARCH.go. ptrSize and regSize are defined in stubs.go.
 
-const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
+const usesLR = minFrameSize > 0
 
 var (
 	// initialized in tracebackinit
@@ -295,10 +296,7 @@
 		// in package runtime and reflect, and for those we use call-specific
 		// metadata recorded by f's caller.
 		if callback != nil || printing {
-			frame.argp = frame.fp
-			if usesLR {
-				frame.argp += ptrSize
-			}
+			frame.argp = frame.fp + minFrameSize
 			setArgInfo(&frame, f, callback != nil)
 		}
 
@@ -396,7 +394,7 @@
 		// before faking a call to sigpanic.
 		if usesLR && waspanic {
 			x := *(*uintptr)(unsafe.Pointer(frame.sp))
-			frame.sp += ptrSize
+			frame.sp += minFrameSize
 			if GOARCH == "arm64" {
 				// arm64 needs 16-byte aligned SP, always
 				frame.sp += ptrSize
@@ -496,10 +494,7 @@
 		// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
 		switch funcname(f) {
 		case "reflect.makeFuncStub", "reflect.methodValueCall":
-			arg0 := frame.sp
-			if usesLR {
-				arg0 += ptrSize
-			}
+			arg0 := frame.sp + minFrameSize
 			fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
 			if fn[0] != f.entry {
 				print("runtime: confused by ", funcname(f), "\n")