mirror of
https://github.com/golang/go
synced 2024-11-05 18:36:08 +00:00
cmd/gc: &x panics if x does
See golang.org/s/go12nil. This CL is about getting all the right checks inserted. A followup CL will add an optimization pass to remove redundant checks. R=ken2 CC=golang-dev https://golang.org/cl/12970043
This commit is contained in:
parent
5ce78b7cd2
commit
999a36f9af
29 changed files with 597 additions and 344 deletions
|
@ -562,6 +562,7 @@ cgenindex(Node *n, Node *res, int bounded)
|
||||||
/*
|
/*
|
||||||
* generate:
|
* generate:
|
||||||
* res = &n;
|
* res = &n;
|
||||||
|
* The generated code checks that the result is not nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
agen(Node *n, Node *res)
|
agen(Node *n, Node *res)
|
||||||
|
@ -689,25 +690,11 @@ agen(Node *n, Node *res)
|
||||||
|
|
||||||
case OIND:
|
case OIND:
|
||||||
cgen(nl, res);
|
cgen(nl, res);
|
||||||
|
cgen_checknil(res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ODOT:
|
case ODOT:
|
||||||
agen(nl, res);
|
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) {
|
if(n->xoffset != 0) {
|
||||||
nodconst(&n1, types[TINT32], n->xoffset);
|
nodconst(&n1, types[TINT32], n->xoffset);
|
||||||
regalloc(&n2, n1.type, N);
|
regalloc(&n2, n1.type, N);
|
||||||
|
@ -723,19 +710,7 @@ agen(Node *n, Node *res)
|
||||||
|
|
||||||
case ODOTPTR:
|
case ODOTPTR:
|
||||||
cgen(nl, res);
|
cgen(nl, res);
|
||||||
// explicit check for nil if struct is large enough
|
cgen_checknil(res);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
if(n->xoffset != 0) {
|
if(n->xoffset != 0) {
|
||||||
nodconst(&n1, types[TINT32], n->xoffset);
|
nodconst(&n1, types[TINT32], n->xoffset);
|
||||||
regalloc(&n2, n1.type, N);
|
regalloc(&n2, n1.type, N);
|
||||||
|
@ -761,11 +736,12 @@ ret:
|
||||||
*
|
*
|
||||||
* on exit, a has been changed to be *newreg.
|
* on exit, a has been changed to be *newreg.
|
||||||
* caller must regfree(a).
|
* caller must regfree(a).
|
||||||
|
* The generated code checks that the result is not *nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
igen(Node *n, Node *a, Node *res)
|
igen(Node *n, Node *a, Node *res)
|
||||||
{
|
{
|
||||||
Node n1, n2;
|
Node n1;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if(debug['g']) {
|
if(debug['g']) {
|
||||||
|
@ -806,19 +782,7 @@ igen(Node *n, Node *a, Node *res)
|
||||||
regalloc(a, types[tptr], res);
|
regalloc(a, types[tptr], res);
|
||||||
cgen(n->left, a);
|
cgen(n->left, a);
|
||||||
}
|
}
|
||||||
// explicit check for nil if struct is large enough
|
cgen_checknil(a);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
a->op = OINDREG;
|
a->op = OINDREG;
|
||||||
a->xoffset = n->xoffset;
|
a->xoffset = n->xoffset;
|
||||||
a->type = n->type;
|
a->type = n->type;
|
||||||
|
@ -908,6 +872,7 @@ cgenr(Node *n, Node *a, Node *res)
|
||||||
* newreg = &n;
|
* newreg = &n;
|
||||||
*
|
*
|
||||||
* caller must regfree(a).
|
* caller must regfree(a).
|
||||||
|
* The generated code checks that the result is not nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
agenr(Node *n, Node *a, Node *res)
|
agenr(Node *n, Node *a, Node *res)
|
||||||
|
@ -939,6 +904,7 @@ agenr(Node *n, Node *a, Node *res)
|
||||||
|
|
||||||
case OIND:
|
case OIND:
|
||||||
cgenr(n->left, a, res);
|
cgenr(n->left, a, res);
|
||||||
|
cgen_checknil(a);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OINDEX:
|
case OINDEX:
|
||||||
|
@ -980,20 +946,6 @@ agenr(Node *n, Node *a, Node *res)
|
||||||
// i is in &n1 (if not constant)
|
// i is in &n1 (if not constant)
|
||||||
// w is width
|
// 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
|
// constant index
|
||||||
if(isconst(nr, CTINT)) {
|
if(isconst(nr, CTINT)) {
|
||||||
if(isconst(nl, CTSTR))
|
if(isconst(nl, CTSTR))
|
||||||
|
|
|
@ -305,6 +305,7 @@ cgen_callinter(Node *n, Node *res, int proc)
|
||||||
|
|
||||||
nodo.xoffset -= widthptr;
|
nodo.xoffset -= widthptr;
|
||||||
cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab
|
cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab
|
||||||
|
cgen_checknil(&nodr); // in case offset is huge
|
||||||
|
|
||||||
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
||||||
|
|
||||||
|
@ -873,3 +874,43 @@ clearfat(Node *nl)
|
||||||
regfree(&dst);
|
regfree(&dst);
|
||||||
regfree(&nz);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1189,48 +1189,6 @@ gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs)
|
||||||
return p;
|
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;
|
* generate code to compute n;
|
||||||
* make a refer to result.
|
* make a refer to result.
|
||||||
|
@ -1294,7 +1252,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->reg = n->val.u.reg;
|
a->reg = n->val.u.reg;
|
||||||
a->sym = n->sym;
|
a->sym = n->sym;
|
||||||
a->offset = n->xoffset;
|
a->offset = n->xoffset;
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPARAM:
|
case OPARAM:
|
||||||
|
@ -1402,8 +1359,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->etype = TINT32;
|
a->etype = TINT32;
|
||||||
if(a->type == D_CONST && a->offset == 0)
|
if(a->type == D_CONST && a->offset == 0)
|
||||||
break; // len(nil)
|
break; // len(nil)
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OLEN:
|
case OLEN:
|
||||||
|
@ -1413,8 +1368,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
if(a->type == D_CONST && a->offset == 0)
|
if(a->type == D_CONST && a->offset == 0)
|
||||||
break; // len(nil)
|
break; // len(nil)
|
||||||
a->offset += Array_nel;
|
a->offset += Array_nel;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCAP:
|
case OCAP:
|
||||||
|
@ -1424,8 +1377,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
if(a->type == D_CONST && a->offset == 0)
|
if(a->type == D_CONST && a->offset == 0)
|
||||||
break; // cap(nil)
|
break; // cap(nil)
|
||||||
a->offset += Array_cap;
|
a->offset += Array_cap;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OADDR:
|
case OADDR:
|
||||||
|
@ -1932,6 +1883,7 @@ odot:
|
||||||
n1.xoffset = oary[0];
|
n1.xoffset = oary[0];
|
||||||
} else {
|
} else {
|
||||||
cgen(nn, reg);
|
cgen(nn, reg);
|
||||||
|
cgen_checknil(reg);
|
||||||
n1.xoffset = -(oary[0]+1);
|
n1.xoffset = -(oary[0]+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1939,6 +1891,7 @@ odot:
|
||||||
if(oary[i] >= 0)
|
if(oary[i] >= 0)
|
||||||
fatal("can't happen");
|
fatal("can't happen");
|
||||||
gins(AMOVW, &n1, reg);
|
gins(AMOVW, &n1, reg);
|
||||||
|
cgen_checknil(reg);
|
||||||
n1.xoffset = -(oary[i]+1);
|
n1.xoffset = -(oary[i]+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1986,9 +1939,10 @@ oindex:
|
||||||
// load the array (reg)
|
// load the array (reg)
|
||||||
if(l->ullman > r->ullman) {
|
if(l->ullman > r->ullman) {
|
||||||
regalloc(reg, types[tptr], N);
|
regalloc(reg, types[tptr], N);
|
||||||
if(o & OPtrto)
|
if(o & OPtrto) {
|
||||||
cgen(l, reg);
|
cgen(l, reg);
|
||||||
else
|
cgen_checknil(reg);
|
||||||
|
} else
|
||||||
agen(l, reg);
|
agen(l, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2005,9 +1959,10 @@ oindex:
|
||||||
// load the array (reg)
|
// load the array (reg)
|
||||||
if(l->ullman <= r->ullman) {
|
if(l->ullman <= r->ullman) {
|
||||||
regalloc(reg, types[tptr], N);
|
regalloc(reg, types[tptr], N);
|
||||||
if(o & OPtrto)
|
if(o & OPtrto) {
|
||||||
cgen(l, reg);
|
cgen(l, reg);
|
||||||
else
|
cgen_checknil(reg);
|
||||||
|
} else
|
||||||
agen(l, reg);
|
agen(l, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2019,20 +1974,10 @@ oindex:
|
||||||
n2.type = types[tptr];
|
n2.type = types[tptr];
|
||||||
n2.xoffset = Array_nel;
|
n2.xoffset = Array_nel;
|
||||||
} else {
|
} 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)
|
if(o & OPtrto)
|
||||||
nodconst(&n2, types[TUINT32], l->type->type->bound);
|
nodconst(&n2, types[TUINT32], l->type->type->bound);
|
||||||
|
else
|
||||||
|
nodconst(&n2, types[TUINT32], l->type->bound);
|
||||||
}
|
}
|
||||||
regalloc(&n3, n2.type, N);
|
regalloc(&n3, n2.type, N);
|
||||||
cgen(&n2, &n3);
|
cgen(&n2, &n3);
|
||||||
|
@ -2080,14 +2025,14 @@ oindex_const:
|
||||||
// can multiply by width statically
|
// can multiply by width statically
|
||||||
|
|
||||||
regalloc(reg, types[tptr], N);
|
regalloc(reg, types[tptr], N);
|
||||||
if(o & OPtrto)
|
if(o & OPtrto) {
|
||||||
cgen(l, reg);
|
cgen(l, reg);
|
||||||
else
|
cgen_checknil(reg);
|
||||||
|
} else
|
||||||
agen(l, reg);
|
agen(l, reg);
|
||||||
|
|
||||||
v = mpgetfix(r->val.u.xval);
|
v = mpgetfix(r->val.u.xval);
|
||||||
if(o & ODynam) {
|
if(o & ODynam) {
|
||||||
|
|
||||||
if(!debug['B'] && !n->bounded) {
|
if(!debug['B'] && !n->bounded) {
|
||||||
n1 = *reg;
|
n1 = *reg;
|
||||||
n1.op = OINDREG;
|
n1.op = OINDREG;
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
|
|
||||||
static int xtramodes(Graph*, Flow*, Adr*);
|
static int xtramodes(Graph*, Flow*, Adr*);
|
||||||
static int shortprop(Flow *r);
|
static int shortprop(Flow *r);
|
||||||
static int regtyp(Adr*);
|
|
||||||
static int subprop(Flow*);
|
static int subprop(Flow*);
|
||||||
static int copyprop(Graph*, Flow*);
|
static int copyprop(Graph*, Flow*);
|
||||||
static int copy1(Adr*, Adr*, Flow*, int);
|
static int copy1(Adr*, Adr*, Flow*, int);
|
||||||
|
@ -240,7 +239,7 @@ loop1:
|
||||||
flowend(g);
|
flowend(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
regtyp(Adr *a)
|
regtyp(Adr *a)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1055,6 +1054,7 @@ copyu(Prog *p, Adr *v, Adr *s)
|
||||||
case ADIVF:
|
case ADIVF:
|
||||||
case ADIVD:
|
case ADIVD:
|
||||||
|
|
||||||
|
case ACHECKNIL: /* read */
|
||||||
case ACMPF: /* read, read, */
|
case ACMPF: /* read, read, */
|
||||||
case ACMPD:
|
case ACMPD:
|
||||||
case ACMP:
|
case ACMP:
|
||||||
|
|
|
@ -28,6 +28,7 @@ static ProgInfo progtable[ALAST] = {
|
||||||
[APCDATA]= {Pseudo},
|
[APCDATA]= {Pseudo},
|
||||||
[AUNDEF]= {OK},
|
[AUNDEF]= {OK},
|
||||||
[AUSEFIELD]= {OK},
|
[AUSEFIELD]= {OK},
|
||||||
|
[ACHECKNIL]= {LeftRead},
|
||||||
|
|
||||||
// NOP is an internal no-op that also stands
|
// NOP is an internal no-op that also stands
|
||||||
// for USED and SET annotations, not the Intel opcode.
|
// for USED and SET annotations, not the Intel opcode.
|
||||||
|
|
|
@ -197,6 +197,7 @@ enum as
|
||||||
ATYPE,
|
ATYPE,
|
||||||
AFUNCDATA,
|
AFUNCDATA,
|
||||||
APCDATA,
|
APCDATA,
|
||||||
|
ACHECKNIL,
|
||||||
|
|
||||||
ALAST,
|
ALAST,
|
||||||
};
|
};
|
||||||
|
|
|
@ -384,7 +384,11 @@ cgen(Node *n, Node *res)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OADDR:
|
case OADDR:
|
||||||
|
if(n->bounded) // let race detector avoid nil checks
|
||||||
|
disable_checknil++;
|
||||||
agen(nl, res);
|
agen(nl, res);
|
||||||
|
if(n->bounded)
|
||||||
|
disable_checknil--;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCALLMETH:
|
case OCALLMETH:
|
||||||
|
@ -518,8 +522,8 @@ ret:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* allocate a register in res and generate
|
* allocate a register (reusing res if possible) and generate
|
||||||
* newreg = &n
|
* a = n
|
||||||
* The caller must call regfree(a).
|
* The caller must call regfree(a).
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
@ -560,14 +564,16 @@ cgenr(Node *n, Node *a, Node *res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* allocate a register in res and generate
|
* allocate a register (reusing res if possible) and generate
|
||||||
* res = &n
|
* a = &n
|
||||||
|
* The caller must call regfree(a).
|
||||||
|
* The generated code checks that the result is not nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
agenr(Node *n, Node *a, Node *res)
|
agenr(Node *n, Node *a, Node *res)
|
||||||
{
|
{
|
||||||
Node *nl, *nr;
|
Node *nl, *nr;
|
||||||
Node n1, n2, n3, n4, n5, tmp, tmp2, nlen;
|
Node n1, n2, n3, n5, tmp, tmp2, nlen;
|
||||||
Prog *p1;
|
Prog *p1;
|
||||||
Type *t;
|
Type *t;
|
||||||
uint64 w;
|
uint64 w;
|
||||||
|
@ -595,6 +601,7 @@ agenr(Node *n, Node *a, Node *res)
|
||||||
|
|
||||||
case OIND:
|
case OIND:
|
||||||
cgenr(n->left, a, res);
|
cgenr(n->left, a, res);
|
||||||
|
cgen_checknil(a);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OINDEX:
|
case OINDEX:
|
||||||
|
@ -656,18 +663,6 @@ agenr(Node *n, Node *a, Node *res)
|
||||||
// len(a) is in nlen (if needed)
|
// len(a) is in nlen (if needed)
|
||||||
// w is width
|
// 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
|
// constant index
|
||||||
if(isconst(nr, CTINT)) {
|
if(isconst(nr, CTINT)) {
|
||||||
if(isconst(nl, CTSTR))
|
if(isconst(nl, CTSTR))
|
||||||
|
@ -777,6 +772,7 @@ agenr(Node *n, Node *a, Node *res)
|
||||||
/*
|
/*
|
||||||
* generate:
|
* generate:
|
||||||
* res = &n;
|
* res = &n;
|
||||||
|
* The generated code checks that the result is not nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
agen(Node *n, Node *res)
|
agen(Node *n, Node *res)
|
||||||
|
@ -882,43 +878,20 @@ agen(Node *n, Node *res)
|
||||||
|
|
||||||
case OIND:
|
case OIND:
|
||||||
cgen(nl, res);
|
cgen(nl, res);
|
||||||
|
cgen_checknil(res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ODOT:
|
case ODOT:
|
||||||
agen(nl, res);
|
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)
|
if(n->xoffset != 0)
|
||||||
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
|
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ODOTPTR:
|
case ODOTPTR:
|
||||||
cgen(nl, res);
|
cgen(nl, res);
|
||||||
// explicit check for nil if struct is large enough
|
cgen_checknil(res);
|
||||||
// that we might derive too big a pointer.
|
if(n->xoffset != 0)
|
||||||
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) {
|
|
||||||
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
|
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,6 +906,7 @@ ret:
|
||||||
*
|
*
|
||||||
* on exit, a has been changed to be *newreg.
|
* on exit, a has been changed to be *newreg.
|
||||||
* caller must regfree(a).
|
* caller must regfree(a).
|
||||||
|
* The generated code checks that the result is not *nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
igen(Node *n, Node *a, Node *res)
|
igen(Node *n, Node *a, Node *res)
|
||||||
|
@ -967,15 +941,7 @@ igen(Node *n, Node *a, Node *res)
|
||||||
|
|
||||||
case ODOTPTR:
|
case ODOTPTR:
|
||||||
cgenr(n->left, a, res);
|
cgenr(n->left, a, res);
|
||||||
// explicit check for nil if struct is large enough
|
cgen_checknil(a);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
a->op = OINDREG;
|
a->op = OINDREG;
|
||||||
a->xoffset += n->xoffset;
|
a->xoffset += n->xoffset;
|
||||||
a->type = n->type;
|
a->type = n->type;
|
||||||
|
@ -1017,6 +983,7 @@ igen(Node *n, Node *a, Node *res)
|
||||||
igen(n->left, a, res);
|
igen(n->left, a, res);
|
||||||
else {
|
else {
|
||||||
igen(n->left, &n1, res);
|
igen(n->left, &n1, res);
|
||||||
|
cgen_checknil(&n1);
|
||||||
regalloc(a, types[tptr], res);
|
regalloc(a, types[tptr], res);
|
||||||
gmove(&n1, a);
|
gmove(&n1, a);
|
||||||
regfree(&n1);
|
regfree(&n1);
|
||||||
|
|
|
@ -254,6 +254,7 @@ cgen_callinter(Node *n, Node *res, int proc)
|
||||||
regalloc(&nodr, types[tptr], &nodo);
|
regalloc(&nodr, types[tptr], &nodo);
|
||||||
if(n->left->xoffset == BADWIDTH)
|
if(n->left->xoffset == BADWIDTH)
|
||||||
fatal("cgen_callinter: badwidth");
|
fatal("cgen_callinter: badwidth");
|
||||||
|
cgen_checknil(&nodo); // in case offset is huge
|
||||||
nodo.op = OINDREG;
|
nodo.op = OINDREG;
|
||||||
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
||||||
if(proc == 0) {
|
if(proc == 0) {
|
||||||
|
@ -1065,3 +1066,51 @@ clearfat(Node *nl)
|
||||||
restx(&n1, &oldn1);
|
restx(&n1, &oldn1);
|
||||||
restx(&ax, &oldax);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1067,43 +1067,6 @@ gins(int as, Node *f, Node *t)
|
||||||
return p;
|
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;
|
* generate code to compute n;
|
||||||
* make a refer to result.
|
* make a refer to result.
|
||||||
|
@ -1161,7 +1124,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->offset = n->xoffset;
|
a->offset = n->xoffset;
|
||||||
if(a->offset != (int32)a->offset)
|
if(a->offset != (int32)a->offset)
|
||||||
yyerror("offset %lld too large for OINDREG", a->offset);
|
yyerror("offset %lld too large for OINDREG", a->offset);
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPARAM:
|
case OPARAM:
|
||||||
|
@ -1280,8 +1242,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
break; // itab(nil)
|
break; // itab(nil)
|
||||||
a->etype = tptr;
|
a->etype = tptr;
|
||||||
a->width = widthptr;
|
a->width = widthptr;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OLEN:
|
case OLEN:
|
||||||
|
@ -1292,8 +1252,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->etype = simtype[TUINT];
|
a->etype = simtype[TUINT];
|
||||||
a->offset += Array_nel;
|
a->offset += Array_nel;
|
||||||
a->width = widthint;
|
a->width = widthint;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCAP:
|
case OCAP:
|
||||||
|
@ -1304,8 +1262,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->etype = simtype[TUINT];
|
a->etype = simtype[TUINT];
|
||||||
a->offset += Array_cap;
|
a->offset += Array_cap;
|
||||||
a->width = widthint;
|
a->width = widthint;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case OADD:
|
// case OADD:
|
||||||
|
@ -2045,6 +2001,7 @@ odot:
|
||||||
n1.xoffset = oary[0];
|
n1.xoffset = oary[0];
|
||||||
} else {
|
} else {
|
||||||
cgen(nn, reg);
|
cgen(nn, reg);
|
||||||
|
cgen_checknil(reg);
|
||||||
n1.xoffset = -(oary[0]+1);
|
n1.xoffset = -(oary[0]+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2052,6 +2009,7 @@ odot:
|
||||||
if(oary[i] >= 0)
|
if(oary[i] >= 0)
|
||||||
fatal("can't happen");
|
fatal("can't happen");
|
||||||
gins(AMOVQ, &n1, reg);
|
gins(AMOVQ, &n1, reg);
|
||||||
|
cgen_checknil(reg);
|
||||||
n1.xoffset = -(oary[i]+1);
|
n1.xoffset = -(oary[i]+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2117,16 +2075,6 @@ oindex:
|
||||||
o |= OAddable;
|
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
|
// check bounds
|
||||||
if(!debug['B'] && !n->bounded) {
|
if(!debug['B'] && !n->bounded) {
|
||||||
// check bounds
|
// check bounds
|
||||||
|
|
|
@ -38,7 +38,6 @@ static void elimshortmov(Graph *g);
|
||||||
static int prevl(Flow *r, int reg);
|
static int prevl(Flow *r, int reg);
|
||||||
static void pushback(Flow *r);
|
static void pushback(Flow *r);
|
||||||
static int regconsttyp(Adr*);
|
static int regconsttyp(Adr*);
|
||||||
static int regtyp(Adr*);
|
|
||||||
static int subprop(Flow*);
|
static int subprop(Flow*);
|
||||||
static int copyprop(Graph*, Flow*);
|
static int copyprop(Graph*, Flow*);
|
||||||
static int copy1(Adr*, Adr*, Flow*, int);
|
static int copy1(Adr*, Adr*, Flow*, int);
|
||||||
|
@ -374,7 +373,7 @@ excise(Flow *r)
|
||||||
ostats.ndelmov++;
|
ostats.ndelmov++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
regtyp(Adr *a)
|
regtyp(Adr *a)
|
||||||
{
|
{
|
||||||
int t;
|
int t;
|
||||||
|
|
|
@ -40,6 +40,7 @@ static ProgInfo progtable[ALAST] = {
|
||||||
[APCDATA]= {Pseudo},
|
[APCDATA]= {Pseudo},
|
||||||
[AUNDEF]= {OK},
|
[AUNDEF]= {OK},
|
||||||
[AUSEFIELD]= {OK},
|
[AUSEFIELD]= {OK},
|
||||||
|
[ACHECKNIL]= {LeftRead},
|
||||||
|
|
||||||
// NOP is an internal no-op that also stands
|
// NOP is an internal no-op that also stands
|
||||||
// for USED and SET annotations, not the Intel opcode.
|
// for USED and SET annotations, not the Intel opcode.
|
||||||
|
|
|
@ -761,6 +761,7 @@ enum as
|
||||||
ATYPE,
|
ATYPE,
|
||||||
AFUNCDATA,
|
AFUNCDATA,
|
||||||
APCDATA,
|
APCDATA,
|
||||||
|
ACHECKNIL,
|
||||||
|
|
||||||
ALAST
|
ALAST
|
||||||
};
|
};
|
||||||
|
|
|
@ -476,12 +476,13 @@ igenindex(Node *n, Node *res, int bounded)
|
||||||
/*
|
/*
|
||||||
* address gen
|
* address gen
|
||||||
* res = &n;
|
* res = &n;
|
||||||
|
* The generated code checks that the result is not nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
agen(Node *n, Node *res)
|
agen(Node *n, Node *res)
|
||||||
{
|
{
|
||||||
Node *nl, *nr;
|
Node *nl, *nr;
|
||||||
Node n1, n2, n3, n4, tmp, nlen;
|
Node n1, n2, n3, tmp, nlen;
|
||||||
Type *t;
|
Type *t;
|
||||||
uint32 w;
|
uint32 w;
|
||||||
uint64 v;
|
uint64 v;
|
||||||
|
@ -606,16 +607,6 @@ agen(Node *n, Node *res)
|
||||||
// len(a) is in nlen (if needed)
|
// len(a) is in nlen (if needed)
|
||||||
// w is width
|
// 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
|
// constant index
|
||||||
if(isconst(nr, CTINT)) {
|
if(isconst(nr, CTINT)) {
|
||||||
if(isconst(nl, CTSTR))
|
if(isconst(nl, CTSTR))
|
||||||
|
@ -739,23 +730,11 @@ agen(Node *n, Node *res)
|
||||||
|
|
||||||
case OIND:
|
case OIND:
|
||||||
cgen(nl, res);
|
cgen(nl, res);
|
||||||
|
cgen_checknil(res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ODOT:
|
case ODOT:
|
||||||
agen(nl, res);
|
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) {
|
if(n->xoffset != 0) {
|
||||||
nodconst(&n1, types[tptr], n->xoffset);
|
nodconst(&n1, types[tptr], n->xoffset);
|
||||||
gins(optoas(OADD, types[tptr]), &n1, res);
|
gins(optoas(OADD, types[tptr]), &n1, res);
|
||||||
|
@ -767,17 +746,7 @@ agen(Node *n, Node *res)
|
||||||
if(!isptr[t->etype])
|
if(!isptr[t->etype])
|
||||||
fatal("agen: not ptr %N", n);
|
fatal("agen: not ptr %N", n);
|
||||||
cgen(nl, res);
|
cgen(nl, res);
|
||||||
// explicit check for nil if struct is large enough
|
cgen_checknil(res);
|
||||||
// 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) {
|
if(n->xoffset != 0) {
|
||||||
nodconst(&n1, types[tptr], n->xoffset);
|
nodconst(&n1, types[tptr], n->xoffset);
|
||||||
gins(optoas(OADD, types[tptr]), &n1, res);
|
gins(optoas(OADD, types[tptr]), &n1, res);
|
||||||
|
@ -793,6 +762,7 @@ agen(Node *n, Node *res)
|
||||||
*
|
*
|
||||||
* on exit, a has been changed to be *newreg.
|
* on exit, a has been changed to be *newreg.
|
||||||
* caller must regfree(a).
|
* caller must regfree(a).
|
||||||
|
* The generated code checks that the result is not *nil.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
igen(Node *n, Node *a, Node *res)
|
igen(Node *n, Node *a, Node *res)
|
||||||
|
@ -842,15 +812,7 @@ igen(Node *n, Node *a, Node *res)
|
||||||
regalloc(a, types[tptr], res);
|
regalloc(a, types[tptr], res);
|
||||||
cgen(n->left, a);
|
cgen(n->left, a);
|
||||||
}
|
}
|
||||||
// explicit check for nil if struct is large enough
|
cgen_checknil(a);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
a->op = OINDREG;
|
a->op = OINDREG;
|
||||||
a->xoffset += n->xoffset;
|
a->xoffset += n->xoffset;
|
||||||
a->type = n->type;
|
a->type = n->type;
|
||||||
|
|
|
@ -288,6 +288,7 @@ cgen_callinter(Node *n, Node *res, int proc)
|
||||||
regalloc(&nodr, types[tptr], &nodo);
|
regalloc(&nodr, types[tptr], &nodo);
|
||||||
if(n->left->xoffset == BADWIDTH)
|
if(n->left->xoffset == BADWIDTH)
|
||||||
fatal("cgen_callinter: badwidth");
|
fatal("cgen_callinter: badwidth");
|
||||||
|
cgen_checknil(&nodo);
|
||||||
nodo.op = OINDREG;
|
nodo.op = OINDREG;
|
||||||
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
||||||
|
|
||||||
|
@ -1216,3 +1217,51 @@ ret:
|
||||||
patch(gbranch(optoas(a, nr->type), T, likely), to);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2170,43 +2170,6 @@ gins(int as, Node *f, Node *t)
|
||||||
return p;
|
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;
|
* generate code to compute n;
|
||||||
* make a refer to result.
|
* make a refer to result.
|
||||||
|
@ -2356,8 +2319,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
break; // len(nil)
|
break; // len(nil)
|
||||||
a->etype = tptr;
|
a->etype = tptr;
|
||||||
a->width = widthptr;
|
a->width = widthptr;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OLEN:
|
case OLEN:
|
||||||
|
@ -2368,8 +2329,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->etype = TUINT32;
|
a->etype = TUINT32;
|
||||||
a->offset += Array_nel;
|
a->offset += Array_nel;
|
||||||
a->width = 4;
|
a->width = 4;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCAP:
|
case OCAP:
|
||||||
|
@ -2380,8 +2339,6 @@ naddr(Node *n, Addr *a, int canemitcode)
|
||||||
a->etype = TUINT32;
|
a->etype = TUINT32;
|
||||||
a->offset += Array_cap;
|
a->offset += Array_cap;
|
||||||
a->width = 4;
|
a->width = 4;
|
||||||
if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
|
|
||||||
checkoffset(a, canemitcode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case OADD:
|
// case OADD:
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
|
|
||||||
static void conprop(Flow *r);
|
static void conprop(Flow *r);
|
||||||
static void elimshortmov(Graph*);
|
static void elimshortmov(Graph*);
|
||||||
static int regtyp(Adr*);
|
|
||||||
static int subprop(Flow*);
|
static int subprop(Flow*);
|
||||||
static int copyprop(Graph*, Flow*);
|
static int copyprop(Graph*, Flow*);
|
||||||
static int copy1(Adr*, Adr*, Flow*, int);
|
static int copy1(Adr*, Adr*, Flow*, int);
|
||||||
|
@ -242,7 +241,7 @@ excise(Flow *r)
|
||||||
ostats.ndelmov++;
|
ostats.ndelmov++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
regtyp(Adr *a)
|
regtyp(Adr *a)
|
||||||
{
|
{
|
||||||
int t;
|
int t;
|
||||||
|
|
|
@ -40,6 +40,7 @@ static ProgInfo progtable[ALAST] = {
|
||||||
[APCDATA]= {Pseudo},
|
[APCDATA]= {Pseudo},
|
||||||
[AUNDEF]= {OK},
|
[AUNDEF]= {OK},
|
||||||
[AUSEFIELD]= {OK},
|
[AUSEFIELD]= {OK},
|
||||||
|
[ACHECKNIL]= {LeftRead},
|
||||||
|
|
||||||
// NOP is an internal no-op that also stands
|
// NOP is an internal no-op that also stands
|
||||||
// for USED and SET annotations, not the Intel opcode.
|
// for USED and SET annotations, not the Intel opcode.
|
||||||
|
|
|
@ -577,6 +577,7 @@ enum as
|
||||||
ATYPE,
|
ATYPE,
|
||||||
AFUNCDATA,
|
AFUNCDATA,
|
||||||
APCDATA,
|
APCDATA,
|
||||||
|
ACHECKNIL,
|
||||||
|
|
||||||
ALAST
|
ALAST
|
||||||
};
|
};
|
||||||
|
|
|
@ -407,8 +407,10 @@ walkpartialcall(Node *n, NodeList **init)
|
||||||
// Like walkclosure above.
|
// Like walkclosure above.
|
||||||
|
|
||||||
if(isinter(n->left->type)) {
|
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);
|
n->left = cheapexpr(n->left, init);
|
||||||
checknotnil(n->left, init);
|
checknil(n->left, init);
|
||||||
}
|
}
|
||||||
|
|
||||||
typ = nod(OTSTRUCT, N, N);
|
typ = nod(OTSTRUCT, N, N);
|
||||||
|
|
|
@ -493,8 +493,8 @@ gen(Node *n)
|
||||||
cgen_ret(n);
|
cgen_ret(n);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCHECKNOTNIL:
|
case OCHECKNIL:
|
||||||
checkref(n->left, 1);
|
cgen_checknil(n->left);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret:
|
ret:
|
||||||
|
@ -777,6 +777,8 @@ cgen_eface(Node *n, Node *res)
|
||||||
* n->left is s
|
* n->left is s
|
||||||
* n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
|
* n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
|
||||||
* caller (cgen) guarantees res is an addable ONAME.
|
* caller (cgen) guarantees res is an addable ONAME.
|
||||||
|
*
|
||||||
|
* called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
cgen_slice(Node *n, Node *res)
|
cgen_slice(Node *n, Node *res)
|
||||||
|
@ -808,21 +810,26 @@ cgen_slice(Node *n, Node *res)
|
||||||
dst.xoffset += Array_array;
|
dst.xoffset += Array_array;
|
||||||
dst.type = types[TUINTPTR];
|
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)) {
|
if(isnil(n->left)) {
|
||||||
tempname(&src, n->left->type);
|
tempname(&src, n->left->type);
|
||||||
cgen(n->left, &src);
|
cgen(n->left, &src);
|
||||||
} else
|
} else
|
||||||
src = *n->left;
|
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];
|
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);
|
cgen(&src, &dst);
|
||||||
} else {
|
} else {
|
||||||
add = nod(OADD, &src, offs);
|
add = nod(OADD, &src, offs);
|
||||||
|
|
|
@ -573,7 +573,7 @@ enum
|
||||||
OITAB, // itable word of an interface value.
|
OITAB, // itable word of an interface value.
|
||||||
OCLOSUREVAR, // variable reference at beginning of closure function
|
OCLOSUREVAR, // variable reference at beginning of closure function
|
||||||
OCFUNC, // reference to c function pointer (not go func value)
|
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
|
// arch-specific registers
|
||||||
OREGISTER, // a register, such as AX.
|
OREGISTER, // a register, such as AX.
|
||||||
|
@ -865,6 +865,8 @@ EXTERN char namebuf[NSYMB];
|
||||||
EXTERN char lexbuf[NSYMB];
|
EXTERN char lexbuf[NSYMB];
|
||||||
EXTERN char litbuf[NSYMB];
|
EXTERN char litbuf[NSYMB];
|
||||||
EXTERN int debug[256];
|
EXTERN int debug[256];
|
||||||
|
EXTERN char* debugstr;
|
||||||
|
EXTERN int debug_checknil;
|
||||||
EXTERN Sym* hash[NHASH];
|
EXTERN Sym* hash[NHASH];
|
||||||
EXTERN Sym* importmyname; // my name for package
|
EXTERN Sym* importmyname; // my name for package
|
||||||
EXTERN Pkg* localpkg; // package being compiled
|
EXTERN Pkg* localpkg; // package being compiled
|
||||||
|
@ -1445,16 +1447,18 @@ EXTERN Prog* firstpc;
|
||||||
EXTERN Prog* retpc;
|
EXTERN Prog* retpc;
|
||||||
|
|
||||||
EXTERN Node* nodfp;
|
EXTERN Node* nodfp;
|
||||||
|
EXTERN int disable_checknil;
|
||||||
|
|
||||||
int anyregalloc(void);
|
int anyregalloc(void);
|
||||||
void betypeinit(void);
|
void betypeinit(void);
|
||||||
void bgen(Node *n, int true, int likely, Prog *to);
|
void bgen(Node *n, int true, int likely, Prog *to);
|
||||||
void checkref(Node *n, int force);
|
void checknil(Node*, NodeList**);
|
||||||
void checknotnil(Node*, NodeList**);
|
void expandchecks(Prog*);
|
||||||
void cgen(Node*, Node*);
|
void cgen(Node*, Node*);
|
||||||
void cgen_asop(Node *n);
|
void cgen_asop(Node *n);
|
||||||
void cgen_call(Node *n, int proc);
|
void cgen_call(Node *n, int proc);
|
||||||
void cgen_callinter(Node *n, Node *res, int proc);
|
void cgen_callinter(Node *n, Node *res, int proc);
|
||||||
|
void cgen_checknil(Node*);
|
||||||
void cgen_ret(Node *n);
|
void cgen_ret(Node *n);
|
||||||
void clearfat(Node *n);
|
void clearfat(Node *n);
|
||||||
void compile(Node*);
|
void compile(Node*);
|
||||||
|
|
|
@ -44,6 +44,18 @@ static struct {
|
||||||
{nil, nil},
|
{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
|
static void
|
||||||
addexp(char *s)
|
addexp(char *s)
|
||||||
{
|
{
|
||||||
|
@ -238,7 +250,7 @@ main(int argc, char *argv[])
|
||||||
flagfn0("V", "print compiler version", doversion);
|
flagfn0("V", "print compiler version", doversion);
|
||||||
flagcount("W", "debug parse tree after type checking", &debug['W']);
|
flagcount("W", "debug parse tree after type checking", &debug['W']);
|
||||||
flagcount("complete", "compiling complete package (no C or assembly)", &pure_go);
|
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("e", "no limit on number of errors reported", &debug['e']);
|
||||||
flagcount("f", "debug stack frames", &debug['f']);
|
flagcount("f", "debug stack frames", &debug['f']);
|
||||||
flagcount("g", "debug code generation", &debug['g']);
|
flagcount("g", "debug code generation", &debug['g']);
|
||||||
|
@ -269,6 +281,24 @@ main(int argc, char *argv[])
|
||||||
racepkg = mkpkg(strlit("runtime/race"));
|
racepkg = mkpkg(strlit("runtime/race"));
|
||||||
racepkg->name = "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:
|
// enable inlining. for now:
|
||||||
// default: inlining on. (debug['l'] == 1)
|
// default: inlining on. (debug['l'] == 1)
|
||||||
|
|
|
@ -171,6 +171,7 @@ compile(Node *fn)
|
||||||
if(!debug['N'] || debug['R'] || debug['P']) {
|
if(!debug['N'] || debug['R'] || debug['P']) {
|
||||||
regopt(ptxt);
|
regopt(ptxt);
|
||||||
}
|
}
|
||||||
|
expandchecks(ptxt);
|
||||||
|
|
||||||
oldstksize = stksize;
|
oldstksize = stksize;
|
||||||
allocauto(ptxt);
|
allocauto(ptxt);
|
||||||
|
@ -504,3 +505,22 @@ movelargefn(Node *fn)
|
||||||
addrescapes(n);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -37,5 +37,6 @@ void flowrpo(Graph*);
|
||||||
void flowend(Graph*);
|
void flowend(Graph*);
|
||||||
void mergetemp(Prog*);
|
void mergetemp(Prog*);
|
||||||
int noreturn(Prog*);
|
int noreturn(Prog*);
|
||||||
|
int regtyp(Addr*);
|
||||||
Flow* uniqp(Flow*);
|
Flow* uniqp(Flow*);
|
||||||
Flow* uniqs(Flow*);
|
Flow* uniqs(Flow*);
|
||||||
|
|
|
@ -398,7 +398,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
|
||||||
// does not require instrumentation
|
// does not require instrumentation
|
||||||
case OPRINT: // don't bother instrumenting it
|
case OPRINT: // don't bother instrumenting it
|
||||||
case OPRINTN: // 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 OPARAM: // it appears only in fn->exit to copy heap params back
|
||||||
case OCLOSUREVAR:// immutable pointer to captured variable
|
case OCLOSUREVAR:// immutable pointer to captured variable
|
||||||
case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT)
|
case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT)
|
||||||
|
@ -530,6 +530,7 @@ uintptraddr(Node *n)
|
||||||
Node *r;
|
Node *r;
|
||||||
|
|
||||||
r = nod(OADDR, n, N);
|
r = nod(OADDR, n, N);
|
||||||
|
r->bounded = 1;
|
||||||
r = conv(r, types[TUNSAFEPTR]);
|
r = conv(r, types[TUNSAFEPTR]);
|
||||||
r = conv(r, types[TUINTPTR]);
|
r = conv(r, types[TUINTPTR]);
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -3769,7 +3769,7 @@ isbadimport(Strlit *path)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
checknotnil(Node *x, NodeList **init)
|
checknil(Node *x, NodeList **init)
|
||||||
{
|
{
|
||||||
Node *n;
|
Node *n;
|
||||||
|
|
||||||
|
@ -3777,7 +3777,7 @@ checknotnil(Node *x, NodeList **init)
|
||||||
x = nod(OITAB, x, N);
|
x = nod(OITAB, x, N);
|
||||||
typecheck(&x, Erv);
|
typecheck(&x, Erv);
|
||||||
}
|
}
|
||||||
n = nod(OCHECKNOTNIL, x, N);
|
n = nod(OCHECKNIL, x, N);
|
||||||
n->typecheck = 1;
|
n->typecheck = 1;
|
||||||
*init = list(*init, n);
|
*init = list(*init, n);
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ walkstmt(Node **np)
|
||||||
case OLABEL:
|
case OLABEL:
|
||||||
case ODCLCONST:
|
case ODCLCONST:
|
||||||
case ODCLTYPE:
|
case ODCLTYPE:
|
||||||
case OCHECKNOTNIL:
|
case OCHECKNIL:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OBLOCK:
|
case OBLOCK:
|
||||||
|
@ -405,8 +405,9 @@ walkexpr(Node **np, NodeList **init)
|
||||||
|
|
||||||
case OIND:
|
case OIND:
|
||||||
if(n->left->type->type->width == 0) {
|
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);
|
n->left = cheapexpr(n->left, init);
|
||||||
checknotnil(n->left, init);
|
checknil(n->left, init);
|
||||||
}
|
}
|
||||||
walkexpr(&n->left, init);
|
walkexpr(&n->left, init);
|
||||||
goto ret;
|
goto ret;
|
||||||
|
@ -419,8 +420,9 @@ walkexpr(Node **np, NodeList **init)
|
||||||
case ODOTPTR:
|
case ODOTPTR:
|
||||||
usefield(n);
|
usefield(n);
|
||||||
if(n->op == ODOTPTR && n->left->type->type->width == 0) {
|
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);
|
n->left = cheapexpr(n->left, init);
|
||||||
checknotnil(n->left, init);
|
checknil(n->left, init);
|
||||||
}
|
}
|
||||||
walkexpr(&n->left, init);
|
walkexpr(&n->left, init);
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
184
test/nilcheck.go
Normal file
184
test/nilcheck.go
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// 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"
|
||||||
|
}
|
||||||
|
|
128
test/nilptr2.go
Normal file
128
test/nilptr2.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// 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
|
||||||
|
}
|
Loading…
Reference in a new issue