if(nacl) {
reg[REG_BP]++;
reg[REG_R15]++;
+ } else if(framepointer_enabled) {
+ // BP is part of the calling convention of framepointer_enabled.
+ reg[REG_BP]++;
}
}
if(nacl) {
reg[REG_BP]--;
reg[REG_R15]--;
+ } else if(framepointer_enabled) {
+ reg[REG_BP]--;
}
b &= 0xffffL;
if(nacl)
b &= ~((1<<(REG_BP-REG_AX)) | (1<<(REG_R15-REG_AX)));
+ else if(framepointer_enabled)
+ // BP is part of the calling convention if framepointer_enabled.
+ b &= ~(1<<(REG_BP-REG_AX));
if(b == 0)
return 0;
return bitno(b) + REG_AX;
int *val;
} exper[] = {
{"fieldtrack", &fieldtrack_enabled},
- {"basepointer", &framepointer_enabled},
+ {"framepointer", &framepointer_enabled},
};
static void
{
Prog *p, *q, *p1, *p2;
int32 autoffset, deltasp;
- int a, pcsize;
+ int a, pcsize, bpsize;
vlong textstksiz, textarg;
if(ctxt->tlsg == nil)
if(autoffset < 0)
autoffset = 0;
+ if(framepointer_enabled && autoffset > 0) {
+ // Make room for to save a base pointer. If autoffset == 0,
+ // this might do something special like a tail jump to
+ // another function, so in that case we omit this.
+ bpsize = ctxt->arch->ptrsize;
+ autoffset += bpsize;
+ textstksiz += bpsize;
+ p->to.offset = ((uint64)p->to.offset & (0xffffffffull<<32)) | (uint32)autoffset;
+ } else {
+ bpsize = 0;
+ }
+
cursym->args = p->to.offset>>32;
cursym->locals = textstksiz;
if(q != nil)
q->pcond = p;
deltasp = autoffset;
+
+ if(bpsize > 0) {
+ // Save caller's BP
+ p = appendp(ctxt, p);
+ p->as = AMOVQ;
+ p->from.type = TYPE_REG;
+ p->from.reg = REG_BP;
+ p->to.type = TYPE_MEM;
+ p->to.reg = REG_SP;
+ p->to.scale = 1;
+ p->to.offset = autoffset - bpsize;
+
+ // Move current frame to BP
+ p = appendp(ctxt, p);
+ p->as = ALEAQ;
+ p->from.type = TYPE_MEM;
+ p->from.reg = REG_SP;
+ p->from.scale = 1;
+ p->from.offset = autoffset - bpsize;
+ p->to.type = TYPE_REG;
+ p->to.reg = REG_BP;
+ }
if(cursym->text->from.scale & WRAPPER) {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
pcsize = p->mode/8;
a = p->from.name;
if(a == NAME_AUTO)
- p->from.offset += deltasp;
+ p->from.offset += deltasp - bpsize;
if(a == NAME_PARAM)
p->from.offset += deltasp + pcsize;
a = p->to.name;
if(a == NAME_AUTO)
- p->to.offset += deltasp;
+ p->to.offset += deltasp - bpsize;
if(a == NAME_PARAM)
p->to.offset += deltasp + pcsize;
ctxt->diag("unbalanced PUSH/POP");
if(autoffset) {
+ if(bpsize > 0) {
+ // Restore caller's BP
+ p->as = AMOVQ;
+ p->from.type = TYPE_MEM;
+ p->from.reg = REG_SP;
+ p->from.scale = 1;
+ p->from.offset = autoffset - bpsize;
+ p->to.type = TYPE_REG;
+ p->to.reg = REG_BP;
+ p = appendp(ctxt, p);
+ }
+
p->as = AADJSP;
p->from.type = TYPE_CONST;
p->from.offset = -autoffset;
thechar = '6'
_BigEndian = 0
_CacheLineSize = 64
- _RuntimeGogoBytes = 64 + (goos_plan9|goos_solaris|goos_windows)*16
+ _RuntimeGogoBytes = 80 + (goos_plan9|goos_solaris|goos_windows)*16
_PhysPageSize = 4096
_PCQuantum = 1
_Int64Align = 8
MOVQ BX, gobuf_pc(AX)
MOVQ $0, gobuf_ret(AX)
MOVQ $0, gobuf_ctxt(AX)
+ MOVQ BP, gobuf_bp(AX)
get_tls(CX)
MOVQ g(CX), BX
MOVQ BX, gobuf_g(AX)
MOVQ gobuf_sp(BX), SP // restore SP
MOVQ gobuf_ret(BX), AX
MOVQ gobuf_ctxt(BX), DX
+ MOVQ gobuf_bp(BX), BP
MOVQ $0, gobuf_sp(BX) // clear to help garbage collector
MOVQ $0, gobuf_ret(BX)
MOVQ $0, gobuf_ctxt(BX)
+ MOVQ $0, gobuf_bp(BX)
MOVQ gobuf_pc(BX), BX
JMP BX
LEAQ fn+0(FP), BX // caller's SP
MOVQ BX, (g_sched+gobuf_sp)(AX)
MOVQ AX, (g_sched+gobuf_g)(AX)
+ MOVQ BP, (g_sched+gobuf_bp)(AX)
// switch to m->g0 & its stack, call fn
MOVQ g(CX), BX
MOVQ SI, (g_sched+gobuf_pc)(AX)
MOVQ SP, (g_sched+gobuf_sp)(AX)
MOVQ AX, (g_sched+gobuf_g)(AX)
+ MOVQ BP, (g_sched+gobuf_bp)(AX)
// switch to g0
MOVQ DX, g(CX)
LEAQ 8(SP), AX // f's SP
MOVQ AX, (g_sched+gobuf_sp)(SI)
MOVQ DX, (g_sched+gobuf_ctxt)(SI)
+ MOVQ BP, (g_sched+gobuf_bp)(SI)
// Call newstack on m->g0's stack.
MOVQ m_g0(BX), BX
MOVQ R9, (g_sched+gobuf_sp)(R8)
MOVQ $0, (g_sched+gobuf_ret)(R8)
MOVQ $0, (g_sched+gobuf_ctxt)(R8)
+ MOVQ BP, (g_sched+gobuf_bp)(R8)
RET
// asmcgocall(void(*fn)(void*), void *arg)
MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
MOVQ (g_sched+gobuf_pc)(SI), BX
MOVQ BX, -8(DI)
- LEAQ -(8+8)(DI), SP
+ // Compute the size of the frame, including return PC and, if
+ // GOEXPERIMENT=framepointer, the saved based pointer
+ LEAQ x+0(FP), AX
+ SUBQ SP, AX
+ SUBQ AX, DI
+ MOVQ DI, SP
+
MOVQ R8, 0(SP)
CALL runtime·cgocallbackg(SB)
MOVQ 0(SP), R8
+ // Compute the size of the frame again. FP and SP have
+ // completely different values here than they did above,
+ // but only their difference matters.
+ LEAQ x+0(FP), AX
+ SUBQ SP, AX
+
// Restore g->sched (== m->curg->sched) from saved values.
get_tls(CX)
MOVQ g(CX), SI
- MOVQ 8(SP), BX
+ MOVQ SP, DI
+ ADDQ AX, DI
+ MOVQ -8(DI), BX
MOVQ BX, (g_sched+gobuf_pc)(SI)
- LEAQ (8+8)(SP), DI
MOVQ DI, (g_sched+gobuf_sp)(SI)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
case "amd64":
// On amd64, stack frame is one word, plus caller PC.
+ if framepointer_enabled {
+ // In this case, there's also saved BP.
+ cb = (*args)(unsafe.Pointer(sp + 3*ptrSize))
+ break
+ }
cb = (*args)(unsafe.Pointer(sp + 2*ptrSize))
case "386":
// On 386, stack frame is three words, plus caller PC.
sched.maxmcount = 10000
+ // Cache the framepointer experiment. This affects stack unwinding.
+ framepointer_enabled = haveexperiment("framepointer")
+
tracebackinit()
symtabinit()
stackinit()
ctxt unsafe.Pointer // this has to be a pointer so that gc scans it
ret uintreg
lr uintptr
+ bp uintptr // for GOEXPERIMENT=framepointer
}
// Known to compiler.
var stackfreequeue stack
+// Cached value of haveexperiment("framepointer")
+var framepointer_enabled bool
+
func stackinit() {
if _StackCacheSize&_PageMask != 0 {
throw("cache size must be a multiple of page size")
// | args from caller |
// +------------------+ <- frame->argp
// | return address |
+// +------------------+
+// | caller's BP (*) | (*) if framepointer_enabled && varp < sp
// +------------------+ <- frame->varp
// | locals |
// +------------------+
adjustpointers(unsafe.Pointer(frame.varp-size), &bv, adjinfo, f)
}
+ // Adjust saved base pointer if there is one.
+ if thechar == '6' && frame.argp-frame.varp == 2*ptrSize {
+ if !framepointer_enabled {
+ print("runtime: found space for saved base pointer, but no framepointer experiment")
+ throw("bad frame layout")
+ }
+ if stackDebug >= 3 {
+ print(" saved bp\n")
+ }
+ adjustpointer(adjinfo, unsafe.Pointer(frame.varp))
+ }
+
// Adjust arguments.
if frame.arglen > 0 {
var bv bitvector
frame.varp -= regSize
}
+ // If framepointer_enabled and there's a frame, then
+ // there's a saved bp here.
+ if GOARCH == "amd64" && frame.varp > frame.sp && framepointer_enabled {
+ frame.varp -= ptrSize
+ }
+
// Derive size of arguments.
// Most functions have a fixed-size argument block,
// so we can use metadata about the function f.
goarch = runtime.GOARCH
}
+ thechar := ""
+ if gochar, err := exec.Command("go", "env", "GOCHAR").Output(); err != nil {
+ bug()
+ fmt.Printf("running go env GOCHAR: %v\n", err)
+ return
+ } else {
+ thechar = strings.TrimSpace(string(gochar))
+ }
+
+ version, err := exec.Command("go", "tool", thechar+"g", "-V").Output()
+ if err != nil {
+ bug()
+ fmt.Printf("running go tool %sg -V: %v\n", thechar, err)
+ return
+ }
+ if strings.Contains(string(version), "framepointer") {
+ // Skip this test if GOEXPERIMENT=framepointer
+ return
+ }
+
dir, err := ioutil.TempDir("", "go-test-nosplit")
if err != nil {
bug()