summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2013-09-12 14:00:16 -0400
committerRuss Cox <rsc@golang.org>2013-09-12 14:00:16 -0400
commit8e733af658ac83ee339cebdea1d51cc82dd487c2 (patch)
tree405dce320f88d1a44d1eb8a72a018dc00c435f9a /src/cmd
parent4df23439a9d69efdf986d6712be1577491e54839 (diff)
downloadgo-8e733af658ac83ee339cebdea1d51cc82dd487c2.tar.gz
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1: Issue 5406 identified an interesting case: defer iface.M() may end up calling a wrapper that copies an indirect receiver from the iface value and then calls the real M method. That's two calls down, not just one, and so recover() == nil always in the real M method, even during a panic. [For the purposes of this entire discussion, a wrapper's implementation is a function containing an ordinary call, not the optimized tail call form that is somtimes possible. The tail call does not create a second frame, so it is already handled correctly.] Fix this bug by introducing g->panicwrap, which counts the number of bytes on current stack segment that are due to wrapper calls that should not count against the recover check. All wrapper functions must now adjust g->panicwrap up on entry and back down on exit. This adds slightly to their expense; on the x86 it is a single instruction at entry and exit; on the ARM it is three. However, the alternative is to make a call to recover depend on being able to walk the stack, which I very much want to avoid. We have enough problems walking the stack for garbage collection and profiling. Also, if performance is critical in a specific case, it is already faster to use a pointer receiver and avoid this kind of wrapper entirely. Bug #2: The old code, which did not consider the possibility of two calls, already contained a check to see if the call had split its stack and so the panic-created segment was one behind the current segment. In the wrapper case, both of the two calls might split their stacks, so the panic-created segment can be two behind the current segment. Fix this by propagating the Stktop.panic flag forward during stack splits instead of looking backward during recover. Fixes issue 5406. R=golang-dev, iant CC=golang-dev https://codereview.appspot.com/13367052
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/5l/noop.c59
-rw-r--r--src/cmd/6l/pass.c32
-rw-r--r--src/cmd/8l/pass.c32
-rw-r--r--src/cmd/gc/go.h1
-rw-r--r--src/cmd/gc/pgen.c13
-rw-r--r--src/cmd/gc/subr.c1
-rw-r--r--src/cmd/ld/textflag.h2
7 files changed, 131 insertions, 9 deletions
diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c
index 7b63aa715..0bd76040d 100644
--- a/src/cmd/5l/noop.c
+++ b/src/cmd/5l/noop.c
@@ -271,6 +271,35 @@ noops(void)
p->to.offset = -autosize;
p->to.reg = REGSP;
p->spadj = autosize;
+
+ if(cursym->text->reg & WRAPPER) {
+ // g->panicwrap += autosize;
+ // MOVW panicwrap_offset(g), R3
+ // ADD $autosize, R3
+ // MOVW R3 panicwrap_offset(g)
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.reg = REGG;
+ p->from.offset = 2*PtrSize;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+
+ p = appendp(p);
+ p->as = AADD;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_REG;
+ p->from.reg = 3;
+ p->to.type = D_OREG;
+ p->to.reg = REGG;
+ p->to.offset = 2*PtrSize;
+ }
break;
case ARET:
@@ -290,6 +319,36 @@ noops(void)
break;
}
}
+
+ if(cursym->text->reg & WRAPPER) {
+ // g->panicwrap -= autosize;
+ // MOVW panicwrap_offset(g), R3
+ // SUB $autosize, R3
+ // MOVW R3 panicwrap_offset(g)
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.reg = REGG;
+ p->from.offset = 2*PtrSize;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+ p = appendp(p);
+
+ p->as = ASUB;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+ p = appendp(p);
+
+ p->as = AMOVW;
+ p->from.type = D_REG;
+ p->from.reg = 3;
+ p->to.type = D_OREG;
+ p->to.reg = REGG;
+ p->to.offset = 2*PtrSize;
+ p = appendp(p);
+ }
+
p->as = AMOVW;
p->scond |= C_PBIT;
p->from.type = D_OREG;
diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c
index d24672432..1be3c18fe 100644
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -511,11 +511,12 @@ dostkoff(void)
diag("nosplit func likely to overflow stack");
q = P;
- if(!(p->from.scale & NOSPLIT)) {
+ if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) {
p = appendp(p);
p = load_g_cx(p); // load g into CX
- p = stacksplit(p, autoffset, &q); // emit split check
}
+ if(!(cursym->text->from.scale & NOSPLIT))
+ p = stacksplit(p, autoffset, &q); // emit split check
if(autoffset) {
p = appendp(p);
@@ -523,8 +524,6 @@ dostkoff(void)
p->from.type = D_CONST;
p->from.offset = autoffset;
p->spadj = autoffset;
- if(q != P)
- q->pcond = p;
} else {
// zero-byte stack adjustment.
// Insert a fake non-zero adjustment so that stkcheck can
@@ -536,7 +535,19 @@ dostkoff(void)
p->as = ANOP;
p->spadj = PtrSize;
}
+ if(q != P)
+ q->pcond = p;
deltasp = autoffset;
+
+ if(cursym->text->from.scale & WRAPPER) {
+ // g->panicwrap += autoffset + PtrSize;
+ p = appendp(p);
+ p->as = AADDL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ }
if(debug['K'] > 1 && autoffset) {
// 6l -KK means double-check for stack overflow
@@ -654,6 +665,19 @@ dostkoff(void)
if(autoffset != deltasp)
diag("unbalanced PUSH/POP");
+
+ if(cursym->text->from.scale & WRAPPER) {
+ p = load_g_cx(p);
+ p = appendp(p);
+ // g->panicwrap -= autoffset + PtrSize;
+ p->as = ASUBL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ p = appendp(p);
+ p->as = ARET;
+ }
if(autoffset) {
p->as = AADJSP;
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index b558ffaa9..1eaf78fe0 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -444,11 +444,12 @@ dostkoff(void)
q = P;
- if(!(p->from.scale & NOSPLIT)) {
+ if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) {
p = appendp(p);
p = load_g_cx(p); // load g into CX
- p = stacksplit(p, autoffset, &q); // emit split check
}
+ if(!(cursym->text->from.scale & NOSPLIT))
+ p = stacksplit(p, autoffset, &q); // emit split check
if(autoffset) {
p = appendp(p);
@@ -456,8 +457,6 @@ dostkoff(void)
p->from.type = D_CONST;
p->from.offset = autoffset;
p->spadj = autoffset;
- if(q != P)
- q->pcond = p;
} else {
// zero-byte stack adjustment.
// Insert a fake non-zero adjustment so that stkcheck can
@@ -469,8 +468,20 @@ dostkoff(void)
p->as = ANOP;
p->spadj = PtrSize;
}
+ if(q != P)
+ q->pcond = p;
deltasp = autoffset;
+ if(cursym->text->from.scale & WRAPPER) {
+ // g->panicwrap += autoffset + PtrSize;
+ p = appendp(p);
+ p->as = AADDL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ }
+
if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) {
// 8l -Z means zero the stack frame on entry.
// This slows down function calls but can help avoid
@@ -540,6 +551,19 @@ dostkoff(void)
if(autoffset != deltasp)
diag("unbalanced PUSH/POP");
+
+ if(cursym->text->from.scale & WRAPPER) {
+ p = load_g_cx(p);
+ p = appendp(p);
+ // g->panicwrap -= autoffset + PtrSize;
+ p->as = ASUBL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ p = appendp(p);
+ p->as = ARET;
+ }
if(autoffset) {
p->as = AADJSP;
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 103aedb41..ba73508b8 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -272,6 +272,7 @@ struct Node
uchar implicit;
uchar addrtaken; // address taken, even if not moved to heap
uchar dupok; // duplicate definitions ok (for func)
+ uchar wrapper; // is method wrapper (for func)
schar likely; // likeliness of if statement
uchar hasbreak; // has break statement
uchar needzero; // if it contains pointers, needs to be zeroed on function entry
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
index d649fc49d..0fbac84cf 100644
--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -95,7 +95,18 @@ compile(Node *fn)
nodconst(&nod1, types[TINT32], 0);
ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
if(fn->dupok)
- ptxt->TEXTFLAG = DUPOK;
+ ptxt->TEXTFLAG |= DUPOK;
+ if(fn->wrapper)
+ ptxt->TEXTFLAG |= WRAPPER;
+
+ // Clumsy but important.
+ // See test/recover.go for test cases and src/pkg/reflect/value.go
+ // for the actual functions being considered.
+ if(myimportpath != nil && strcmp(myimportpath, "reflect") == 0) {
+ if(strcmp(curfn->nname->sym->name, "callReflect") == 0 || strcmp(curfn->nname->sym->name, "callMethod") == 0)
+ ptxt->TEXTFLAG |= WRAPPER;
+ }
+
afunclit(&ptxt->from, curfn->nname);
ginit();
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 54fddbb90..3b3b57631 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2588,6 +2588,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
n->left = newname(methodsym(method->sym, methodrcvr, 0));
fn->nbody = list(fn->nbody, n);
} else {
+ fn->wrapper = 1; // ignore frame for panic+recover matching
call = nod(OCALL, dot, N);
call->list = args;
call->isddd = isddd;
diff --git a/src/cmd/ld/textflag.h b/src/cmd/ld/textflag.h
index 64ae647fb..1d62db736 100644
--- a/src/cmd/ld/textflag.h
+++ b/src/cmd/ld/textflag.h
@@ -17,3 +17,5 @@
#define RODATA 8
// This data contains no pointers.
#define NOPTR 16
+// This is a wrapper function and should not count as disabling 'recover'.
+#define WRAPPER 32