/*
* generate:
* res = &n;
+ * The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
case OIND:
cgen(nl, res);
+ cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer. If the left node
- // was ODOT we have already done the nil check.
- if(nl->op != ODOT)
- if(nl->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], N);
- gmove(res, &n1);
- regalloc(&n2, types[TUINT8], &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gmove(&n1, &n2);
- regfree(&n1);
- regfree(&n2);
- }
if(n->xoffset != 0) {
nodconst(&n1, types[TINT32], n->xoffset);
regalloc(&n2, n1.type, N);
case ODOTPTR:
cgen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(nl->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], N);
- gmove(res, &n1);
- regalloc(&n2, types[TUINT8], &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gmove(&n1, &n2);
- regfree(&n1);
- regfree(&n2);
- }
+ cgen_checknil(res);
if(n->xoffset != 0) {
nodconst(&n1, types[TINT32], n->xoffset);
regalloc(&n2, n1.type, N);
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
+ * The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
{
- Node n1, n2;
+ Node n1;
int r;
if(debug['g']) {
regalloc(a, types[tptr], res);
cgen(n->left, a);
}
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(n->left->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], N);
- gmove(a, &n1);
- regalloc(&n2, types[TUINT8], &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gmove(&n1, &n2);
- regfree(&n1);
- regfree(&n2);
- }
+ cgen_checknil(a);
a->op = OINDREG;
a->xoffset = n->xoffset;
a->type = n->type;
* newreg = &n;
*
* caller must regfree(a).
+ * The generated code checks that the result is not nil.
*/
void
agenr(Node *n, Node *a, Node *res)
case OIND:
cgenr(n->left, a, res);
+ cgen_checknil(a);
break;
case OINDEX:
// i is in &n1 (if not constant)
// w is width
- // explicit check for nil if array is large enough
- // that we might derive too big a pointer.
- if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) {
- regalloc(&n4, types[tptr], N);
- gmove(&n3, &n4);
- regalloc(&tmp, types[TUINT8], &n4);
- n4.op = OINDREG;
- n4.type = types[TUINT8];
- n4.xoffset = 0;
- gmove(&n4, &tmp);
- regfree(&n4);
- regfree(&tmp);
- }
-
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
nodo.xoffset -= widthptr;
cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab
+ cgen_checknil(&nodr); // in case offset is huge
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
regfree(&dst);
regfree(&nz);
}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+void
+expandchecks(Prog *firstp)
+{
+ int reg;
+ Prog *p, *p1;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != ACHECKNIL)
+ continue;
+ if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers
+ warnl(p->lineno, "nil check %D", &p->from);
+ if(p->from.type != D_REG)
+ fatal("invalid nil check %P", p);
+ reg = p->from.reg;
+ // check is
+ // CMP arg, $0
+ // MOV.EQ arg, 0(arg)
+ p1 = mal(sizeof *p1);
+ clearp(p1);
+ p1->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+ p1->loc = 9999;
+ p1->as = AMOVW;
+ p1->from.type = D_REG;
+ p1->from.reg = reg;
+ p1->to.type = D_OREG;
+ p1->to.reg = reg;
+ p1->to.offset = 0;
+ p1->scond = C_SCOND_EQ;
+ p->as = ACMP;
+ p->from.type = D_CONST;
+ p->from.reg = NREG;
+ p->from.offset = 0;
+ p->reg = reg;
+ }
+}
return p;
}
-// Generate an instruction referencing *n
-// to force segv on nil pointer dereference.
-void
-checkref(Node *n, int force)
-{
- Node m1, m2;
-
- if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero)
- return;
-
- regalloc(&m1, types[TUINTPTR], n);
- regalloc(&m2, types[TUINT8], n);
- cgen(n, &m1);
- m1.xoffset = 0;
- m1.op = OINDREG;
- m1.type = types[TUINT8];
- gins(AMOVB, &m1, &m2);
- regfree(&m2);
- regfree(&m1);
-}
-
-static void
-checkoffset(Addr *a, int canemitcode)
-{
- Prog *p;
- Node n1;
-
- if(a->offset < unmappedzero)
- return;
- if(!canemitcode)
- fatal("checkoffset %#x, cannot emit code", a->offset);
-
- // cannot rely on unmapped nil page at 0 to catch
- // reference with large offset. instead, emit explicit
- // test of 0(reg).
- regalloc(&n1, types[TUINTPTR], N);
- p = gins(AMOVB, N, &n1);
- p->from = *a;
- p->from.offset = 0;
- regfree(&n1);
-}
-
/*
* generate code to compute n;
* make a refer to result.
a->reg = n->val.u.reg;
a->sym = n->sym;
a->offset = n->xoffset;
- checkoffset(a, canemitcode);
break;
case OPARAM:
a->etype = TINT32;
if(a->type == D_CONST && a->offset == 0)
break; // len(nil)
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OLEN:
if(a->type == D_CONST && a->offset == 0)
break; // len(nil)
a->offset += Array_nel;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OCAP:
if(a->type == D_CONST && a->offset == 0)
break; // cap(nil)
a->offset += Array_cap;
- if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OADDR:
n1.xoffset = oary[0];
} else {
cgen(nn, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[0]+1);
}
if(oary[i] >= 0)
fatal("can't happen");
gins(AMOVW, &n1, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[i]+1);
}
// load the array (reg)
if(l->ullman > r->ullman) {
regalloc(reg, types[tptr], N);
- if(o & OPtrto)
+ if(o & OPtrto) {
cgen(l, reg);
- else
+ cgen_checknil(reg);
+ } else
agen(l, reg);
}
// load the array (reg)
if(l->ullman <= r->ullman) {
regalloc(reg, types[tptr], N);
- if(o & OPtrto)
+ if(o & OPtrto) {
cgen(l, reg);
- else
+ cgen_checknil(reg);
+ } else
agen(l, reg);
}
n2.type = types[tptr];
n2.xoffset = Array_nel;
} else {
- if(l->type->width >= unmappedzero && l->op == OIND) {
- // cannot rely on page protections to
- // catch array ptr == 0, so dereference.
- n2 = *reg;
- n2.op = OINDREG;
- n2.type = types[TUINTPTR];
- n2.xoffset = 0;
- regalloc(&n3, n2.type, N);
- gins(AMOVW, &n2, &n3);
- regfree(&n3);
- }
- nodconst(&n2, types[TUINT32], l->type->bound);
if(o & OPtrto)
nodconst(&n2, types[TUINT32], l->type->type->bound);
+ else
+ nodconst(&n2, types[TUINT32], l->type->bound);
}
regalloc(&n3, n2.type, N);
cgen(&n2, &n3);
// can multiply by width statically
regalloc(reg, types[tptr], N);
- if(o & OPtrto)
+ if(o & OPtrto) {
cgen(l, reg);
- else
+ cgen_checknil(reg);
+ } else
agen(l, reg);
v = mpgetfix(r->val.u.xval);
if(o & ODynam) {
-
if(!debug['B'] && !n->bounded) {
n1 = *reg;
n1.op = OINDREG;
static int xtramodes(Graph*, Flow*, Adr*);
static int shortprop(Flow *r);
-static int regtyp(Adr*);
static int subprop(Flow*);
static int copyprop(Graph*, Flow*);
static int copy1(Adr*, Adr*, Flow*, int);
flowend(g);
}
-static int
+int
regtyp(Adr *a)
{
case ADIVF:
case ADIVD:
+ case ACHECKNIL: /* read */
case ACMPF: /* read, read, */
case ACMPD:
case ACMP:
[APCDATA]= {Pseudo},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
+ [ACHECKNIL]= {LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
ATYPE,
AFUNCDATA,
APCDATA,
+ ACHECKNIL,
ALAST,
};
break;
case OADDR:
+ if(n->bounded) // let race detector avoid nil checks
+ disable_checknil++;
agen(nl, res);
+ if(n->bounded)
+ disable_checknil--;
break;
case OCALLMETH:
}
/*
- * allocate a register in res and generate
- * newreg = &n
+ * allocate a register (reusing res if possible) and generate
+ * a = n
* The caller must call regfree(a).
*/
void
}
/*
- * allocate a register in res and generate
- * res = &n
+ * allocate a register (reusing res if possible) and generate
+ * a = &n
+ * The caller must call regfree(a).
+ * The generated code checks that the result is not nil.
*/
void
agenr(Node *n, Node *a, Node *res)
{
Node *nl, *nr;
- Node n1, n2, n3, n4, n5, tmp, tmp2, nlen;
+ Node n1, n2, n3, n5, tmp, tmp2, nlen;
Prog *p1;
Type *t;
uint64 w;
case OIND:
cgenr(n->left, a, res);
+ cgen_checknil(a);
break;
case OINDEX:
// len(a) is in nlen (if needed)
// w is width
- // explicit check for nil if array is large enough
- // that we might derive too big a pointer.
- if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) {
- regalloc(&n4, types[tptr], &n3);
- gmove(&n3, &n4);
- n4.op = OINDREG;
- n4.type = types[TUINT8];
- n4.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n4);
- regfree(&n4);
- }
-
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
/*
* generate:
* res = &n;
+ * The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
case OIND:
cgen(nl, res);
+ cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer. If the left node
- // was ODOT we have already done the nil check.
- if(nl->op != ODOT)
- if(nl->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
if(n->xoffset != 0)
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
break;
case ODOTPTR:
cgen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(nl->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
- if(n->xoffset != 0) {
+ cgen_checknil(res);
+ if(n->xoffset != 0)
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
- }
break;
}
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
+ * The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
case ODOTPTR:
cgenr(n->left, a, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(n->left->type->type->width >= unmappedzero) {
- n1 = *a;
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- }
+ cgen_checknil(a);
a->op = OINDREG;
a->xoffset += n->xoffset;
a->type = n->type;
igen(n->left, a, res);
else {
igen(n->left, &n1, res);
+ cgen_checknil(&n1);
regalloc(a, types[tptr], res);
gmove(&n1, a);
regfree(&n1);
regalloc(&nodr, types[tptr], &nodo);
if(n->left->xoffset == BADWIDTH)
fatal("cgen_callinter: badwidth");
+ cgen_checknil(&nodo); // in case offset is huge
nodo.op = OINDREG;
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
if(proc == 0) {
restx(&n1, &oldn1);
restx(&ax, &oldax);
}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+void
+expandchecks(Prog *firstp)
+{
+ Prog *p, *p1, *p2;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != ACHECKNIL)
+ continue;
+ if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers
+ warnl(p->lineno, "nil check %D", &p->from);
+ // check is
+ // CMP arg, $0
+ // JNE 2(PC) (likely)
+ // MOV AX, 0
+ p1 = mal(sizeof *p1);
+ p2 = mal(sizeof *p2);
+ clearp(p1);
+ clearp(p2);
+ p1->link = p2;
+ p2->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+ p2->lineno = p->lineno;
+ p1->loc = 9999;
+ p2->loc = 9999;
+ p->as = ACMPQ;
+ p->to.type = D_CONST;
+ p->to.offset = 0;
+ p1->as = AJNE;
+ p1->from.type = D_CONST;
+ p1->from.offset = 1; // likely
+ p1->to.type = D_BRANCH;
+ p1->to.u.branch = p2->link;
+ // crash by write to memory address 0.
+ // if possible, since we know arg is 0, use 0(arg),
+ // which will be shorter to encode than plain 0.
+ p2->as = AMOVL;
+ p2->from.type = D_AX;
+ if(regtyp(&p->from))
+ p2->to.type = p->from.type + D_INDIR;
+ else
+ p2->to.type = D_INDIR+D_NONE;
+ p2->to.offset = 0;
+ }
+}
return p;
}
-// Generate an instruction referencing *n
-// to force segv on nil pointer dereference.
-void
-checkref(Node *n, int force)
-{
- Node m;
-
- if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero)
- return;
-
- regalloc(&m, types[TUINTPTR], n);
- cgen(n, &m);
- m.xoffset = 0;
- m.op = OINDREG;
- m.type = types[TUINT8];
- gins(ATESTB, nodintconst(0), &m);
- regfree(&m);
-}
-
-static void
-checkoffset(Addr *a, int canemitcode)
-{
- Prog *p;
-
- if(a->offset < unmappedzero)
- return;
- if(!canemitcode)
- fatal("checkoffset %#llx, cannot emit code", a->offset);
-
- // cannot rely on unmapped nil page at 0 to catch
- // reference with large offset. instead, emit explicit
- // test of 0(reg).
- p = gins(ATESTB, nodintconst(0), N);
- p->to = *a;
- p->to.offset = 0;
-}
-
/*
* generate code to compute n;
* make a refer to result.
a->offset = n->xoffset;
if(a->offset != (int32)a->offset)
yyerror("offset %lld too large for OINDREG", a->offset);
- checkoffset(a, canemitcode);
break;
case OPARAM:
break; // itab(nil)
a->etype = tptr;
a->width = widthptr;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OLEN:
a->etype = simtype[TUINT];
a->offset += Array_nel;
a->width = widthint;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OCAP:
a->etype = simtype[TUINT];
a->offset += Array_cap;
a->width = widthint;
- if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
- checkoffset(a, canemitcode);
break;
// case OADD:
n1.xoffset = oary[0];
} else {
cgen(nn, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[0]+1);
}
if(oary[i] >= 0)
fatal("can't happen");
gins(AMOVQ, &n1, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[i]+1);
}
o |= OAddable;
}
- if(!(o & ODynam) && l->type->width >= unmappedzero && l->op == OIND) {
- // cannot rely on page protections to
- // catch array ptr == 0, so dereference.
- n2 = *reg;
- n2.xoffset = 0;
- n2.op = OINDREG;
- n2.type = types[TUINT8];
- gins(ATESTB, nodintconst(0), &n2);
- }
-
// check bounds
if(!debug['B'] && !n->bounded) {
// check bounds
static int prevl(Flow *r, int reg);
static void pushback(Flow *r);
static int regconsttyp(Adr*);
-static int regtyp(Adr*);
static int subprop(Flow*);
static int copyprop(Graph*, Flow*);
static int copy1(Adr*, Adr*, Flow*, int);
ostats.ndelmov++;
}
-static int
+int
regtyp(Adr *a)
{
int t;
[APCDATA]= {Pseudo},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
+ [ACHECKNIL]= {LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
ATYPE,
AFUNCDATA,
APCDATA,
+ ACHECKNIL,
ALAST
};
/*
* address gen
* res = &n;
+ * The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
{
Node *nl, *nr;
- Node n1, n2, n3, n4, tmp, nlen;
+ Node n1, n2, n3, tmp, nlen;
Type *t;
uint32 w;
uint64 v;
// len(a) is in nlen (if needed)
// w is width
- // explicit check for nil if array is large enough
- // that we might derive too big a pointer.
- if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) {
- n4 = n3;
- n4.op = OINDREG;
- n4.type = types[TUINT8];
- n4.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n4);
- }
-
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
case OIND:
cgen(nl, res);
+ cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer. If the left node
- // was ODOT we have already done the nil check.
- if(nl->op != ODOT)
- if(nl->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
if(n->xoffset != 0) {
nodconst(&n1, types[tptr], n->xoffset);
gins(optoas(OADD, types[tptr]), &n1, res);
if(!isptr[t->etype])
fatal("agen: not ptr %N", n);
cgen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(nl->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
+ cgen_checknil(res);
if(n->xoffset != 0) {
nodconst(&n1, types[tptr], n->xoffset);
gins(optoas(OADD, types[tptr]), &n1, res);
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
+ * The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
regalloc(a, types[tptr], res);
cgen(n->left, a);
}
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(n->left->type->type->width >= unmappedzero) {
- n1 = *a;
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- }
+ cgen_checknil(a);
a->op = OINDREG;
a->xoffset += n->xoffset;
a->type = n->type;
regalloc(&nodr, types[tptr], &nodo);
if(n->left->xoffset == BADWIDTH)
fatal("cgen_callinter: badwidth");
+ cgen_checknil(&nodo);
nodo.op = OINDREG;
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
patch(gbranch(optoas(a, nr->type), T, likely), to);
}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+void
+expandchecks(Prog *firstp)
+{
+ Prog *p, *p1, *p2;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != ACHECKNIL)
+ continue;
+ if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers
+ warnl(p->lineno, "nil check %D", &p->from);
+ // check is
+ // CMP arg, $0
+ // JNE 2(PC) (likely)
+ // MOV AX, 0
+ p1 = mal(sizeof *p1);
+ p2 = mal(sizeof *p2);
+ clearp(p1);
+ clearp(p2);
+ p1->link = p2;
+ p2->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+ p2->lineno = p->lineno;
+ p1->loc = 9999;
+ p2->loc = 9999;
+ p->as = ACMPL;
+ p->to.type = D_CONST;
+ p->to.offset = 0;
+ p1->as = AJNE;
+ p1->from.type = D_CONST;
+ p1->from.offset = 1; // likely
+ p1->to.type = D_BRANCH;
+ p1->to.u.branch = p2->link;
+ // crash by write to memory address 0.
+ // if possible, since we know arg is 0, use 0(arg),
+ // which will be shorter to encode than plain 0.
+ p2->as = AMOVL;
+ p2->from.type = D_AX;
+ if(regtyp(&p->from))
+ p2->to.type = p->from.type + D_INDIR;
+ else
+ p2->to.type = D_INDIR+D_NONE;
+ p2->to.offset = 0;
+ }
+}
return p;
}
-// Generate an instruction referencing *n
-// to force segv on nil pointer dereference.
-void
-checkref(Node *n, int force)
-{
- Node m;
-
- if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero)
- return;
-
- regalloc(&m, types[TUINTPTR], n);
- cgen(n, &m);
- m.xoffset = 0;
- m.op = OINDREG;
- m.type = types[TUINT8];
- gins(ATESTB, nodintconst(0), &m);
- regfree(&m);
-}
-
-static void
-checkoffset(Addr *a, int canemitcode)
-{
- Prog *p;
-
- if(a->offset < unmappedzero)
- return;
- if(!canemitcode)
- fatal("checkoffset %#x, cannot emit code", a->offset);
-
- // cannot rely on unmapped nil page at 0 to catch
- // reference with large offset. instead, emit explicit
- // test of 0(reg).
- p = gins(ATESTB, nodintconst(0), N);
- p->to = *a;
- p->to.offset = 0;
-}
-
/*
* generate code to compute n;
* make a refer to result.
break; // len(nil)
a->etype = tptr;
a->width = widthptr;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OLEN:
a->etype = TUINT32;
a->offset += Array_nel;
a->width = 4;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OCAP:
a->etype = TUINT32;
a->offset += Array_cap;
a->width = 4;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
// case OADD:
static void conprop(Flow *r);
static void elimshortmov(Graph*);
-static int regtyp(Adr*);
static int subprop(Flow*);
static int copyprop(Graph*, Flow*);
static int copy1(Adr*, Adr*, Flow*, int);
ostats.ndelmov++;
}
-static int
+int
regtyp(Adr *a)
{
int t;
[APCDATA]= {Pseudo},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
+ [ACHECKNIL]= {LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
ATYPE,
AFUNCDATA,
APCDATA,
+ ACHECKNIL,
ALAST
};
// Like walkclosure above.
if(isinter(n->left->type)) {
+ // Trigger panic for method on nil interface now.
+ // Otherwise it happens in the wrapper and is confusing.
n->left = cheapexpr(n->left, init);
- checknotnil(n->left, init);
+ checknil(n->left, init);
}
typ = nod(OTSTRUCT, N, N);
cgen_ret(n);
break;
- case OCHECKNOTNIL:
- checkref(n->left, 1);
+ case OCHECKNIL:
+ cgen_checknil(n->left);
}
ret:
* n->left is s
* n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
* caller (cgen) guarantees res is an addable ONAME.
+ *
+ * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
*/
void
cgen_slice(Node *n, Node *res)
dst.xoffset += Array_array;
dst.type = types[TUINTPTR];
- if(n->op == OSLICEARR) {
- if(!isptr[n->left->type->etype])
- fatal("slicearr is supposed to work on pointer: %+N\n", n);
- checkref(n->left, 0);
- }
-
if(isnil(n->left)) {
tempname(&src, n->left->type);
cgen(n->left, &src);
} else
src = *n->left;
- src.xoffset += Array_array;
+ if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR)
+ src.xoffset += Array_array;
src.type = types[TUINTPTR];
- if(offs == N) {
+ if(n->op == OSLICEARR || n->op == OSLICE3ARR) {
+ if(!isptr[n->left->type->etype])
+ fatal("slicearr is supposed to work on pointer: %+N\n", n);
+ cgen(&src, &dst);
+ cgen_checknil(&dst);
+ if(offs != N) {
+ add = nod(OADD, &dst, offs);
+ typecheck(&add, Erv);
+ cgen(add, &dst);
+ }
+ } else if(offs == N) {
cgen(&src, &dst);
} else {
add = nod(OADD, &src, offs);
OITAB, // itable word of an interface value.
OCLOSUREVAR, // variable reference at beginning of closure function
OCFUNC, // reference to c function pointer (not go func value)
- OCHECKNOTNIL, // emit code to ensure pointer/interface not nil
+ OCHECKNIL, // emit code to ensure pointer/interface not nil
// arch-specific registers
OREGISTER, // a register, such as AX.
EXTERN char lexbuf[NSYMB];
EXTERN char litbuf[NSYMB];
EXTERN int debug[256];
+EXTERN char* debugstr;
+EXTERN int debug_checknil;
EXTERN Sym* hash[NHASH];
EXTERN Sym* importmyname; // my name for package
EXTERN Pkg* localpkg; // package being compiled
EXTERN Prog* retpc;
EXTERN Node* nodfp;
+EXTERN int disable_checknil;
int anyregalloc(void);
void betypeinit(void);
void bgen(Node *n, int true, int likely, Prog *to);
-void checkref(Node *n, int force);
-void checknotnil(Node*, NodeList**);
+void checknil(Node*, NodeList**);
+void expandchecks(Prog*);
void cgen(Node*, Node*);
void cgen_asop(Node *n);
void cgen_call(Node *n, int proc);
void cgen_callinter(Node *n, Node *res, int proc);
+void cgen_checknil(Node*);
void cgen_ret(Node *n);
void clearfat(Node *n);
void compile(Node*);
{nil, nil},
};
+// Debug arguments.
+// These can be specified with the -d flag, as in "-d checknil"
+// to set the debug_checknil variable. In general the list passed
+// to -d can be comma-separated.
+static struct {
+ char *name;
+ int *val;
+} debugtab[] = {
+ {"nil", &debug_checknil},
+ {nil, nil},
+};
+
static void
addexp(char *s)
{
flagfn0("V", "print compiler version", doversion);
flagcount("W", "debug parse tree after type checking", &debug['W']);
flagcount("complete", "compiling complete package (no C or assembly)", &pure_go);
- flagcount("d", "debug declarations", &debug['d']);
+ flagstr("d", "list: print debug information about items in list", &debugstr);
flagcount("e", "no limit on number of errors reported", &debug['e']);
flagcount("f", "debug stack frames", &debug['f']);
flagcount("g", "debug code generation", &debug['g']);
racepkg = mkpkg(strlit("runtime/race"));
racepkg->name = "race";
}
+
+ // parse -d argument
+ if(debugstr) {
+ char *f[100];
+ int i, j, nf;
+
+ nf = getfields(debugstr, f, nelem(f), 1, ",");
+ for(i=0; i<nf; i++) {
+ for(j=0; debugtab[j].name != nil; j++) {
+ if(strcmp(debugtab[j].name, f[i]) == 0) {
+ *debugtab[j].val = 1;
+ break;
+ }
+ }
+ if(j == nelem(debugtab))
+ fatal("unknown debug information -d '%s'\n", f[i]);
+ }
+ }
// enable inlining. for now:
// default: inlining on. (debug['l'] == 1)
if(!debug['N'] || debug['R'] || debug['P']) {
regopt(ptxt);
}
+ expandchecks(ptxt);
oldstksize = stksize;
allocauto(ptxt);
addrescapes(n);
}
}
+
+void
+cgen_checknil(Node *n)
+{
+ Node reg;
+
+ if(disable_checknil)
+ return;
+ while(n->op == ODOT || (n->op == OINDEX && isfixedarray(n->left->type->type))) // NOTE: not ODOTPTR
+ n = n->left;
+ if(thechar == '5' && n->op != OREGISTER) {
+ regalloc(®, types[tptr], N);
+ cgen(n, ®);
+ gins(ACHECKNIL, ®, N);
+ regfree(®);
+ return;
+ }
+ gins(ACHECKNIL, n, N);
+}
void flowend(Graph*);
void mergetemp(Prog*);
int noreturn(Prog*);
+int regtyp(Addr*);
Flow* uniqp(Flow*);
Flow* uniqs(Flow*);
// does not require instrumentation
case OPRINT: // don't bother instrumenting it
case OPRINTN: // don't bother instrumenting it
- case OCHECKNOTNIL: // always followed by a read.
+ case OCHECKNIL: // always followed by a read.
case OPARAM: // it appears only in fn->exit to copy heap params back
case OCLOSUREVAR:// immutable pointer to captured variable
case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT)
Node *r;
r = nod(OADDR, n, N);
+ r->bounded = 1;
r = conv(r, types[TUNSAFEPTR]);
r = conv(r, types[TUINTPTR]);
return r;
}
void
-checknotnil(Node *x, NodeList **init)
+checknil(Node *x, NodeList **init)
{
Node *n;
x = nod(OITAB, x, N);
typecheck(&x, Erv);
}
- n = nod(OCHECKNOTNIL, x, N);
+ n = nod(OCHECKNIL, x, N);
n->typecheck = 1;
*init = list(*init, n);
}
case OLABEL:
case ODCLCONST:
case ODCLTYPE:
- case OCHECKNOTNIL:
+ case OCHECKNIL:
break;
case OBLOCK:
case OIND:
if(n->left->type->type->width == 0) {
+ // No actual copy will be generated, so emit an explicit nil check.
n->left = cheapexpr(n->left, init);
- checknotnil(n->left, init);
+ checknil(n->left, init);
}
walkexpr(&n->left, init);
goto ret;
case ODOTPTR:
usefield(n);
if(n->op == ODOTPTR && n->left->type->type->width == 0) {
+ // No actual copy will be generated, so emit an explicit nil check.
n->left = cheapexpr(n->left, init);
- checknotnil(n->left, init);
+ checknil(n->left, init);
}
walkexpr(&n->left, init);
goto ret;
--- /dev/null
+// errorcheck -0 -N -d=nil
+
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that nil checks are inserted.
+// Optimization is disabled, so redundant checks are not removed.
+
+package p
+
+type Struct struct {
+ X int
+ Y float64
+}
+
+type BigStruct struct {
+ X int
+ Y float64
+ A [1<<20]int
+ Z string
+}
+
+type Empty struct {
+}
+
+type Empty1 struct {
+ Empty
+}
+
+var (
+ intp *int
+ arrayp *[10]int
+ array0p *[0]int
+ bigarrayp *[1<<26]int
+ structp *Struct
+ bigstructp *BigStruct
+ emptyp *Empty
+ empty1p *Empty1
+)
+
+func f1() {
+ _ = *intp // ERROR "nil check"
+ _ = *arrayp // ERROR "nil check"
+ _ = *array0p // ERROR "nil check"
+ _ = *array0p // ERROR "nil check"
+ _ = *intp // ERROR "nil check"
+ _ = *arrayp // ERROR "nil check"
+ _ = *structp // ERROR "nil check"
+ _ = *emptyp // ERROR "nil check"
+ _ = *arrayp // ERROR "nil check"
+}
+
+func f2() {
+ var (
+ intp *int
+ arrayp *[10]int
+ array0p *[0]int
+ bigarrayp *[1<<20]int
+ structp *Struct
+ bigstructp *BigStruct
+ emptyp *Empty
+ empty1p *Empty1
+ )
+
+ _ = *intp // ERROR "nil check"
+ _ = *arrayp // ERROR "nil check"
+ _ = *array0p // ERROR "nil check"
+ _ = *array0p // ERROR "nil check"
+ _ = *intp // ERROR "nil check"
+ _ = *arrayp // ERROR "nil check"
+ _ = *structp // ERROR "nil check"
+ _ = *emptyp // ERROR "nil check"
+ _ = *arrayp // ERROR "nil check"
+ _ = *bigarrayp // ERROR "nil check"
+ _ = *bigstructp // ERROR "nil check"
+ _ = *empty1p // ERROR "nil check"
+}
+
+func fx10k() *[10000]int
+var b bool
+
+
+func f3(x *[10000]int) {
+ // Using a huge type and huge offsets so the compiler
+ // does not expect the memory hardware to fault.
+ _ = x[9999] // ERROR "nil check"
+
+ for {
+ if x[9999] != 0 { // ERROR "nil check"
+ break
+ }
+ }
+
+ x = fx10k()
+ _ = x[9999] // ERROR "nil check"
+ if b {
+ _ = x[9999] // ERROR "nil check"
+ } else {
+ _ = x[9999] // ERROR "nil check"
+ }
+ _ = x[9999] // ERROR "nil check"
+
+ x = fx10k()
+ if b {
+ _ = x[9999] // ERROR "nil check"
+ } else {
+ _ = x[9999] // ERROR "nil check"
+ }
+ _ = x[9999] // ERROR "nil check"
+
+ fx10k()
+ // This one is a bit redundant, if we figured out that
+ // x wasn't going to change across the function call.
+ // But it's a little complex to do and in practice doesn't
+ // matter enough.
+ _ = x[9999] // ERROR "nil check"
+}
+
+func f3a() {
+ x := fx10k()
+ y := fx10k()
+ z := fx10k()
+ _ = &x[9] // ERROR "nil check"
+ y = z
+ _ = &x[9] // ERROR "nil check"
+ x = y
+ _ = &x[9] // ERROR "nil check"
+}
+
+func f3b() {
+ x := fx10k()
+ y := fx10k()
+ _ = &x[9] // ERROR "nil check"
+ y = x
+ _ = &x[9] // ERROR "nil check"
+ x = y
+ _ = &x[9] // ERROR "nil check"
+}
+
+func fx10() *[10]int
+
+func f4(x *[10]int) {
+ // Most of these have no checks because a real memory reference follows,
+ // and the offset is small enough that if x is nil, the address will still be
+ // in the first unmapped page of memory.
+
+ _ = x[9] // ERROR "nil check"
+
+ for {
+ if x[9] != 0 { // ERROR "nil check"
+ break
+ }
+ }
+
+ x = fx10()
+ _ = x[9] // ERROR "nil check"
+ if b {
+ _ = x[9] // ERROR "nil check"
+ } else {
+ _ = x[9] // ERROR "nil check"
+ }
+ _ = x[9] // ERROR "nil check"
+
+ x = fx10()
+ if b {
+ _ = x[9] // ERROR "nil check"
+ } else {
+ _ = &x[9] // ERROR "nil check"
+ }
+ _ = x[9] // ERROR "nil check"
+
+ fx10()
+ _ = x[9] // ERROR "nil check"
+
+ x = fx10()
+ y := fx10()
+ _ = &x[9] // ERROR "nil check"
+ y = x
+ _ = &x[9] // ERROR "nil check"
+ x = y
+ _ = &x[9] // ERROR "nil check"
+}
+
--- /dev/null
+// run
+
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {
+ ok := true
+ for _, tt := range tests {
+ func() {
+ defer func() {
+ if err := recover(); err == nil {
+ println(tt.name, "did not panic")
+ ok = false
+ }
+ }()
+ tt.fn()
+ }()
+ }
+ if !ok {
+ println("BUG")
+ }
+}
+
+var intp *int
+var slicep *[]byte
+var a10p *[10]int
+var a10Mp *[1<<20]int
+var structp *Struct
+var bigstructp *BigStruct
+var i int
+var m *M
+var m1 *M1
+var m2 *M2
+
+func use(interface{}) {
+}
+
+var tests = []struct{
+ name string
+ fn func()
+}{
+ // Edit .+1,/^}/s/^[^ ].+/ {"&", func() { println(&) }},\n {"\&&", func() { println(\&&) }},/g
+ {"*intp", func() { println(*intp) }},
+ {"&*intp", func() { println(&*intp) }},
+ {"*slicep", func() { println(*slicep) }},
+ {"&*slicep", func() { println(&*slicep) }},
+ {"(*slicep)[0]", func() { println((*slicep)[0]) }},
+ {"&(*slicep)[0]", func() { println(&(*slicep)[0]) }},
+ {"(*slicep)[i]", func() { println((*slicep)[i]) }},
+ {"&(*slicep)[i]", func() { println(&(*slicep)[i]) }},
+ {"*a10p", func() { use(*a10p) }},
+ {"&*a10p", func() { println(&*a10p) }},
+ {"a10p[0]", func() { println(a10p[0]) }},
+ {"&a10p[0]", func() { println(&a10p[0]) }},
+ {"a10p[i]", func() { println(a10p[i]) }},
+ {"&a10p[i]", func() { println(&a10p[i]) }},
+ {"*structp", func() { use(*structp) }},
+ {"&*structp", func() { println(&*structp) }},
+ {"structp.i", func() { println(structp.i) }},
+ {"&structp.i", func() { println(&structp.i) }},
+ {"structp.j", func() { println(structp.j) }},
+ {"&structp.j", func() { println(&structp.j) }},
+ {"structp.k", func() { println(structp.k) }},
+ {"&structp.k", func() { println(&structp.k) }},
+ {"structp.x[0]", func() { println(structp.x[0]) }},
+ {"&structp.x[0]", func() { println(&structp.x[0]) }},
+ {"structp.x[i]", func() { println(structp.x[i]) }},
+ {"&structp.x[i]", func() { println(&structp.x[i]) }},
+ {"structp.x[9]", func() { println(structp.x[9]) }},
+ {"&structp.x[9]", func() { println(&structp.x[9]) }},
+ {"structp.l", func() { println(structp.l) }},
+ {"&structp.l", func() { println(&structp.l) }},
+ {"*bigstructp", func() { use(*bigstructp) }},
+ {"&*bigstructp", func() { println(&*bigstructp) }},
+ {"bigstructp.i", func() { println(bigstructp.i) }},
+ {"&bigstructp.i", func() { println(&bigstructp.i) }},
+ {"bigstructp.j", func() { println(bigstructp.j) }},
+ {"&bigstructp.j", func() { println(&bigstructp.j) }},
+ {"bigstructp.k", func() { println(bigstructp.k) }},
+ {"&bigstructp.k", func() { println(&bigstructp.k) }},
+ {"bigstructp.x[0]", func() { println(bigstructp.x[0]) }},
+ {"&bigstructp.x[0]", func() { println(&bigstructp.x[0]) }},
+ {"bigstructp.x[i]", func() { println(bigstructp.x[i]) }},
+ {"&bigstructp.x[i]", func() { println(&bigstructp.x[i]) }},
+ {"bigstructp.x[9]", func() { println(bigstructp.x[9]) }},
+ {"&bigstructp.x[9]", func() { println(&bigstructp.x[9]) }},
+ {"bigstructp.x[200<<20]", func() { println(bigstructp.x[200<<20]) }},
+ {"&bigstructp.x[200<<20]", func() { println(&bigstructp.x[200<<20]) }},
+ {"bigstructp.l", func() { println(bigstructp.l) }},
+ {"&bigstructp.l", func() { println(&bigstructp.l) }},
+ {"m1.F()", func() { println(m1.F()) }},
+ {"m1.M.F()", func() { println(m1.M.F()) }},
+ {"m2.F()", func() { println(m2.F()) }},
+ {"m2.M.F()", func() { println(m2.M.F()) }},
+}
+
+type Struct struct {
+ i int
+ j float64
+ k string
+ x [10]int
+ l []byte
+}
+
+type BigStruct struct {
+ i int
+ j float64
+ k string
+ x [256<<20]byte
+ l []byte
+}
+
+type M struct {
+}
+
+func (m *M) F() int {return 0}
+
+type M1 struct {
+ M
+}
+
+type M2 struct {
+ x int
+ M
+}