mirror of
https://github.com/golang/go
synced 2024-11-23 05:20:11 -07:00
54e9406ffb
The change adds specialized type algorithms for slices and types of size 8/16/32/64/128. It significantly accelerates chan and map operations for most builtin types as well as user structs. benchmark old,ns/op new,ns/op BenchmarkChanUncontended 226 94 (on Intel Xeon E5620, 2.4GHz, Linux 64 bit) R=golang-dev, rsc CC=golang-dev https://golang.org/cl/4815087
3886 lines
66 KiB
C
3886 lines
66 KiB
C
// Copyright 2009 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.
|
|
|
|
#include "go.h"
|
|
#include "md5.h"
|
|
#include "y.tab.h"
|
|
#include "opnames.h"
|
|
#include "yerr.h"
|
|
|
|
static void dodump(Node*, int);
|
|
|
|
typedef struct Error Error;
|
|
struct Error
|
|
{
|
|
int lineno;
|
|
int seq;
|
|
char *msg;
|
|
};
|
|
static Error *err;
|
|
static int nerr;
|
|
static int merr;
|
|
|
|
void
|
|
errorexit(void)
|
|
{
|
|
flusherrors();
|
|
if(outfile)
|
|
remove(outfile);
|
|
exit(1);
|
|
}
|
|
|
|
extern int yychar;
|
|
int
|
|
parserline(void)
|
|
{
|
|
if(yychar != 0 && yychar != -2) // parser has one symbol lookahead
|
|
return prevlineno;
|
|
return lineno;
|
|
}
|
|
|
|
static void
|
|
adderr(int line, char *fmt, va_list arg)
|
|
{
|
|
Fmt f;
|
|
Error *p;
|
|
|
|
erroring++;
|
|
fmtstrinit(&f);
|
|
fmtprint(&f, "%L: ", line);
|
|
fmtvprint(&f, fmt, arg);
|
|
fmtprint(&f, "\n");
|
|
erroring--;
|
|
|
|
if(nerr >= merr) {
|
|
if(merr == 0)
|
|
merr = 16;
|
|
else
|
|
merr *= 2;
|
|
p = realloc(err, merr*sizeof err[0]);
|
|
if(p == nil) {
|
|
merr = nerr;
|
|
flusherrors();
|
|
print("out of memory\n");
|
|
errorexit();
|
|
}
|
|
err = p;
|
|
}
|
|
err[nerr].seq = nerr;
|
|
err[nerr].lineno = line;
|
|
err[nerr].msg = fmtstrflush(&f);
|
|
nerr++;
|
|
}
|
|
|
|
static int
|
|
errcmp(const void *va, const void *vb)
|
|
{
|
|
Error *a, *b;
|
|
|
|
a = (Error*)va;
|
|
b = (Error*)vb;
|
|
if(a->lineno != b->lineno)
|
|
return a->lineno - b->lineno;
|
|
if(a->seq != b->seq)
|
|
return a->seq - b->seq;
|
|
return strcmp(a->msg, b->msg);
|
|
}
|
|
|
|
void
|
|
flusherrors(void)
|
|
{
|
|
int i;
|
|
|
|
if(nerr == 0)
|
|
return;
|
|
qsort(err, nerr, sizeof err[0], errcmp);
|
|
for(i=0; i<nerr; i++)
|
|
if(i==0 || strcmp(err[i].msg, err[i-1].msg) != 0)
|
|
print("%s", err[i].msg);
|
|
nerr = 0;
|
|
}
|
|
|
|
static void
|
|
hcrash(void)
|
|
{
|
|
if(debug['h']) {
|
|
flusherrors();
|
|
if(outfile)
|
|
unlink(outfile);
|
|
*(volatile int*)0 = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
yyerrorl(int line, char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
adderr(line, fmt, arg);
|
|
va_end(arg);
|
|
|
|
hcrash();
|
|
nerrors++;
|
|
if(nerrors >= 10 && !debug['e']) {
|
|
flusherrors();
|
|
print("%L: too many errors\n", line);
|
|
errorexit();
|
|
}
|
|
}
|
|
|
|
extern int yystate, yychar;
|
|
|
|
void
|
|
yyerror(char *fmt, ...)
|
|
{
|
|
int i;
|
|
static int lastsyntax;
|
|
va_list arg;
|
|
char buf[512], *p;
|
|
|
|
if(strncmp(fmt, "syntax error", 12) == 0) {
|
|
nsyntaxerrors++;
|
|
|
|
if(debug['x'])
|
|
print("yyerror: yystate=%d yychar=%d\n", yystate, yychar);
|
|
|
|
// only one syntax error per line
|
|
if(lastsyntax == lexlineno)
|
|
return;
|
|
lastsyntax = lexlineno;
|
|
|
|
if(strstr(fmt, "{ or {")) {
|
|
// The grammar has { and LBRACE but both show up as {.
|
|
// Rewrite syntax error referring to "{ or {" to say just "{".
|
|
strecpy(buf, buf+sizeof buf, fmt);
|
|
p = strstr(buf, "{ or {");
|
|
if(p)
|
|
memmove(p+1, p+6, strlen(p+6)+1);
|
|
fmt = buf;
|
|
}
|
|
|
|
// look for parse state-specific errors in list (see go.errors).
|
|
for(i=0; i<nelem(yymsg); i++) {
|
|
if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
|
|
yyerrorl(lexlineno, "syntax error: %s", yymsg[i].msg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// plain "syntax error" gets "near foo" added
|
|
if(strcmp(fmt, "syntax error") == 0) {
|
|
yyerrorl(lexlineno, "syntax error near %s", lexbuf);
|
|
return;
|
|
}
|
|
|
|
// if bison says "syntax error, more info"; print "syntax error: more info".
|
|
if(fmt[12] == ',') {
|
|
yyerrorl(lexlineno, "syntax error:%s", fmt+13);
|
|
return;
|
|
}
|
|
|
|
yyerrorl(lexlineno, "%s", fmt);
|
|
return;
|
|
}
|
|
|
|
va_start(arg, fmt);
|
|
adderr(parserline(), fmt, arg);
|
|
va_end(arg);
|
|
|
|
hcrash();
|
|
nerrors++;
|
|
if(nerrors >= 10 && !debug['e']) {
|
|
flusherrors();
|
|
print("%L: too many errors\n", parserline());
|
|
errorexit();
|
|
}
|
|
}
|
|
|
|
void
|
|
warn(char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
adderr(parserline(), fmt, arg);
|
|
va_end(arg);
|
|
|
|
hcrash();
|
|
}
|
|
|
|
void
|
|
fatal(char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
flusherrors();
|
|
|
|
print("%L: internal compiler error: ", lineno);
|
|
va_start(arg, fmt);
|
|
vfprint(1, fmt, arg);
|
|
va_end(arg);
|
|
print("\n");
|
|
|
|
// If this is a released compiler version, ask for a bug report.
|
|
if(strncmp(getgoversion(), "release", 7) == 0) {
|
|
print("\n");
|
|
print("Please file a bug report including a short program that triggers the error.\n");
|
|
print("http://code.google.com/p/go/issues/entry?template=compilerbug\n");
|
|
}
|
|
hcrash();
|
|
errorexit();
|
|
}
|
|
|
|
void
|
|
linehist(char *file, int32 off, int relative)
|
|
{
|
|
Hist *h;
|
|
char *cp;
|
|
|
|
if(debug['i']) {
|
|
if(file != nil) {
|
|
if(off < 0)
|
|
print("pragma %s", file);
|
|
else
|
|
if(off > 0)
|
|
print("line %s", file);
|
|
else
|
|
print("import %s", file);
|
|
} else
|
|
print("end of import");
|
|
print(" at line %L\n", lexlineno);
|
|
}
|
|
|
|
if(off < 0 && file[0] != '/' && !relative) {
|
|
cp = mal(strlen(file) + strlen(pathname) + 2);
|
|
sprint(cp, "%s/%s", pathname, file);
|
|
file = cp;
|
|
}
|
|
|
|
h = mal(sizeof(Hist));
|
|
h->name = file;
|
|
h->line = lexlineno;
|
|
h->offset = off;
|
|
h->link = H;
|
|
if(ehist == H) {
|
|
hist = h;
|
|
ehist = h;
|
|
return;
|
|
}
|
|
ehist->link = h;
|
|
ehist = h;
|
|
}
|
|
|
|
int32
|
|
setlineno(Node *n)
|
|
{
|
|
int32 lno;
|
|
|
|
lno = lineno;
|
|
if(n != N)
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OTYPE:
|
|
case OPACK:
|
|
case OLITERAL:
|
|
break;
|
|
default:
|
|
lineno = n->lineno;
|
|
if(lineno == 0) {
|
|
if(debug['K'])
|
|
warn("setlineno: line 0");
|
|
lineno = lno;
|
|
}
|
|
}
|
|
return lno;
|
|
}
|
|
|
|
uint32
|
|
stringhash(char *p)
|
|
{
|
|
int32 h;
|
|
int c;
|
|
|
|
h = 0;
|
|
for(;;) {
|
|
c = *p++;
|
|
if(c == 0)
|
|
break;
|
|
h = h*PRIME1 + c;
|
|
}
|
|
|
|
if(h < 0) {
|
|
h = -h;
|
|
if(h < 0)
|
|
h = 0;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
Sym*
|
|
lookup(char *name)
|
|
{
|
|
return pkglookup(name, localpkg);
|
|
}
|
|
|
|
Sym*
|
|
pkglookup(char *name, Pkg *pkg)
|
|
{
|
|
Sym *s;
|
|
uint32 h;
|
|
int c;
|
|
|
|
h = stringhash(name) % NHASH;
|
|
c = name[0];
|
|
for(s = hash[h]; s != S; s = s->link) {
|
|
if(s->name[0] != c || s->pkg != pkg)
|
|
continue;
|
|
if(strcmp(s->name, name) == 0)
|
|
return s;
|
|
}
|
|
|
|
s = mal(sizeof(*s));
|
|
s->name = mal(strlen(name)+1);
|
|
strcpy(s->name, name);
|
|
|
|
s->pkg = pkg;
|
|
|
|
s->link = hash[h];
|
|
hash[h] = s;
|
|
s->lexical = LNAME;
|
|
|
|
return s;
|
|
}
|
|
|
|
Sym*
|
|
restrictlookup(char *name, Pkg *pkg)
|
|
{
|
|
if(!exportname(name) && pkg != localpkg)
|
|
yyerror("cannot refer to unexported name %s.%s", pkg->name, name);
|
|
return pkglookup(name, pkg);
|
|
}
|
|
|
|
|
|
// find all the exported symbols in package opkg
|
|
// and make them available in the current package
|
|
void
|
|
importdot(Pkg *opkg, Node *pack)
|
|
{
|
|
Sym *s, *s1;
|
|
uint32 h;
|
|
int n;
|
|
|
|
n = 0;
|
|
for(h=0; h<NHASH; h++) {
|
|
for(s = hash[h]; s != S; s = s->link) {
|
|
if(s->pkg != opkg)
|
|
continue;
|
|
if(s->def == N)
|
|
continue;
|
|
if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot
|
|
continue;
|
|
s1 = lookup(s->name);
|
|
if(s1->def != N) {
|
|
redeclare(s1, "during import");
|
|
continue;
|
|
}
|
|
s1->def = s->def;
|
|
s1->block = s->block;
|
|
s1->def->pack = pack;
|
|
n++;
|
|
}
|
|
}
|
|
if(n == 0) {
|
|
// can't possibly be used - there were no symbols
|
|
yyerrorl(pack->lineno, "imported and not used: %Z", opkg->path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gethunk(void)
|
|
{
|
|
char *h;
|
|
int32 nh;
|
|
|
|
nh = NHUNK;
|
|
if(thunk >= 10L*NHUNK)
|
|
nh = 10L*NHUNK;
|
|
h = (char*)malloc(nh);
|
|
if(h == nil) {
|
|
flusherrors();
|
|
yyerror("out of memory");
|
|
errorexit();
|
|
}
|
|
hunk = h;
|
|
nhunk = nh;
|
|
thunk += nh;
|
|
}
|
|
|
|
void*
|
|
mal(int32 n)
|
|
{
|
|
void *p;
|
|
|
|
if(n >= NHUNK) {
|
|
p = malloc(n);
|
|
if(p == nil) {
|
|
flusherrors();
|
|
yyerror("out of memory");
|
|
errorexit();
|
|
}
|
|
memset(p, 0, n);
|
|
return p;
|
|
}
|
|
|
|
while((uintptr)hunk & MAXALIGN) {
|
|
hunk++;
|
|
nhunk--;
|
|
}
|
|
if(nhunk < n)
|
|
gethunk();
|
|
|
|
p = hunk;
|
|
nhunk -= n;
|
|
hunk += n;
|
|
memset(p, 0, n);
|
|
return p;
|
|
}
|
|
|
|
void*
|
|
remal(void *p, int32 on, int32 n)
|
|
{
|
|
void *q;
|
|
|
|
q = (uchar*)p + on;
|
|
if(q != hunk || nhunk < n) {
|
|
if(on+n >= NHUNK) {
|
|
q = mal(on+n);
|
|
memmove(q, p, on);
|
|
return q;
|
|
}
|
|
if(nhunk < on+n)
|
|
gethunk();
|
|
memmove(hunk, p, on);
|
|
p = hunk;
|
|
hunk += on;
|
|
nhunk -= on;
|
|
}
|
|
hunk += n;
|
|
nhunk -= n;
|
|
return p;
|
|
}
|
|
|
|
Node*
|
|
nod(int op, Node *nleft, Node *nright)
|
|
{
|
|
Node *n;
|
|
|
|
n = mal(sizeof(*n));
|
|
n->op = op;
|
|
n->left = nleft;
|
|
n->right = nright;
|
|
n->lineno = parserline();
|
|
n->xoffset = BADWIDTH;
|
|
n->orig = n;
|
|
return n;
|
|
}
|
|
|
|
int
|
|
algtype(Type *t)
|
|
{
|
|
int a;
|
|
|
|
if(issimple[t->etype] || isptr[t->etype] ||
|
|
t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) {
|
|
if(t->width == 1)
|
|
a = AMEM8;
|
|
else if(t->width == 2)
|
|
a = AMEM16;
|
|
else if(t->width == 4)
|
|
a = AMEM32;
|
|
else if(t->width == 8)
|
|
a = AMEM64;
|
|
else if(t->width == 16)
|
|
a = AMEM128;
|
|
else
|
|
a = AMEM; // just bytes (int, ptr, etc)
|
|
} else if(t->etype == TSTRING)
|
|
a = ASTRING; // string
|
|
else if(isnilinter(t))
|
|
a = ANILINTER; // nil interface
|
|
else if(t->etype == TINTER)
|
|
a = AINTER; // interface
|
|
else if(isslice(t))
|
|
a = ASLICE; // slice
|
|
else {
|
|
if(t->width == 1)
|
|
a = ANOEQ8;
|
|
else if(t->width == 2)
|
|
a = ANOEQ16;
|
|
else if(t->width == 4)
|
|
a = ANOEQ32;
|
|
else if(t->width == 8)
|
|
a = ANOEQ64;
|
|
else if(t->width == 16)
|
|
a = ANOEQ128;
|
|
else
|
|
a = ANOEQ; // just bytes, but no hash/eq
|
|
}
|
|
return a;
|
|
}
|
|
|
|
Type*
|
|
maptype(Type *key, Type *val)
|
|
{
|
|
Type *t;
|
|
|
|
|
|
if(key != nil && key->etype != TANY && algtype(key) == ANOEQ) {
|
|
if(key->etype == TFORW) {
|
|
// map[key] used during definition of key.
|
|
// postpone check until key is fully defined.
|
|
// if there are multiple uses of map[key]
|
|
// before key is fully defined, the error
|
|
// will only be printed for the first one.
|
|
// good enough.
|
|
if(key->maplineno == 0)
|
|
key->maplineno = lineno;
|
|
} else
|
|
yyerror("invalid map key type %T", key);
|
|
}
|
|
t = typ(TMAP);
|
|
t->down = key;
|
|
t->type = val;
|
|
return t;
|
|
}
|
|
|
|
Type*
|
|
typ(int et)
|
|
{
|
|
Type *t;
|
|
|
|
t = mal(sizeof(*t));
|
|
t->etype = et;
|
|
t->width = BADWIDTH;
|
|
t->lineno = lineno;
|
|
t->orig = t;
|
|
return t;
|
|
}
|
|
|
|
static int
|
|
methcmp(const void *va, const void *vb)
|
|
{
|
|
Type *a, *b;
|
|
int i;
|
|
|
|
a = *(Type**)va;
|
|
b = *(Type**)vb;
|
|
i = strcmp(a->sym->name, b->sym->name);
|
|
if(i != 0)
|
|
return i;
|
|
if(!exportname(a->sym->name)) {
|
|
i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
|
|
if(i != 0)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Type*
|
|
sortinter(Type *t)
|
|
{
|
|
Type *f;
|
|
int i;
|
|
Type **a;
|
|
|
|
if(t->type == nil || t->type->down == nil)
|
|
return t;
|
|
|
|
i=0;
|
|
for(f=t->type; f; f=f->down)
|
|
i++;
|
|
a = mal(i*sizeof f);
|
|
i = 0;
|
|
for(f=t->type; f; f=f->down)
|
|
a[i++] = f;
|
|
qsort(a, i, sizeof a[0], methcmp);
|
|
while(i-- > 0) {
|
|
a[i]->down = f;
|
|
f = a[i];
|
|
}
|
|
t->type = f;
|
|
return t;
|
|
}
|
|
|
|
Node*
|
|
nodintconst(int64 v)
|
|
{
|
|
Node *c;
|
|
|
|
c = nod(OLITERAL, N, N);
|
|
c->addable = 1;
|
|
c->val.u.xval = mal(sizeof(*c->val.u.xval));
|
|
mpmovecfix(c->val.u.xval, v);
|
|
c->val.ctype = CTINT;
|
|
c->type = types[TIDEAL];
|
|
ullmancalc(c);
|
|
return c;
|
|
}
|
|
|
|
Node*
|
|
nodfltconst(Mpflt* v)
|
|
{
|
|
Node *c;
|
|
|
|
c = nod(OLITERAL, N, N);
|
|
c->addable = 1;
|
|
c->val.u.fval = mal(sizeof(*c->val.u.fval));
|
|
mpmovefltflt(c->val.u.fval, v);
|
|
c->val.ctype = CTFLT;
|
|
c->type = types[TIDEAL];
|
|
ullmancalc(c);
|
|
return c;
|
|
}
|
|
|
|
void
|
|
nodconst(Node *n, Type *t, int64 v)
|
|
{
|
|
memset(n, 0, sizeof(*n));
|
|
n->op = OLITERAL;
|
|
n->addable = 1;
|
|
ullmancalc(n);
|
|
n->val.u.xval = mal(sizeof(*n->val.u.xval));
|
|
mpmovecfix(n->val.u.xval, v);
|
|
n->val.ctype = CTINT;
|
|
n->type = t;
|
|
|
|
if(isfloat[t->etype])
|
|
fatal("nodconst: bad type %T", t);
|
|
}
|
|
|
|
Node*
|
|
nodnil(void)
|
|
{
|
|
Node *c;
|
|
|
|
c = nodintconst(0);
|
|
c->val.ctype = CTNIL;
|
|
c->type = types[TNIL];
|
|
return c;
|
|
}
|
|
|
|
Node*
|
|
nodbool(int b)
|
|
{
|
|
Node *c;
|
|
|
|
c = nodintconst(0);
|
|
c->val.ctype = CTBOOL;
|
|
c->val.u.bval = b;
|
|
c->type = idealbool;
|
|
return c;
|
|
}
|
|
|
|
Type*
|
|
aindex(Node *b, Type *t)
|
|
{
|
|
Type *r;
|
|
int bound;
|
|
|
|
bound = -1; // open bound
|
|
typecheck(&b, Erv);
|
|
if(b != nil) {
|
|
switch(consttype(b)) {
|
|
default:
|
|
yyerror("array bound must be an integer expression");
|
|
break;
|
|
case CTINT:
|
|
bound = mpgetfix(b->val.u.xval);
|
|
if(bound < 0)
|
|
yyerror("array bound must be non negative");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// fixed array
|
|
r = typ(TARRAY);
|
|
r->type = t;
|
|
r->bound = bound;
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
indent(int dep)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; i<dep; i++)
|
|
print(". ");
|
|
}
|
|
|
|
static void
|
|
dodumplist(NodeList *l, int dep)
|
|
{
|
|
for(; l; l=l->next)
|
|
dodump(l->n, dep);
|
|
}
|
|
|
|
static void
|
|
dodump(Node *n, int dep)
|
|
{
|
|
if(n == N)
|
|
return;
|
|
|
|
indent(dep);
|
|
if(dep > 10) {
|
|
print("...\n");
|
|
return;
|
|
}
|
|
|
|
if(n->ninit != nil) {
|
|
print("%O-init\n", n->op);
|
|
dodumplist(n->ninit, dep+1);
|
|
indent(dep);
|
|
}
|
|
|
|
switch(n->op) {
|
|
default:
|
|
print("%N\n", n);
|
|
dodump(n->left, dep+1);
|
|
dodump(n->right, dep+1);
|
|
break;
|
|
|
|
case OTYPE:
|
|
print("%O %S type=%T\n", n->op, n->sym, n->type);
|
|
if(n->type == T && n->ntype) {
|
|
indent(dep);
|
|
print("%O-ntype\n", n->op);
|
|
dodump(n->ntype, dep+1);
|
|
}
|
|
break;
|
|
|
|
case OIF:
|
|
print("%O%J\n", n->op, n);
|
|
dodump(n->ntest, dep+1);
|
|
if(n->nbody != nil) {
|
|
indent(dep);
|
|
print("%O-then\n", n->op);
|
|
dodumplist(n->nbody, dep+1);
|
|
}
|
|
if(n->nelse != nil) {
|
|
indent(dep);
|
|
print("%O-else\n", n->op);
|
|
dodumplist(n->nelse, dep+1);
|
|
}
|
|
break;
|
|
|
|
case OSELECT:
|
|
print("%O%J\n", n->op, n);
|
|
dodumplist(n->nbody, dep+1);
|
|
break;
|
|
|
|
case OSWITCH:
|
|
case OFOR:
|
|
print("%O%J\n", n->op, n);
|
|
dodump(n->ntest, dep+1);
|
|
|
|
if(n->nbody != nil) {
|
|
indent(dep);
|
|
print("%O-body\n", n->op);
|
|
dodumplist(n->nbody, dep+1);
|
|
}
|
|
|
|
if(n->nincr != N) {
|
|
indent(dep);
|
|
print("%O-incr\n", n->op);
|
|
dodump(n->nincr, dep+1);
|
|
}
|
|
break;
|
|
|
|
case OCASE:
|
|
// the right side points to label of the body
|
|
if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME)
|
|
print("%O%J GOTO %N\n", n->op, n, n->right->left);
|
|
else
|
|
print("%O%J\n", n->op, n);
|
|
dodump(n->left, dep+1);
|
|
break;
|
|
|
|
case OXCASE:
|
|
print("%N\n", n);
|
|
dodump(n->left, dep+1);
|
|
dodump(n->right, dep+1);
|
|
indent(dep);
|
|
print("%O-nbody\n", n->op);
|
|
dodumplist(n->nbody, dep+1);
|
|
break;
|
|
}
|
|
|
|
if(0 && n->ntype != nil) {
|
|
indent(dep);
|
|
print("%O-ntype\n", n->op);
|
|
dodump(n->ntype, dep+1);
|
|
}
|
|
if(n->list != nil) {
|
|
indent(dep);
|
|
print("%O-list\n", n->op);
|
|
dodumplist(n->list, dep+1);
|
|
}
|
|
if(n->rlist != nil) {
|
|
indent(dep);
|
|
print("%O-rlist\n", n->op);
|
|
dodumplist(n->rlist, dep+1);
|
|
}
|
|
if(n->op != OIF && n->nbody != nil) {
|
|
indent(dep);
|
|
print("%O-nbody\n", n->op);
|
|
dodumplist(n->nbody, dep+1);
|
|
}
|
|
}
|
|
|
|
void
|
|
dumplist(char *s, NodeList *l)
|
|
{
|
|
print("%s\n", s);
|
|
dodumplist(l, 1);
|
|
}
|
|
|
|
void
|
|
dump(char *s, Node *n)
|
|
{
|
|
print("%s [%p]\n", s, n);
|
|
dodump(n, 1);
|
|
}
|
|
|
|
static char*
|
|
goopnames[] =
|
|
{
|
|
[OADDR] = "&",
|
|
[OADD] = "+",
|
|
[OANDAND] = "&&",
|
|
[OANDNOT] = "&^",
|
|
[OAND] = "&",
|
|
[OAPPEND] = "append",
|
|
[OAS] = "=",
|
|
[OAS2] = "=",
|
|
[OBREAK] = "break",
|
|
[OCALL] = "function call",
|
|
[OCAP] = "cap",
|
|
[OCASE] = "case",
|
|
[OCLOSE] = "close",
|
|
[OCOMPLEX] = "complex",
|
|
[OCOM] = "^",
|
|
[OCONTINUE] = "continue",
|
|
[OCOPY] = "copy",
|
|
[ODEC] = "--",
|
|
[ODEFER] = "defer",
|
|
[ODIV] = "/",
|
|
[OEQ] = "==",
|
|
[OFALL] = "fallthrough",
|
|
[OFOR] = "for",
|
|
[OGE] = ">=",
|
|
[OGOTO] = "goto",
|
|
[OGT] = ">",
|
|
[OIF] = "if",
|
|
[OIMAG] = "imag",
|
|
[OINC] = "++",
|
|
[OIND] = "*",
|
|
[OLEN] = "len",
|
|
[OLE] = "<=",
|
|
[OLSH] = "<<",
|
|
[OLT] = "<",
|
|
[OMAKE] = "make",
|
|
[OMINUS] = "-",
|
|
[OMOD] = "%",
|
|
[OMUL] = "*",
|
|
[ONEW] = "new",
|
|
[ONE] = "!=",
|
|
[ONOT] = "!",
|
|
[OOROR] = "||",
|
|
[OOR] = "|",
|
|
[OPANIC] = "panic",
|
|
[OPLUS] = "+",
|
|
[OPRINTN] = "println",
|
|
[OPRINT] = "print",
|
|
[ORANGE] = "range",
|
|
[OREAL] = "real",
|
|
[ORECV] = "<-",
|
|
[ORETURN] = "return",
|
|
[ORSH] = ">>",
|
|
[OSELECT] = "select",
|
|
[OSEND] = "<-",
|
|
[OSUB] = "-",
|
|
[OSWITCH] = "switch",
|
|
[OXOR] = "^",
|
|
};
|
|
|
|
int
|
|
Oconv(Fmt *fp)
|
|
{
|
|
int o;
|
|
|
|
o = va_arg(fp->args, int);
|
|
if((fp->flags & FmtSharp) && o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
|
|
return fmtstrcpy(fp, goopnames[o]);
|
|
if(o < 0 || o >= nelem(opnames) || opnames[o] == nil)
|
|
return fmtprint(fp, "O-%d", o);
|
|
return fmtstrcpy(fp, opnames[o]);
|
|
}
|
|
|
|
int
|
|
Lconv(Fmt *fp)
|
|
{
|
|
struct
|
|
{
|
|
Hist* incl; /* start of this include file */
|
|
int32 idel; /* delta line number to apply to include */
|
|
Hist* line; /* start of this #line directive */
|
|
int32 ldel; /* delta line number to apply to #line */
|
|
} a[HISTSZ];
|
|
int32 lno, d;
|
|
int i, n;
|
|
Hist *h;
|
|
|
|
lno = va_arg(fp->args, int32);
|
|
|
|
n = 0;
|
|
for(h=hist; h!=H; h=h->link) {
|
|
if(h->offset < 0)
|
|
continue;
|
|
if(lno < h->line)
|
|
break;
|
|
if(h->name) {
|
|
if(h->offset > 0) {
|
|
// #line directive
|
|
if(n > 0 && n < HISTSZ) {
|
|
a[n-1].line = h;
|
|
a[n-1].ldel = h->line - h->offset + 1;
|
|
}
|
|
} else {
|
|
// beginning of file
|
|
if(n < HISTSZ) {
|
|
a[n].incl = h;
|
|
a[n].idel = h->line;
|
|
a[n].line = 0;
|
|
}
|
|
n++;
|
|
}
|
|
continue;
|
|
}
|
|
n--;
|
|
if(n > 0 && n < HISTSZ) {
|
|
d = h->line - a[n].incl->line;
|
|
a[n-1].ldel += d;
|
|
a[n-1].idel += d;
|
|
}
|
|
}
|
|
|
|
if(n > HISTSZ)
|
|
n = HISTSZ;
|
|
|
|
for(i=n-1; i>=0; i--) {
|
|
if(i != n-1) {
|
|
if(fp->flags & ~(FmtWidth|FmtPrec))
|
|
break;
|
|
fmtprint(fp, " ");
|
|
}
|
|
if(debug['L'])
|
|
fmtprint(fp, "%s/", pathname);
|
|
if(a[i].line)
|
|
fmtprint(fp, "%s:%d[%s:%d]",
|
|
a[i].line->name, lno-a[i].ldel+1,
|
|
a[i].incl->name, lno-a[i].idel+1);
|
|
else
|
|
fmtprint(fp, "%s:%d",
|
|
a[i].incl->name, lno-a[i].idel+1);
|
|
lno = a[i].incl->line - 1; // now print out start of this file
|
|
}
|
|
if(n == 0)
|
|
fmtprint(fp, "<epoch>");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
s%,%,\n%g
|
|
s%\n+%\n%g
|
|
s%^[ ]*T%%g
|
|
s%,.*%%g
|
|
s%.+% [T&] = "&",%g
|
|
s%^ ........*\]%&~%g
|
|
s%~ %%g
|
|
*/
|
|
|
|
static char*
|
|
etnames[] =
|
|
{
|
|
[TINT] = "INT",
|
|
[TUINT] = "UINT",
|
|
[TINT8] = "INT8",
|
|
[TUINT8] = "UINT8",
|
|
[TINT16] = "INT16",
|
|
[TUINT16] = "UINT16",
|
|
[TINT32] = "INT32",
|
|
[TUINT32] = "UINT32",
|
|
[TINT64] = "INT64",
|
|
[TUINT64] = "UINT64",
|
|
[TUINTPTR] = "UINTPTR",
|
|
[TFLOAT32] = "FLOAT32",
|
|
[TFLOAT64] = "FLOAT64",
|
|
[TCOMPLEX64] = "COMPLEX64",
|
|
[TCOMPLEX128] = "COMPLEX128",
|
|
[TBOOL] = "BOOL",
|
|
[TPTR32] = "PTR32",
|
|
[TPTR64] = "PTR64",
|
|
[TFUNC] = "FUNC",
|
|
[TARRAY] = "ARRAY",
|
|
[TSTRUCT] = "STRUCT",
|
|
[TCHAN] = "CHAN",
|
|
[TMAP] = "MAP",
|
|
[TINTER] = "INTER",
|
|
[TFORW] = "FORW",
|
|
[TFIELD] = "FIELD",
|
|
[TSTRING] = "STRING",
|
|
[TANY] = "ANY",
|
|
};
|
|
|
|
int
|
|
Econv(Fmt *fp)
|
|
{
|
|
int et;
|
|
|
|
et = va_arg(fp->args, int);
|
|
if(et < 0 || et >= nelem(etnames) || etnames[et] == nil)
|
|
return fmtprint(fp, "E-%d", et);
|
|
return fmtstrcpy(fp, etnames[et]);
|
|
}
|
|
|
|
static const char* classnames[] = {
|
|
"Pxxx",
|
|
"PEXTERN",
|
|
"PAUTO",
|
|
"PPARAM",
|
|
"PPARAMOUT",
|
|
"PPARAMREF",
|
|
"PFUNC",
|
|
};
|
|
|
|
int
|
|
Jconv(Fmt *fp)
|
|
{
|
|
Node *n;
|
|
char *s;
|
|
int c;
|
|
|
|
n = va_arg(fp->args, Node*);
|
|
|
|
c = fp->flags&FmtShort;
|
|
|
|
if(!c && n->ullman != 0)
|
|
fmtprint(fp, " u(%d)", n->ullman);
|
|
|
|
if(!c && n->addable != 0)
|
|
fmtprint(fp, " a(%d)", n->addable);
|
|
|
|
if(!c && n->vargen != 0)
|
|
fmtprint(fp, " g(%d)", n->vargen);
|
|
|
|
if(n->lineno != 0)
|
|
fmtprint(fp, " l(%d)", n->lineno);
|
|
|
|
if(!c && n->xoffset != BADWIDTH)
|
|
fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
|
|
|
|
if(n->class != 0) {
|
|
s = "";
|
|
if (n->class & PHEAP) s = ",heap";
|
|
if ((n->class & ~PHEAP) < nelem(classnames))
|
|
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
|
|
else
|
|
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
|
|
}
|
|
|
|
if(n->colas != 0)
|
|
fmtprint(fp, " colas(%d)", n->colas);
|
|
|
|
if(n->funcdepth != 0)
|
|
fmtprint(fp, " f(%d)", n->funcdepth);
|
|
|
|
if(n->noescape != 0)
|
|
fmtprint(fp, " ne(%d)", n->noescape);
|
|
|
|
if(!c && n->typecheck != 0)
|
|
fmtprint(fp, " tc(%d)", n->typecheck);
|
|
|
|
if(!c && n->dodata != 0)
|
|
fmtprint(fp, " dd(%d)", n->dodata);
|
|
|
|
if(n->isddd != 0)
|
|
fmtprint(fp, " isddd(%d)", n->isddd);
|
|
|
|
if(n->implicit != 0)
|
|
fmtprint(fp, " implicit(%d)", n->implicit);
|
|
|
|
if(!c && n->pun != 0)
|
|
fmtprint(fp, " pun(%d)", n->pun);
|
|
|
|
if(!c && n->used != 0)
|
|
fmtprint(fp, " used(%d)", n->used);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Sconv(Fmt *fp)
|
|
{
|
|
Sym *s;
|
|
|
|
s = va_arg(fp->args, Sym*);
|
|
if(s == S) {
|
|
fmtstrcpy(fp, "<S>");
|
|
return 0;
|
|
}
|
|
|
|
if(fp->flags & FmtShort)
|
|
goto shrt;
|
|
|
|
if(exporting || (fp->flags & FmtSharp)) {
|
|
if(packagequotes)
|
|
fmtprint(fp, "\"%Z\"", s->pkg->path);
|
|
else
|
|
fmtprint(fp, "%s", s->pkg->prefix);
|
|
fmtprint(fp, ".%s", s->name);
|
|
return 0;
|
|
}
|
|
|
|
if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
|
|
// This one is for the user. If the package name
|
|
// was used by multiple packages, give the full
|
|
// import path to disambiguate.
|
|
if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) {
|
|
fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
|
|
return 0;
|
|
}
|
|
fmtprint(fp, "%s.%s", s->pkg->name, s->name);
|
|
return 0;
|
|
}
|
|
|
|
shrt:
|
|
fmtstrcpy(fp, s->name);
|
|
return 0;
|
|
}
|
|
|
|
static char*
|
|
basicnames[] =
|
|
{
|
|
[TINT] = "int",
|
|
[TUINT] = "uint",
|
|
[TINT8] = "int8",
|
|
[TUINT8] = "uint8",
|
|
[TINT16] = "int16",
|
|
[TUINT16] = "uint16",
|
|
[TINT32] = "int32",
|
|
[TUINT32] = "uint32",
|
|
[TINT64] = "int64",
|
|
[TUINT64] = "uint64",
|
|
[TUINTPTR] = "uintptr",
|
|
[TFLOAT32] = "float32",
|
|
[TFLOAT64] = "float64",
|
|
[TCOMPLEX64] = "complex64",
|
|
[TCOMPLEX128] = "complex128",
|
|
[TBOOL] = "bool",
|
|
[TANY] = "any",
|
|
[TSTRING] = "string",
|
|
[TNIL] = "nil",
|
|
[TIDEAL] = "ideal",
|
|
[TBLANK] = "blank",
|
|
};
|
|
|
|
int
|
|
Tpretty(Fmt *fp, Type *t)
|
|
{
|
|
Type *t1;
|
|
Sym *s;
|
|
|
|
if(0 && debug['r']) {
|
|
debug['r'] = 0;
|
|
fmtprint(fp, "%T (orig=%T)", t, t->orig);
|
|
debug['r'] = 1;
|
|
return 0;
|
|
}
|
|
|
|
if(t->etype != TFIELD
|
|
&& t->sym != S
|
|
&& !(fp->flags&FmtLong)) {
|
|
s = t->sym;
|
|
if(t == types[t->etype] && t->etype != TUNSAFEPTR)
|
|
return fmtprint(fp, "%s", s->name);
|
|
if(exporting) {
|
|
if(fp->flags & FmtShort)
|
|
fmtprint(fp, "%hS", s);
|
|
else
|
|
fmtprint(fp, "%S", s);
|
|
if(s->pkg != localpkg)
|
|
return 0;
|
|
if(t->vargen)
|
|
fmtprint(fp, "·%d", t->vargen);
|
|
return 0;
|
|
}
|
|
return fmtprint(fp, "%S", s);
|
|
}
|
|
|
|
if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
|
|
if(isideal(t) && t->etype != TIDEAL && t->etype != TNIL)
|
|
fmtprint(fp, "ideal ");
|
|
return fmtprint(fp, "%s", basicnames[t->etype]);
|
|
}
|
|
|
|
switch(t->etype) {
|
|
case TPTR32:
|
|
case TPTR64:
|
|
if(fp->flags&FmtShort) // pass flag thru for methodsym
|
|
return fmtprint(fp, "*%hT", t->type);
|
|
return fmtprint(fp, "*%T", t->type);
|
|
|
|
case TCHAN:
|
|
switch(t->chan) {
|
|
case Crecv:
|
|
return fmtprint(fp, "<-chan %T", t->type);
|
|
case Csend:
|
|
return fmtprint(fp, "chan<- %T", t->type);
|
|
}
|
|
if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
|
|
return fmtprint(fp, "chan (%T)", t->type);
|
|
return fmtprint(fp, "chan %T", t->type);
|
|
|
|
case TMAP:
|
|
return fmtprint(fp, "map[%T] %T", t->down, t->type);
|
|
|
|
case TFUNC:
|
|
// t->type is method struct
|
|
// t->type->down is result struct
|
|
// t->type->down->down is arg struct
|
|
if(t->thistuple && !(fp->flags&FmtSharp) && !(fp->flags&FmtShort)) {
|
|
fmtprint(fp, "method(");
|
|
for(t1=getthisx(t)->type; t1; t1=t1->down) {
|
|
fmtprint(fp, "%T", t1);
|
|
if(t1->down)
|
|
fmtprint(fp, ", ");
|
|
}
|
|
fmtprint(fp, ")");
|
|
}
|
|
|
|
if(!(fp->flags&FmtByte))
|
|
fmtprint(fp, "func");
|
|
fmtprint(fp, "(");
|
|
for(t1=getinargx(t)->type; t1; t1=t1->down) {
|
|
if(noargnames && t1->etype == TFIELD) {
|
|
if(t1->isddd)
|
|
fmtprint(fp, "...%T", t1->type->type);
|
|
else
|
|
fmtprint(fp, "%T", t1->type);
|
|
} else
|
|
fmtprint(fp, "%T", t1);
|
|
if(t1->down)
|
|
fmtprint(fp, ", ");
|
|
}
|
|
fmtprint(fp, ")");
|
|
switch(t->outtuple) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
t1 = getoutargx(t)->type;
|
|
if(t1 == T) {
|
|
// failure to typecheck earlier; don't know the type
|
|
fmtprint(fp, " ?unknown-type?");
|
|
break;
|
|
}
|
|
if(t1->etype == TFIELD)
|
|
t1 = t1->type;
|
|
fmtprint(fp, " %T", t1);
|
|
break;
|
|
default:
|
|
t1 = getoutargx(t)->type;
|
|
fmtprint(fp, " (");
|
|
for(; t1; t1=t1->down) {
|
|
if(noargnames && t1->etype == TFIELD)
|
|
fmtprint(fp, "%T", t1->type);
|
|
else
|
|
fmtprint(fp, "%T", t1);
|
|
if(t1->down)
|
|
fmtprint(fp, ", ");
|
|
}
|
|
fmtprint(fp, ")");
|
|
break;
|
|
}
|
|
return 0;
|
|
|
|
case TARRAY:
|
|
if(t->bound >= 0)
|
|
return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
|
|
if(t->bound == -100)
|
|
return fmtprint(fp, "[...]%T", t->type);
|
|
return fmtprint(fp, "[]%T", t->type);
|
|
|
|
case TINTER:
|
|
fmtprint(fp, "interface {");
|
|
for(t1=t->type; t1!=T; t1=t1->down) {
|
|
fmtprint(fp, " ");
|
|
if(exportname(t1->sym->name))
|
|
fmtprint(fp, "%hS", t1->sym);
|
|
else
|
|
fmtprint(fp, "%S", t1->sym);
|
|
fmtprint(fp, "%hhT", t1->type);
|
|
if(t1->down)
|
|
fmtprint(fp, ";");
|
|
}
|
|
return fmtprint(fp, " }");
|
|
|
|
case TSTRUCT:
|
|
if(t->funarg) {
|
|
fmtprint(fp, "(");
|
|
for(t1=t->type; t1!=T; t1=t1->down) {
|
|
fmtprint(fp, "%T", t1);
|
|
if(t1->down)
|
|
fmtprint(fp, ", ");
|
|
}
|
|
return fmtprint(fp, ")");
|
|
}
|
|
fmtprint(fp, "struct {");
|
|
for(t1=t->type; t1!=T; t1=t1->down) {
|
|
fmtprint(fp, " %T", t1);
|
|
if(t1->down)
|
|
fmtprint(fp, ";");
|
|
}
|
|
return fmtprint(fp, " }");
|
|
|
|
case TFIELD:
|
|
if(t->sym == S || t->embedded) {
|
|
if(exporting)
|
|
fmtprint(fp, "? ");
|
|
} else
|
|
fmtprint(fp, "%hS ", t->sym);
|
|
if(t->isddd)
|
|
fmtprint(fp, "...%T", t->type->type);
|
|
else
|
|
fmtprint(fp, "%T", t->type);
|
|
if(t->note) {
|
|
fmtprint(fp, " ");
|
|
if(exporting)
|
|
fmtprint(fp, ":");
|
|
fmtprint(fp, "\"%Z\"", t->note);
|
|
}
|
|
return 0;
|
|
|
|
case TFORW:
|
|
if(exporting)
|
|
yyerror("undefined type %S", t->sym);
|
|
if(t->sym)
|
|
return fmtprint(fp, "undefined %S", t->sym);
|
|
return fmtprint(fp, "undefined");
|
|
|
|
case TUNSAFEPTR:
|
|
if(exporting)
|
|
return fmtprint(fp, "\"unsafe\".Pointer");
|
|
return fmtprint(fp, "unsafe.Pointer");
|
|
}
|
|
|
|
// Don't know how to handle - fall back to detailed prints.
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
Tconv(Fmt *fp)
|
|
{
|
|
Type *t, *t1;
|
|
int r, et, sharp, minus;
|
|
|
|
sharp = (fp->flags & FmtSharp);
|
|
minus = (fp->flags & FmtLeft);
|
|
fp->flags &= ~(FmtSharp|FmtLeft);
|
|
|
|
t = va_arg(fp->args, Type*);
|
|
if(t == T)
|
|
return fmtstrcpy(fp, "<T>");
|
|
|
|
t->trecur++;
|
|
if(t->trecur > 5) {
|
|
fmtprint(fp, "...");
|
|
goto out;
|
|
}
|
|
|
|
if(!debug['t']) {
|
|
if(sharp)
|
|
exporting++;
|
|
if(minus)
|
|
noargnames++;
|
|
r = Tpretty(fp, t);
|
|
if(sharp)
|
|
exporting--;
|
|
if(minus)
|
|
noargnames--;
|
|
if(r >= 0) {
|
|
t->trecur--;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if(sharp || exporting)
|
|
fatal("missing %E case during export", t->etype);
|
|
|
|
et = t->etype;
|
|
fmtprint(fp, "%E ", et);
|
|
if(t->sym != S)
|
|
fmtprint(fp, "<%S>", t->sym);
|
|
|
|
switch(et) {
|
|
default:
|
|
if(t->type != T)
|
|
fmtprint(fp, " %T", t->type);
|
|
break;
|
|
|
|
case TFIELD:
|
|
fmtprint(fp, "%T", t->type);
|
|
break;
|
|
|
|
case TFUNC:
|
|
if(fp->flags & FmtLong)
|
|
fmtprint(fp, "%d%d%d(%lT,%lT)%lT",
|
|
t->thistuple, t->intuple, t->outtuple,
|
|
t->type, t->type->down->down, t->type->down);
|
|
else
|
|
fmtprint(fp, "%d%d%d(%T,%T)%T",
|
|
t->thistuple, t->intuple, t->outtuple,
|
|
t->type, t->type->down->down, t->type->down);
|
|
break;
|
|
|
|
case TINTER:
|
|
fmtprint(fp, "{");
|
|
if(fp->flags & FmtLong)
|
|
for(t1=t->type; t1!=T; t1=t1->down)
|
|
fmtprint(fp, "%lT;", t1);
|
|
fmtprint(fp, "}");
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
fmtprint(fp, "{");
|
|
if(fp->flags & FmtLong)
|
|
for(t1=t->type; t1!=T; t1=t1->down)
|
|
fmtprint(fp, "%lT;", t1);
|
|
fmtprint(fp, "}");
|
|
break;
|
|
|
|
case TMAP:
|
|
fmtprint(fp, "[%T]%T", t->down, t->type);
|
|
break;
|
|
|
|
case TARRAY:
|
|
if(t->bound >= 0)
|
|
fmtprint(fp, "[%d]%T", t->bound, t->type);
|
|
else
|
|
fmtprint(fp, "[]%T", t->type);
|
|
break;
|
|
|
|
case TPTR32:
|
|
case TPTR64:
|
|
fmtprint(fp, "%T", t->type);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
t->trecur--;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Nconv(Fmt *fp)
|
|
{
|
|
char buf1[500];
|
|
Node *n;
|
|
|
|
n = va_arg(fp->args, Node*);
|
|
if(n == N) {
|
|
fmtprint(fp, "<N>");
|
|
goto out;
|
|
}
|
|
|
|
if(fp->flags & FmtSign) {
|
|
if(n->type == T)
|
|
fmtprint(fp, "%#N", n);
|
|
else if(n->type->etype == TNIL)
|
|
fmtprint(fp, "nil");
|
|
else
|
|
fmtprint(fp, "%#N (type %T)", n, n->type);
|
|
goto out;
|
|
}
|
|
|
|
if(fp->flags & FmtSharp) {
|
|
if(n->orig != N)
|
|
n = n->orig;
|
|
exprfmt(fp, n, 0);
|
|
goto out;
|
|
}
|
|
|
|
switch(n->op) {
|
|
default:
|
|
if (fp->flags & FmtShort)
|
|
fmtprint(fp, "%O%hJ", n->op, n);
|
|
else
|
|
fmtprint(fp, "%O%J", n->op, n);
|
|
break;
|
|
|
|
case ONAME:
|
|
case ONONAME:
|
|
if(n->sym == S) {
|
|
if (fp->flags & FmtShort)
|
|
fmtprint(fp, "%O%hJ", n->op, n);
|
|
else
|
|
fmtprint(fp, "%O%J", n->op, n);
|
|
break;
|
|
}
|
|
if (fp->flags & FmtShort)
|
|
fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
|
|
else
|
|
fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
|
|
goto ptyp;
|
|
|
|
case OREGISTER:
|
|
fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
|
|
break;
|
|
|
|
case OLITERAL:
|
|
switch(n->val.ctype) {
|
|
default:
|
|
snprint(buf1, sizeof(buf1), "LITERAL-ctype=%d", n->val.ctype);
|
|
break;
|
|
case CTINT:
|
|
snprint(buf1, sizeof(buf1), "I%B", n->val.u.xval);
|
|
break;
|
|
case CTFLT:
|
|
snprint(buf1, sizeof(buf1), "F%g", mpgetflt(n->val.u.fval));
|
|
break;
|
|
case CTCPLX:
|
|
snprint(buf1, sizeof(buf1), "(F%g+F%gi)",
|
|
mpgetflt(&n->val.u.cval->real),
|
|
mpgetflt(&n->val.u.cval->imag));
|
|
break;
|
|
case CTSTR:
|
|
snprint(buf1, sizeof(buf1), "S\"%Z\"", n->val.u.sval);
|
|
break;
|
|
case CTBOOL:
|
|
snprint(buf1, sizeof(buf1), "B%d", n->val.u.bval);
|
|
break;
|
|
case CTNIL:
|
|
snprint(buf1, sizeof(buf1), "N");
|
|
break;
|
|
}
|
|
fmtprint(fp, "%O-%s%J", n->op, buf1, n);
|
|
break;
|
|
|
|
case OASOP:
|
|
fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
|
|
break;
|
|
|
|
case OTYPE:
|
|
fmtprint(fp, "%O %T", n->op, n->type);
|
|
break;
|
|
}
|
|
if(n->sym != S)
|
|
fmtprint(fp, " %S G%d", n->sym, n->vargen);
|
|
|
|
ptyp:
|
|
if(n->type != T)
|
|
fmtprint(fp, " %T", n->type);
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
Node*
|
|
treecopy(Node *n)
|
|
{
|
|
Node *m;
|
|
|
|
if(n == N)
|
|
return N;
|
|
|
|
switch(n->op) {
|
|
default:
|
|
m = nod(OXXX, N, N);
|
|
*m = *n;
|
|
m->left = treecopy(n->left);
|
|
m->right = treecopy(n->right);
|
|
m->list = listtreecopy(n->list);
|
|
if(m->defn)
|
|
abort();
|
|
break;
|
|
|
|
case ONONAME:
|
|
if(n->sym == lookup("iota")) {
|
|
// Not sure yet whether this is the real iota,
|
|
// but make a copy of the Node* just in case,
|
|
// so that all the copies of this const definition
|
|
// don't have the same iota value.
|
|
m = nod(OXXX, N, N);
|
|
*m = *n;
|
|
m->iota = iota;
|
|
break;
|
|
}
|
|
// fall through
|
|
case ONAME:
|
|
case OLITERAL:
|
|
case OTYPE:
|
|
m = n;
|
|
break;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
int
|
|
Zconv(Fmt *fp)
|
|
{
|
|
Rune r;
|
|
Strlit *sp;
|
|
char *s, *se;
|
|
int n;
|
|
|
|
sp = va_arg(fp->args, Strlit*);
|
|
if(sp == nil)
|
|
return fmtstrcpy(fp, "<nil>");
|
|
|
|
s = sp->s;
|
|
se = s + sp->len;
|
|
while(s < se) {
|
|
n = chartorune(&r, s);
|
|
s += n;
|
|
switch(r) {
|
|
case Runeerror:
|
|
if(n == 1) {
|
|
fmtprint(fp, "\\x%02x", (uchar)*(s-1));
|
|
break;
|
|
}
|
|
// fall through
|
|
default:
|
|
if(r < ' ') {
|
|
fmtprint(fp, "\\x%02x", r);
|
|
break;
|
|
}
|
|
fmtrune(fp, r);
|
|
break;
|
|
case '\t':
|
|
fmtstrcpy(fp, "\\t");
|
|
break;
|
|
case '\n':
|
|
fmtstrcpy(fp, "\\n");
|
|
break;
|
|
case '\"':
|
|
case '\\':
|
|
fmtrune(fp, '\\');
|
|
fmtrune(fp, r);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
isnil(Node *n)
|
|
{
|
|
if(n == N)
|
|
return 0;
|
|
if(n->op != OLITERAL)
|
|
return 0;
|
|
if(n->val.ctype != CTNIL)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
isptrto(Type *t, int et)
|
|
{
|
|
if(t == T)
|
|
return 0;
|
|
if(!isptr[t->etype])
|
|
return 0;
|
|
t = t->type;
|
|
if(t == T)
|
|
return 0;
|
|
if(t->etype != et)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
istype(Type *t, int et)
|
|
{
|
|
return t != T && t->etype == et;
|
|
}
|
|
|
|
int
|
|
isfixedarray(Type *t)
|
|
{
|
|
return t != T && t->etype == TARRAY && t->bound >= 0;
|
|
}
|
|
|
|
int
|
|
isslice(Type *t)
|
|
{
|
|
return t != T && t->etype == TARRAY && t->bound < 0;
|
|
}
|
|
|
|
int
|
|
isblank(Node *n)
|
|
{
|
|
char *p;
|
|
|
|
if(n == N || n->sym == S)
|
|
return 0;
|
|
p = n->sym->name;
|
|
if(p == nil)
|
|
return 0;
|
|
return p[0] == '_' && p[1] == '\0';
|
|
}
|
|
|
|
int
|
|
isinter(Type *t)
|
|
{
|
|
return t != T && t->etype == TINTER;
|
|
}
|
|
|
|
int
|
|
isnilinter(Type *t)
|
|
{
|
|
if(!isinter(t))
|
|
return 0;
|
|
if(t->type != T)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
isideal(Type *t)
|
|
{
|
|
if(t == T)
|
|
return 0;
|
|
if(t == idealstring || t == idealbool)
|
|
return 1;
|
|
switch(t->etype) {
|
|
case TNIL:
|
|
case TIDEAL:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* given receiver of type t (t == r or t == *r)
|
|
* return type to hang methods off (r).
|
|
*/
|
|
Type*
|
|
methtype(Type *t)
|
|
{
|
|
if(t == T)
|
|
return T;
|
|
|
|
// strip away pointer if it's there
|
|
if(isptr[t->etype]) {
|
|
if(t->sym != S)
|
|
return T;
|
|
t = t->type;
|
|
if(t == T)
|
|
return T;
|
|
}
|
|
|
|
// need a type name
|
|
if(t->sym == S)
|
|
return T;
|
|
|
|
// check types
|
|
if(!issimple[t->etype])
|
|
switch(t->etype) {
|
|
default:
|
|
return T;
|
|
case TSTRUCT:
|
|
case TARRAY:
|
|
case TMAP:
|
|
case TCHAN:
|
|
case TSTRING:
|
|
case TFUNC:
|
|
break;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
int
|
|
cplxsubtype(int et)
|
|
{
|
|
switch(et) {
|
|
case TCOMPLEX64:
|
|
return TFLOAT32;
|
|
case TCOMPLEX128:
|
|
return TFLOAT64;
|
|
}
|
|
fatal("cplxsubtype: %E\n", et);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
eqnote(Strlit *a, Strlit *b)
|
|
{
|
|
if(a == b)
|
|
return 1;
|
|
if(a == nil || b == nil)
|
|
return 0;
|
|
if(a->len != b->len)
|
|
return 0;
|
|
return memcmp(a->s, b->s, a->len) == 0;
|
|
}
|
|
|
|
// Return 1 if t1 and t2 are identical, following the spec rules.
|
|
//
|
|
// Any cyclic type must go through a named type, and if one is
|
|
// named, it is only identical to the other if they are the same
|
|
// pointer (t1 == t2), so there's no chance of chasing cycles
|
|
// ad infinitum, so no need for a depth counter.
|
|
int
|
|
eqtype(Type *t1, Type *t2)
|
|
{
|
|
if(t1 == t2)
|
|
return 1;
|
|
if(t1 == T || t2 == T || t1->etype != t2->etype || t1->sym || t2->sym)
|
|
return 0;
|
|
|
|
switch(t1->etype) {
|
|
case TINTER:
|
|
case TSTRUCT:
|
|
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
|
|
if(t1->etype != TFIELD || t2->etype != TFIELD)
|
|
fatal("struct/interface missing field: %T %T", t1, t2);
|
|
if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype(t1->type, t2->type) || !eqnote(t1->note, t2->note))
|
|
return 0;
|
|
}
|
|
return t1 == T && t2 == T;
|
|
|
|
case TFUNC:
|
|
// Loop over structs: receiver, in, out.
|
|
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
|
|
Type *ta, *tb;
|
|
|
|
if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
|
|
fatal("func missing struct: %T %T", t1, t2);
|
|
|
|
// Loop over fields in structs, ignoring argument names.
|
|
for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
|
|
if(ta->etype != TFIELD || tb->etype != TFIELD)
|
|
fatal("func struct missing field: %T %T", ta, tb);
|
|
if(ta->isddd != tb->isddd || !eqtype(ta->type, tb->type))
|
|
return 0;
|
|
}
|
|
if(ta != T || tb != T)
|
|
return 0;
|
|
}
|
|
return t1 == T && t2 == T;
|
|
|
|
case TARRAY:
|
|
if(t1->bound != t2->bound)
|
|
return 0;
|
|
break;
|
|
|
|
case TCHAN:
|
|
if(t1->chan != t2->chan)
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
return eqtype(t1->down, t2->down) && eqtype(t1->type, t2->type);
|
|
}
|
|
|
|
// Are t1 and t2 equal struct types when field names are ignored?
|
|
// For deciding whether the result struct from g can be copied
|
|
// directly when compiling f(g()).
|
|
int
|
|
eqtypenoname(Type *t1, Type *t2)
|
|
{
|
|
if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT)
|
|
return 0;
|
|
|
|
t1 = t1->type;
|
|
t2 = t2->type;
|
|
for(;;) {
|
|
if(!eqtype(t1, t2))
|
|
return 0;
|
|
if(t1 == T)
|
|
return 1;
|
|
t1 = t1->down;
|
|
t2 = t2->down;
|
|
}
|
|
}
|
|
|
|
// Is type src assignment compatible to type dst?
|
|
// If so, return op code to use in conversion.
|
|
// If not, return 0.
|
|
//
|
|
// It is the caller's responsibility to call exportassignok
|
|
// to check for assignments to other packages' unexported fields,
|
|
int
|
|
assignop(Type *src, Type *dst, char **why)
|
|
{
|
|
Type *missing, *have;
|
|
int ptr;
|
|
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
if(safemode && src != T && src->etype == TUNSAFEPTR) {
|
|
yyerror("cannot use unsafe.Pointer");
|
|
errorexit();
|
|
}
|
|
|
|
if(src == dst)
|
|
return OCONVNOP;
|
|
if(src == T || dst == T || src->etype == TFORW || dst->etype == TFORW || src->orig == T || dst->orig == T)
|
|
return 0;
|
|
|
|
// 1. src type is identical to dst.
|
|
if(eqtype(src, dst))
|
|
return OCONVNOP;
|
|
|
|
// 2. src and dst have identical underlying types
|
|
// and either src or dst is not a named type or
|
|
// both are interface types.
|
|
if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER))
|
|
return OCONVNOP;
|
|
|
|
// 3. dst is an interface type and src implements dst.
|
|
if(dst->etype == TINTER && src->etype != TNIL) {
|
|
if(implements(src, dst, &missing, &have, &ptr))
|
|
return OCONVIFACE;
|
|
if(why != nil) {
|
|
if(isptrto(src, TINTER))
|
|
*why = smprint(":\n\t%T is pointer to interface, not interface", src);
|
|
else if(have && have->sym == missing->sym)
|
|
*why = smprint(":\n\t%T does not implement %T (wrong type for %S method)\n"
|
|
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
|
|
have->sym, have->type, missing->sym, missing->type);
|
|
else if(ptr)
|
|
*why = smprint(":\n\t%T does not implement %T (%S method requires pointer receiver)",
|
|
src, dst, missing->sym);
|
|
else if(have)
|
|
*why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
|
|
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
|
|
have->sym, have->type, missing->sym, missing->type);
|
|
else
|
|
*why = smprint(":\n\t%T does not implement %T (missing %S method)",
|
|
src, dst, missing->sym);
|
|
}
|
|
return 0;
|
|
}
|
|
if(isptrto(dst, TINTER)) {
|
|
if(why != nil)
|
|
*why = smprint(":\n\t%T is pointer to interface, not interface", dst);
|
|
return 0;
|
|
}
|
|
if(src->etype == TINTER && dst->etype != TBLANK) {
|
|
if(why != nil)
|
|
*why = ": need type assertion";
|
|
return 0;
|
|
}
|
|
|
|
// 4. src is a bidirectional channel value, dst is a channel type,
|
|
// src and dst have identical element types, and
|
|
// either src or dst is not a named type.
|
|
if(src->etype == TCHAN && src->chan == Cboth && dst->etype == TCHAN)
|
|
if(eqtype(src->type, dst->type) && (src->sym == S || dst->sym == S))
|
|
return OCONVNOP;
|
|
|
|
// 5. src is the predeclared identifier nil and dst is a nillable type.
|
|
if(src->etype == TNIL) {
|
|
switch(dst->etype) {
|
|
case TARRAY:
|
|
if(dst->bound != -100) // not slice
|
|
break;
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TFUNC:
|
|
case TMAP:
|
|
case TCHAN:
|
|
case TINTER:
|
|
return OCONVNOP;
|
|
}
|
|
}
|
|
|
|
// 6. rule about untyped constants - already converted by defaultlit.
|
|
|
|
// 7. Any typed value can be assigned to the blank identifier.
|
|
if(dst->etype == TBLANK)
|
|
return OCONVNOP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Can we convert a value of type src to a value of type dst?
|
|
// If so, return op code to use in conversion (maybe OCONVNOP).
|
|
// If not, return 0.
|
|
int
|
|
convertop(Type *src, Type *dst, char **why)
|
|
{
|
|
int op;
|
|
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
if(src == dst)
|
|
return OCONVNOP;
|
|
if(src == T || dst == T)
|
|
return 0;
|
|
|
|
// 1. src can be assigned to dst.
|
|
if((op = assignop(src, dst, why)) != 0)
|
|
return op;
|
|
|
|
// The rules for interfaces are no different in conversions
|
|
// than assignments. If interfaces are involved, stop now
|
|
// with the good message from assignop.
|
|
// Otherwise clear the error.
|
|
if(src->etype == TINTER || dst->etype == TINTER)
|
|
return 0;
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
// 2. src and dst have identical underlying types.
|
|
if(eqtype(src->orig, dst->orig))
|
|
return OCONVNOP;
|
|
|
|
// 3. src and dst are unnamed pointer types
|
|
// and their base types have identical underlying types.
|
|
if(isptr[src->etype] && isptr[dst->etype] && src->sym == S && dst->sym == S)
|
|
if(eqtype(src->type->orig, dst->type->orig))
|
|
return OCONVNOP;
|
|
|
|
// 4. src and dst are both integer or floating point types.
|
|
if((isint[src->etype] || isfloat[src->etype]) && (isint[dst->etype] || isfloat[dst->etype])) {
|
|
if(simtype[src->etype] == simtype[dst->etype])
|
|
return OCONVNOP;
|
|
return OCONV;
|
|
}
|
|
|
|
// 5. src and dst are both complex types.
|
|
if(iscomplex[src->etype] && iscomplex[dst->etype]) {
|
|
if(simtype[src->etype] == simtype[dst->etype])
|
|
return OCONVNOP;
|
|
return OCONV;
|
|
}
|
|
|
|
// 6. src is an integer or has type []byte or []int
|
|
// and dst is a string type.
|
|
if(isint[src->etype] && dst->etype == TSTRING)
|
|
return ORUNESTR;
|
|
|
|
if(isslice(src) && src->sym == nil && src->type == types[src->type->etype] && dst->etype == TSTRING) {
|
|
switch(src->type->etype) {
|
|
case TUINT8:
|
|
return OARRAYBYTESTR;
|
|
case TINT:
|
|
return OARRAYRUNESTR;
|
|
}
|
|
}
|
|
|
|
// 7. src is a string and dst is []byte or []int.
|
|
// String to slice.
|
|
if(src->etype == TSTRING && isslice(dst) && dst->sym == nil && dst->type == types[dst->type->etype]) {
|
|
switch(dst->type->etype) {
|
|
case TUINT8:
|
|
return OSTRARRAYBYTE;
|
|
case TINT:
|
|
return OSTRARRAYRUNE;
|
|
}
|
|
}
|
|
|
|
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
|
|
if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR)
|
|
return OCONVNOP;
|
|
|
|
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
|
|
if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR))
|
|
return OCONVNOP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Convert node n for assignment to type t.
|
|
Node*
|
|
assignconv(Node *n, Type *t, char *context)
|
|
{
|
|
int op;
|
|
Node *r, *old;
|
|
char *why;
|
|
|
|
if(n == N || n->type == T)
|
|
return n;
|
|
|
|
old = n;
|
|
old->diag++; // silence errors about n; we'll issue one below
|
|
defaultlit(&n, t);
|
|
old->diag--;
|
|
if(t->etype == TBLANK)
|
|
return n;
|
|
|
|
exportassignok(n->type, context);
|
|
if(eqtype(n->type, t))
|
|
return n;
|
|
|
|
op = assignop(n->type, t, &why);
|
|
if(op == 0) {
|
|
yyerror("cannot use %+N as type %T in %s%s", n, t, context, why);
|
|
op = OCONV;
|
|
}
|
|
|
|
r = nod(op, n, N);
|
|
r->type = t;
|
|
r->typecheck = 1;
|
|
r->implicit = 1;
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
subtype(Type **stp, Type *t, int d)
|
|
{
|
|
Type *st;
|
|
|
|
loop:
|
|
st = *stp;
|
|
if(st == T)
|
|
return 0;
|
|
|
|
d++;
|
|
if(d >= 10)
|
|
return 0;
|
|
|
|
switch(st->etype) {
|
|
default:
|
|
return 0;
|
|
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TCHAN:
|
|
case TARRAY:
|
|
stp = &st->type;
|
|
goto loop;
|
|
|
|
case TANY:
|
|
if(!st->copyany)
|
|
return 0;
|
|
*stp = t;
|
|
break;
|
|
|
|
case TMAP:
|
|
if(subtype(&st->down, t, d))
|
|
break;
|
|
stp = &st->type;
|
|
goto loop;
|
|
|
|
case TFUNC:
|
|
for(;;) {
|
|
if(subtype(&st->type, t, d))
|
|
break;
|
|
if(subtype(&st->type->down->down, t, d))
|
|
break;
|
|
if(subtype(&st->type->down, t, d))
|
|
break;
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
for(st=st->type; st!=T; st=st->down)
|
|
if(subtype(&st->type, t, d))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Is this a 64-bit type?
|
|
*/
|
|
int
|
|
is64(Type *t)
|
|
{
|
|
if(t == T)
|
|
return 0;
|
|
switch(simtype[t->etype]) {
|
|
case TINT64:
|
|
case TUINT64:
|
|
case TPTR64:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Is a conversion between t1 and t2 a no-op?
|
|
*/
|
|
int
|
|
noconv(Type *t1, Type *t2)
|
|
{
|
|
int e1, e2;
|
|
|
|
e1 = simtype[t1->etype];
|
|
e2 = simtype[t2->etype];
|
|
|
|
switch(e1) {
|
|
case TINT8:
|
|
case TUINT8:
|
|
return e2 == TINT8 || e2 == TUINT8;
|
|
|
|
case TINT16:
|
|
case TUINT16:
|
|
return e2 == TINT16 || e2 == TUINT16;
|
|
|
|
case TINT32:
|
|
case TUINT32:
|
|
case TPTR32:
|
|
return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32;
|
|
|
|
case TINT64:
|
|
case TUINT64:
|
|
case TPTR64:
|
|
return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64;
|
|
|
|
case TFLOAT32:
|
|
return e2 == TFLOAT32;
|
|
|
|
case TFLOAT64:
|
|
return e2 == TFLOAT64;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
argtype(Node *on, Type *t)
|
|
{
|
|
dowidth(t);
|
|
if(!subtype(&on->type, t, 0))
|
|
fatal("argtype: failed %N %T\n", on, t);
|
|
}
|
|
|
|
Type*
|
|
shallow(Type *t)
|
|
{
|
|
Type *nt;
|
|
|
|
if(t == T)
|
|
return T;
|
|
nt = typ(0);
|
|
*nt = *t;
|
|
if(t->orig == t)
|
|
nt->orig = nt;
|
|
return nt;
|
|
}
|
|
|
|
static Type*
|
|
deep(Type *t)
|
|
{
|
|
Type *nt, *xt;
|
|
|
|
if(t == T)
|
|
return T;
|
|
|
|
switch(t->etype) {
|
|
default:
|
|
nt = t; // share from here down
|
|
break;
|
|
|
|
case TANY:
|
|
nt = shallow(t);
|
|
nt->copyany = 1;
|
|
break;
|
|
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TCHAN:
|
|
case TARRAY:
|
|
nt = shallow(t);
|
|
nt->type = deep(t->type);
|
|
break;
|
|
|
|
case TMAP:
|
|
nt = shallow(t);
|
|
nt->down = deep(t->down);
|
|
nt->type = deep(t->type);
|
|
break;
|
|
|
|
case TFUNC:
|
|
nt = shallow(t);
|
|
nt->type = deep(t->type);
|
|
nt->type->down = deep(t->type->down);
|
|
nt->type->down->down = deep(t->type->down->down);
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
nt = shallow(t);
|
|
nt->type = shallow(t->type);
|
|
xt = nt->type;
|
|
|
|
for(t=t->type; t!=T; t=t->down) {
|
|
xt->type = deep(t->type);
|
|
xt->down = shallow(t->down);
|
|
xt = xt->down;
|
|
}
|
|
break;
|
|
}
|
|
return nt;
|
|
}
|
|
|
|
Node*
|
|
syslook(char *name, int copy)
|
|
{
|
|
Sym *s;
|
|
Node *n;
|
|
|
|
s = pkglookup(name, runtimepkg);
|
|
if(s == S || s->def == N)
|
|
fatal("syslook: can't find runtime.%s", name);
|
|
|
|
if(!copy)
|
|
return s->def;
|
|
|
|
n = nod(0, N, N);
|
|
*n = *s->def;
|
|
n->type = deep(s->def->type);
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* compute a hash value for type t.
|
|
* if t is a method type, ignore the receiver
|
|
* so that the hash can be used in interface checks.
|
|
* %-T (which calls Tpretty, above) already contains
|
|
* all the necessary logic to generate a representation
|
|
* of the type that completely describes it.
|
|
* using smprint here avoids duplicating that code.
|
|
* using md5 here is overkill, but i got tired of
|
|
* accidental collisions making the runtime think
|
|
* two types are equal when they really aren't.
|
|
*/
|
|
uint32
|
|
typehash(Type *t)
|
|
{
|
|
char *p;
|
|
MD5 d;
|
|
|
|
longsymnames = 1;
|
|
if(t->thistuple) {
|
|
// hide method receiver from Tpretty
|
|
t->thistuple = 0;
|
|
p = smprint("%-T", t);
|
|
t->thistuple = 1;
|
|
}else
|
|
p = smprint("%-T", t);
|
|
longsymnames = 0;
|
|
md5reset(&d);
|
|
md5write(&d, (uchar*)p, strlen(p));
|
|
free(p);
|
|
return md5sum(&d);
|
|
}
|
|
|
|
Type*
|
|
ptrto(Type *t)
|
|
{
|
|
Type *t1;
|
|
|
|
if(tptr == 0)
|
|
fatal("ptrto: nil");
|
|
t1 = typ(tptr);
|
|
t1->type = t;
|
|
t1->width = widthptr;
|
|
t1->align = widthptr;
|
|
return t1;
|
|
}
|
|
|
|
void
|
|
frame(int context)
|
|
{
|
|
char *p;
|
|
NodeList *l;
|
|
Node *n;
|
|
int flag;
|
|
|
|
p = "stack";
|
|
l = nil;
|
|
if(curfn)
|
|
l = curfn->dcl;
|
|
if(context) {
|
|
p = "external";
|
|
l = externdcl;
|
|
}
|
|
|
|
flag = 1;
|
|
for(; l; l=l->next) {
|
|
n = l->n;
|
|
switch(n->op) {
|
|
case ONAME:
|
|
if(flag)
|
|
print("--- %s frame ---\n", p);
|
|
print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type);
|
|
flag = 0;
|
|
break;
|
|
|
|
case OTYPE:
|
|
if(flag)
|
|
print("--- %s frame ---\n", p);
|
|
print("%O %T\n", n->op, n->type);
|
|
flag = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* calculate sethi/ullman number
|
|
* roughly how many registers needed to
|
|
* compile a node. used to compile the
|
|
* hardest side first to minimize registers.
|
|
*/
|
|
void
|
|
ullmancalc(Node *n)
|
|
{
|
|
int ul, ur;
|
|
|
|
if(n == N)
|
|
return;
|
|
|
|
switch(n->op) {
|
|
case OREGISTER:
|
|
case OLITERAL:
|
|
case ONAME:
|
|
ul = 1;
|
|
if(n->class == PPARAMREF || (n->class & PHEAP))
|
|
ul++;
|
|
goto out;
|
|
case OCALL:
|
|
case OCALLFUNC:
|
|
case OCALLMETH:
|
|
case OCALLINTER:
|
|
ul = UINF;
|
|
goto out;
|
|
}
|
|
ul = 1;
|
|
if(n->left != N)
|
|
ul = n->left->ullman;
|
|
ur = 1;
|
|
if(n->right != N)
|
|
ur = n->right->ullman;
|
|
if(ul == ur)
|
|
ul += 1;
|
|
if(ur > ul)
|
|
ul = ur;
|
|
|
|
out:
|
|
n->ullman = ul;
|
|
}
|
|
|
|
void
|
|
badtype(int o, Type *tl, Type *tr)
|
|
{
|
|
Fmt fmt;
|
|
char *s;
|
|
|
|
fmtstrinit(&fmt);
|
|
if(tl != T)
|
|
fmtprint(&fmt, "\n %T", tl);
|
|
if(tr != T)
|
|
fmtprint(&fmt, "\n %T", tr);
|
|
|
|
// common mistake: *struct and *interface.
|
|
if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) {
|
|
if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER)
|
|
fmtprint(&fmt, "\n (*struct vs *interface)");
|
|
else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT)
|
|
fmtprint(&fmt, "\n (*interface vs *struct)");
|
|
}
|
|
s = fmtstrflush(&fmt);
|
|
yyerror("illegal types for operand: %O%s", o, s);
|
|
}
|
|
|
|
/*
|
|
* iterator to walk a structure declaration
|
|
*/
|
|
Type*
|
|
structfirst(Iter *s, Type **nn)
|
|
{
|
|
Type *n, *t;
|
|
|
|
n = *nn;
|
|
if(n == T)
|
|
goto bad;
|
|
|
|
switch(n->etype) {
|
|
default:
|
|
goto bad;
|
|
|
|
case TSTRUCT:
|
|
case TINTER:
|
|
case TFUNC:
|
|
break;
|
|
}
|
|
|
|
t = n->type;
|
|
if(t == T)
|
|
goto rnil;
|
|
|
|
if(t->etype != TFIELD)
|
|
fatal("structfirst: not field %T", t);
|
|
|
|
s->t = t;
|
|
return t;
|
|
|
|
bad:
|
|
fatal("structfirst: not struct %T", n);
|
|
|
|
rnil:
|
|
return T;
|
|
}
|
|
|
|
Type*
|
|
structnext(Iter *s)
|
|
{
|
|
Type *n, *t;
|
|
|
|
n = s->t;
|
|
t = n->down;
|
|
if(t == T)
|
|
goto rnil;
|
|
|
|
if(t->etype != TFIELD)
|
|
goto bad;
|
|
|
|
s->t = t;
|
|
return t;
|
|
|
|
bad:
|
|
fatal("structnext: not struct %T", n);
|
|
|
|
rnil:
|
|
return T;
|
|
}
|
|
|
|
/*
|
|
* iterator to this and inargs in a function
|
|
*/
|
|
Type*
|
|
funcfirst(Iter *s, Type *t)
|
|
{
|
|
Type *fp;
|
|
|
|
if(t == T)
|
|
goto bad;
|
|
|
|
if(t->etype != TFUNC)
|
|
goto bad;
|
|
|
|
s->tfunc = t;
|
|
s->done = 0;
|
|
fp = structfirst(s, getthis(t));
|
|
if(fp == T) {
|
|
s->done = 1;
|
|
fp = structfirst(s, getinarg(t));
|
|
}
|
|
return fp;
|
|
|
|
bad:
|
|
fatal("funcfirst: not func %T", t);
|
|
return T;
|
|
}
|
|
|
|
Type*
|
|
funcnext(Iter *s)
|
|
{
|
|
Type *fp;
|
|
|
|
fp = structnext(s);
|
|
if(fp == T && !s->done) {
|
|
s->done = 1;
|
|
fp = structfirst(s, getinarg(s->tfunc));
|
|
}
|
|
return fp;
|
|
}
|
|
|
|
Type**
|
|
getthis(Type *t)
|
|
{
|
|
if(t->etype != TFUNC)
|
|
fatal("getthis: not a func %T", t);
|
|
return &t->type;
|
|
}
|
|
|
|
Type**
|
|
getoutarg(Type *t)
|
|
{
|
|
if(t->etype != TFUNC)
|
|
fatal("getoutarg: not a func %T", t);
|
|
return &t->type->down;
|
|
}
|
|
|
|
Type**
|
|
getinarg(Type *t)
|
|
{
|
|
if(t->etype != TFUNC)
|
|
fatal("getinarg: not a func %T", t);
|
|
return &t->type->down->down;
|
|
}
|
|
|
|
Type*
|
|
getthisx(Type *t)
|
|
{
|
|
return *getthis(t);
|
|
}
|
|
|
|
Type*
|
|
getoutargx(Type *t)
|
|
{
|
|
return *getoutarg(t);
|
|
}
|
|
|
|
Type*
|
|
getinargx(Type *t)
|
|
{
|
|
return *getinarg(t);
|
|
}
|
|
|
|
/*
|
|
* return !(op)
|
|
* eg == <=> !=
|
|
*/
|
|
int
|
|
brcom(int a)
|
|
{
|
|
switch(a) {
|
|
case OEQ: return ONE;
|
|
case ONE: return OEQ;
|
|
case OLT: return OGE;
|
|
case OGT: return OLE;
|
|
case OLE: return OGT;
|
|
case OGE: return OLT;
|
|
}
|
|
fatal("brcom: no com for %A\n", a);
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
* return reverse(op)
|
|
* eg a op b <=> b r(op) a
|
|
*/
|
|
int
|
|
brrev(int a)
|
|
{
|
|
switch(a) {
|
|
case OEQ: return OEQ;
|
|
case ONE: return ONE;
|
|
case OLT: return OGT;
|
|
case OGT: return OLT;
|
|
case OLE: return OGE;
|
|
case OGE: return OLE;
|
|
}
|
|
fatal("brcom: no rev for %A\n", a);
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
* return side effect-free n, appending side effects to init.
|
|
* result is assignable if n is.
|
|
*/
|
|
Node*
|
|
safeexpr(Node *n, NodeList **init)
|
|
{
|
|
Node *l;
|
|
Node *r;
|
|
Node *a;
|
|
|
|
if(n == N)
|
|
return N;
|
|
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OLITERAL:
|
|
return n;
|
|
|
|
case ODOT:
|
|
l = safeexpr(n->left, init);
|
|
if(l == n->left)
|
|
return n;
|
|
r = nod(OXXX, N, N);
|
|
*r = *n;
|
|
r->left = l;
|
|
typecheck(&r, Erv);
|
|
walkexpr(&r, init);
|
|
return r;
|
|
|
|
case ODOTPTR:
|
|
case OIND:
|
|
l = safeexpr(n->left, init);
|
|
if(l == n->left)
|
|
return n;
|
|
a = nod(OXXX, N, N);
|
|
*a = *n;
|
|
a->left = l;
|
|
walkexpr(&a, init);
|
|
return a;
|
|
|
|
case OINDEX:
|
|
case OINDEXMAP:
|
|
l = safeexpr(n->left, init);
|
|
r = safeexpr(n->right, init);
|
|
if(l == n->left && r == n->right)
|
|
return n;
|
|
a = nod(OXXX, N, N);
|
|
*a = *n;
|
|
a->left = l;
|
|
a->right = r;
|
|
walkexpr(&a, init);
|
|
return a;
|
|
}
|
|
|
|
// make a copy; must not be used as an lvalue
|
|
if(islvalue(n))
|
|
fatal("missing lvalue case in safeexpr: %N", n);
|
|
return cheapexpr(n, init);
|
|
}
|
|
|
|
static Node*
|
|
copyexpr(Node *n, Type *t, NodeList **init)
|
|
{
|
|
Node *a, *l;
|
|
|
|
l = nod(OXXX, N, N);
|
|
tempname(l, t);
|
|
a = nod(OAS, l, n);
|
|
typecheck(&a, Etop);
|
|
walkexpr(&a, init);
|
|
*init = list(*init, a);
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
* return side-effect free and cheap n, appending side effects to init.
|
|
* result may not be assignable.
|
|
*/
|
|
Node*
|
|
cheapexpr(Node *n, NodeList **init)
|
|
{
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OLITERAL:
|
|
return n;
|
|
}
|
|
|
|
return copyexpr(n, n->type, init);
|
|
}
|
|
|
|
/*
|
|
* return n in a local variable of type t if it is not already.
|
|
*/
|
|
Node*
|
|
localexpr(Node *n, Type *t, NodeList **init)
|
|
{
|
|
if(n->op == ONAME &&
|
|
(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
|
|
convertop(n->type, t, nil) == OCONVNOP)
|
|
return n;
|
|
|
|
return copyexpr(n, t, init);
|
|
}
|
|
|
|
void
|
|
setmaxarg(Type *t)
|
|
{
|
|
int32 w;
|
|
|
|
dowidth(t);
|
|
w = t->argwid;
|
|
if(t->argwid >= MAXWIDTH)
|
|
fatal("bad argwid %T", t);
|
|
if(w > maxarg)
|
|
maxarg = w;
|
|
}
|
|
|
|
/* unicode-aware case-insensitive strcmp */
|
|
|
|
static int
|
|
cistrcmp(char *p, char *q)
|
|
{
|
|
Rune rp, rq;
|
|
|
|
while(*p || *q) {
|
|
if(*p == 0)
|
|
return +1;
|
|
if(*q == 0)
|
|
return -1;
|
|
p += chartorune(&rp, p);
|
|
q += chartorune(&rq, q);
|
|
rp = tolowerrune(rp);
|
|
rq = tolowerrune(rq);
|
|
if(rp < rq)
|
|
return -1;
|
|
if(rp > rq)
|
|
return +1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* code to resolve elided DOTs
|
|
* in embedded types
|
|
*/
|
|
|
|
// search depth 0 --
|
|
// return count of fields+methods
|
|
// found with a given name
|
|
static int
|
|
lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
|
|
{
|
|
Type *f, *u;
|
|
int c;
|
|
|
|
u = t;
|
|
if(isptr[u->etype])
|
|
u = u->type;
|
|
|
|
c = 0;
|
|
if(u->etype == TSTRUCT || u->etype == TINTER) {
|
|
for(f=u->type; f!=T; f=f->down)
|
|
if(f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0)) {
|
|
if(save)
|
|
*save = f;
|
|
c++;
|
|
}
|
|
}
|
|
u = methtype(t);
|
|
if(u != T) {
|
|
for(f=u->method; f!=T; f=f->down)
|
|
if(f->embedded == 0 && (f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0))) {
|
|
if(save)
|
|
*save = f;
|
|
c++;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// search depth d --
|
|
// return count of fields+methods
|
|
// found at search depth.
|
|
// answer is in dotlist array and
|
|
// count of number of ways is returned.
|
|
int
|
|
adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase)
|
|
{
|
|
Type *f, *u;
|
|
int c, a;
|
|
|
|
if(t->trecur)
|
|
return 0;
|
|
t->trecur = 1;
|
|
|
|
if(d == 0) {
|
|
c = lookdot0(s, t, save, ignorecase);
|
|
goto out;
|
|
}
|
|
|
|
c = 0;
|
|
u = t;
|
|
if(isptr[u->etype])
|
|
u = u->type;
|
|
if(u->etype != TSTRUCT && u->etype != TINTER)
|
|
goto out;
|
|
|
|
d--;
|
|
for(f=u->type; f!=T; f=f->down) {
|
|
if(!f->embedded)
|
|
continue;
|
|
if(f->sym == S)
|
|
continue;
|
|
a = adddot1(s, f->type, d, save, ignorecase);
|
|
if(a != 0 && c == 0)
|
|
dotlist[d].field = f;
|
|
c += a;
|
|
}
|
|
|
|
out:
|
|
t->trecur = 0;
|
|
return c;
|
|
}
|
|
|
|
// in T.field
|
|
// find missing fields that
|
|
// will give shortest unique addressing.
|
|
// modify the tree with missing type names.
|
|
Node*
|
|
adddot(Node *n)
|
|
{
|
|
Type *t;
|
|
Sym *s;
|
|
int c, d;
|
|
|
|
typecheck(&n->left, Etype|Erv);
|
|
t = n->left->type;
|
|
if(t == T)
|
|
goto ret;
|
|
|
|
if(n->left->op == OTYPE)
|
|
goto ret;
|
|
|
|
if(n->right->op != ONAME)
|
|
goto ret;
|
|
s = n->right->sym;
|
|
if(s == S)
|
|
goto ret;
|
|
|
|
for(d=0; d<nelem(dotlist); d++) {
|
|
c = adddot1(s, t, d, nil, 0);
|
|
if(c > 0)
|
|
goto out;
|
|
}
|
|
goto ret;
|
|
|
|
out:
|
|
if(c > 1)
|
|
yyerror("ambiguous DOT reference %T.%S", t, s);
|
|
|
|
// rebuild elided dots
|
|
for(c=d-1; c>=0; c--)
|
|
n->left = nod(ODOT, n->left, newname(dotlist[c].field->sym));
|
|
ret:
|
|
return n;
|
|
}
|
|
|
|
|
|
/*
|
|
* code to help generate trampoline
|
|
* functions for methods on embedded
|
|
* subtypes.
|
|
* these are approx the same as
|
|
* the corresponding adddot routines
|
|
* except that they expect to be called
|
|
* with unique tasks and they return
|
|
* the actual methods.
|
|
*/
|
|
|
|
typedef struct Symlink Symlink;
|
|
struct Symlink
|
|
{
|
|
Type* field;
|
|
uchar good;
|
|
uchar followptr;
|
|
Symlink* link;
|
|
};
|
|
static Symlink* slist;
|
|
|
|
static void
|
|
expand0(Type *t, int followptr)
|
|
{
|
|
Type *f, *u;
|
|
Symlink *sl;
|
|
|
|
u = t;
|
|
if(isptr[u->etype]) {
|
|
followptr = 1;
|
|
u = u->type;
|
|
}
|
|
|
|
if(u->etype == TINTER) {
|
|
for(f=u->type; f!=T; f=f->down) {
|
|
if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
|
|
continue;
|
|
if(f->sym->flags & SymUniq)
|
|
continue;
|
|
f->sym->flags |= SymUniq;
|
|
sl = mal(sizeof(*sl));
|
|
sl->field = f;
|
|
sl->link = slist;
|
|
sl->followptr = followptr;
|
|
slist = sl;
|
|
}
|
|
return;
|
|
}
|
|
|
|
u = methtype(t);
|
|
if(u != T) {
|
|
for(f=u->method; f!=T; f=f->down) {
|
|
if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
|
|
continue;
|
|
if(f->sym->flags & SymUniq)
|
|
continue;
|
|
f->sym->flags |= SymUniq;
|
|
sl = mal(sizeof(*sl));
|
|
sl->field = f;
|
|
sl->link = slist;
|
|
sl->followptr = followptr;
|
|
slist = sl;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
expand1(Type *t, int d, int followptr)
|
|
{
|
|
Type *f, *u;
|
|
|
|
if(t->trecur)
|
|
return;
|
|
if(d == 0)
|
|
return;
|
|
t->trecur = 1;
|
|
|
|
if(d != nelem(dotlist)-1)
|
|
expand0(t, followptr);
|
|
|
|
u = t;
|
|
if(isptr[u->etype]) {
|
|
followptr = 1;
|
|
u = u->type;
|
|
}
|
|
if(u->etype != TSTRUCT && u->etype != TINTER)
|
|
goto out;
|
|
|
|
for(f=u->type; f!=T; f=f->down) {
|
|
if(!f->embedded)
|
|
continue;
|
|
if(f->sym == S)
|
|
continue;
|
|
expand1(f->type, d-1, followptr);
|
|
}
|
|
|
|
out:
|
|
t->trecur = 0;
|
|
}
|
|
|
|
void
|
|
expandmeth(Sym *s, Type *t)
|
|
{
|
|
Symlink *sl;
|
|
Type *f;
|
|
int c, d;
|
|
|
|
if(s == S)
|
|
return;
|
|
if(t == T || t->xmethod != nil)
|
|
return;
|
|
|
|
// mark top-level method symbols
|
|
// so that expand1 doesn't consider them.
|
|
for(f=t->method; f != nil; f=f->down)
|
|
f->sym->flags |= SymUniq;
|
|
|
|
// generate all reachable methods
|
|
slist = nil;
|
|
expand1(t, nelem(dotlist)-1, 0);
|
|
|
|
// check each method to be uniquely reachable
|
|
for(sl=slist; sl!=nil; sl=sl->link) {
|
|
sl->field->sym->flags &= ~SymUniq;
|
|
for(d=0; d<nelem(dotlist); d++) {
|
|
c = adddot1(sl->field->sym, t, d, &f, 0);
|
|
if(c == 0)
|
|
continue;
|
|
if(c == 1) {
|
|
sl->good = 1;
|
|
sl->field = f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(f=t->method; f != nil; f=f->down)
|
|
f->sym->flags &= ~SymUniq;
|
|
|
|
t->xmethod = t->method;
|
|
for(sl=slist; sl!=nil; sl=sl->link) {
|
|
if(sl->good) {
|
|
// add it to the base type method list
|
|
f = typ(TFIELD);
|
|
*f = *sl->field;
|
|
f->embedded = 1; // needs a trampoline
|
|
if(sl->followptr)
|
|
f->embedded = 2;
|
|
f->down = t->xmethod;
|
|
t->xmethod = f;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Given funarg struct list, return list of ODCLFIELD Node fn args.
|
|
*/
|
|
static NodeList*
|
|
structargs(Type **tl, int mustname)
|
|
{
|
|
Iter savet;
|
|
Node *a, *n;
|
|
NodeList *args;
|
|
Type *t;
|
|
char buf[100];
|
|
int gen;
|
|
|
|
args = nil;
|
|
gen = 0;
|
|
for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) {
|
|
n = N;
|
|
if(t->sym)
|
|
n = newname(t->sym);
|
|
else if(mustname) {
|
|
// have to give it a name so we can refer to it in trampoline
|
|
snprint(buf, sizeof buf, ".anon%d", gen++);
|
|
n = newname(lookup(buf));
|
|
}
|
|
a = nod(ODCLFIELD, n, typenod(t->type));
|
|
a->isddd = t->isddd;
|
|
if(n != N)
|
|
n->isddd = t->isddd;
|
|
args = list(args, a);
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/*
|
|
* Generate a wrapper function to convert from
|
|
* a receiver of type T to a receiver of type U.
|
|
* That is,
|
|
*
|
|
* func (t T) M() {
|
|
* ...
|
|
* }
|
|
*
|
|
* already exists; this function generates
|
|
*
|
|
* func (u U) M() {
|
|
* u.M()
|
|
* }
|
|
*
|
|
* where the types T and U are such that u.M() is valid
|
|
* and calls the T.M method.
|
|
* The resulting function is for use in method tables.
|
|
*
|
|
* rcvr - U
|
|
* method - M func (t T)(), a TFIELD type struct
|
|
* newnam - the eventual mangled name of this function
|
|
*/
|
|
void
|
|
genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
|
|
{
|
|
Node *this, *fn, *call, *n, *t, *pad;
|
|
NodeList *l, *args, *in, *out;
|
|
Type *tpad;
|
|
int isddd;
|
|
Val v;
|
|
|
|
if(debug['r'])
|
|
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
|
|
rcvr, method, newnam);
|
|
|
|
lineno = 1; // less confusing than end of input
|
|
|
|
dclcontext = PEXTERN;
|
|
markdcl();
|
|
|
|
this = nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr));
|
|
this->left->ntype = this->right;
|
|
in = structargs(getinarg(method->type), 1);
|
|
out = structargs(getoutarg(method->type), 0);
|
|
|
|
fn = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(newnam);
|
|
t = nod(OTFUNC, N, N);
|
|
l = list1(this);
|
|
if(iface && rcvr->width < types[tptr]->width) {
|
|
// Building method for interface table and receiver
|
|
// is smaller than the single pointer-sized word
|
|
// that the interface call will pass in.
|
|
// Add a dummy padding argument after the
|
|
// receiver to make up the difference.
|
|
tpad = typ(TARRAY);
|
|
tpad->type = types[TUINT8];
|
|
tpad->bound = types[tptr]->width - rcvr->width;
|
|
pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad));
|
|
l = list(l, pad);
|
|
}
|
|
t->list = concat(l, in);
|
|
t->rlist = out;
|
|
fn->nname->ntype = t;
|
|
funchdr(fn);
|
|
|
|
// arg list
|
|
args = nil;
|
|
isddd = 0;
|
|
for(l=in; l; l=l->next) {
|
|
args = list(args, l->n->left);
|
|
isddd = l->n->left->isddd;
|
|
}
|
|
|
|
// generate nil pointer check for better error
|
|
if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) {
|
|
// generating wrapper from *T to T.
|
|
n = nod(OIF, N, N);
|
|
n->ntest = nod(OEQ, this->left, nodnil());
|
|
// these strings are already in the reflect tables,
|
|
// so no space cost to use them here.
|
|
l = nil;
|
|
v.ctype = CTSTR;
|
|
v.u.sval = strlit(rcvr->type->sym->pkg->name); // package name
|
|
l = list(l, nodlit(v));
|
|
v.u.sval = strlit(rcvr->type->sym->name); // type name
|
|
l = list(l, nodlit(v));
|
|
v.u.sval = strlit(method->sym->name);
|
|
l = list(l, nodlit(v)); // method name
|
|
call = nod(OCALL, syslook("panicwrap", 0), N);
|
|
call->list = l;
|
|
n->nbody = list1(call);
|
|
fn->nbody = list(fn->nbody, n);
|
|
}
|
|
|
|
// generate call
|
|
call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N);
|
|
call->list = args;
|
|
call->isddd = isddd;
|
|
if(method->type->outtuple > 0) {
|
|
n = nod(ORETURN, N, N);
|
|
n->list = list1(call);
|
|
call = n;
|
|
}
|
|
fn->nbody = list(fn->nbody, call);
|
|
|
|
if(0 && debug['r'])
|
|
dumplist("genwrapper body", fn->nbody);
|
|
|
|
funcbody(fn);
|
|
curfn = fn;
|
|
typecheck(&fn, Etop);
|
|
typechecklist(fn->nbody, Etop);
|
|
curfn = nil;
|
|
funccompile(fn, 0);
|
|
}
|
|
|
|
static Type*
|
|
ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
|
|
{
|
|
int i, c, d;
|
|
Type *m;
|
|
|
|
*followptr = 0;
|
|
|
|
if(t == T)
|
|
return T;
|
|
|
|
for(d=0; d<nelem(dotlist); d++) {
|
|
c = adddot1(s, t, d, &m, ignorecase);
|
|
if(c > 1) {
|
|
yyerror("%T.%S is ambiguous", t, s);
|
|
return T;
|
|
}
|
|
if(c == 1) {
|
|
for(i=0; i<d; i++) {
|
|
if(isptr[dotlist[i].field->type->etype]) {
|
|
*followptr = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(m->type->etype != TFUNC || m->type->thistuple == 0) {
|
|
yyerror("%T.%S is a field, not a method", t, s);
|
|
return T;
|
|
}
|
|
return m;
|
|
}
|
|
}
|
|
return T;
|
|
}
|
|
|
|
int
|
|
implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
|
|
{
|
|
Type *t0, *im, *tm, *rcvr, *imtype;
|
|
int followptr;
|
|
|
|
t0 = t;
|
|
if(t == T)
|
|
return 0;
|
|
|
|
// if this is too slow,
|
|
// could sort these first
|
|
// and then do one loop.
|
|
|
|
if(t->etype == TINTER) {
|
|
for(im=iface->type; im; im=im->down) {
|
|
for(tm=t->type; tm; tm=tm->down) {
|
|
if(tm->sym == im->sym) {
|
|
if(eqtype(tm->type, im->type))
|
|
goto found;
|
|
*m = im;
|
|
*samename = tm;
|
|
*ptr = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
*m = im;
|
|
*samename = nil;
|
|
*ptr = 0;
|
|
return 0;
|
|
found:;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
t = methtype(t);
|
|
if(t != T)
|
|
expandmeth(t->sym, t);
|
|
for(im=iface->type; im; im=im->down) {
|
|
imtype = methodfunc(im->type, 0);
|
|
tm = ifacelookdot(im->sym, t, &followptr, 0);
|
|
if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) {
|
|
if(tm == T)
|
|
tm = ifacelookdot(im->sym, t, &followptr, 1);
|
|
*m = im;
|
|
*samename = tm;
|
|
*ptr = 0;
|
|
return 0;
|
|
}
|
|
// if pointer receiver in method,
|
|
// the method does not exist for value types.
|
|
rcvr = getthisx(tm->type)->type->type;
|
|
if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr && !isifacemethod(tm->type)) {
|
|
if(0 && debug['r'])
|
|
yyerror("interface pointer mismatch");
|
|
|
|
*m = im;
|
|
*samename = nil;
|
|
*ptr = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* even simpler simtype; get rid of ptr, bool.
|
|
* assuming that the front end has rejected
|
|
* all the invalid conversions (like ptr -> bool)
|
|
*/
|
|
int
|
|
simsimtype(Type *t)
|
|
{
|
|
int et;
|
|
|
|
if(t == 0)
|
|
return 0;
|
|
|
|
et = simtype[t->etype];
|
|
switch(et) {
|
|
case TPTR32:
|
|
et = TUINT32;
|
|
break;
|
|
case TPTR64:
|
|
et = TUINT64;
|
|
break;
|
|
case TBOOL:
|
|
et = TUINT8;
|
|
break;
|
|
}
|
|
return et;
|
|
}
|
|
|
|
NodeList*
|
|
concat(NodeList *a, NodeList *b)
|
|
{
|
|
if(a == nil)
|
|
return b;
|
|
if(b == nil)
|
|
return a;
|
|
|
|
a->end->next = b;
|
|
a->end = b->end;
|
|
b->end = nil;
|
|
return a;
|
|
}
|
|
|
|
NodeList*
|
|
list1(Node *n)
|
|
{
|
|
NodeList *l;
|
|
|
|
if(n == nil)
|
|
return nil;
|
|
if(n->op == OBLOCK && n->ninit == nil)
|
|
return n->list;
|
|
l = mal(sizeof *l);
|
|
l->n = n;
|
|
l->end = l;
|
|
return l;
|
|
}
|
|
|
|
NodeList*
|
|
list(NodeList *l, Node *n)
|
|
{
|
|
return concat(l, list1(n));
|
|
}
|
|
|
|
void
|
|
listsort(NodeList** l, int(*f)(Node*, Node*))
|
|
{
|
|
NodeList *l1, *l2, *le;
|
|
|
|
if(*l == nil || (*l)->next == nil)
|
|
return;
|
|
|
|
l1 = *l;
|
|
l2 = *l;
|
|
for(;;) {
|
|
l2 = l2->next;
|
|
if(l2 == nil)
|
|
break;
|
|
l2 = l2->next;
|
|
if(l2 == nil)
|
|
break;
|
|
l1 = l1->next;
|
|
}
|
|
|
|
l2 = l1->next;
|
|
l1->next = nil;
|
|
l2->end = (*l)->end;
|
|
(*l)->end = l1;
|
|
|
|
l1 = *l;
|
|
listsort(&l1, f);
|
|
listsort(&l2, f);
|
|
|
|
if ((*f)(l1->n, l2->n) < 0) {
|
|
*l = l1;
|
|
} else {
|
|
*l = l2;
|
|
l2 = l1;
|
|
l1 = *l;
|
|
}
|
|
|
|
// now l1 == *l; and l1 < l2
|
|
|
|
while ((l1 != nil) && (l2 != nil)) {
|
|
while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0)
|
|
l1 = l1->next;
|
|
|
|
// l1 is last one from l1 that is < l2
|
|
le = l1->next; // le is the rest of l1, first one that is >= l2
|
|
if (le != nil)
|
|
le->end = (*l)->end;
|
|
|
|
(*l)->end = l1; // cut *l at l1
|
|
*l = concat(*l, l2); // glue l2 to *l's tail
|
|
|
|
l1 = l2; // l1 is the first element of *l that is < the new l2
|
|
l2 = le; // ... because l2 now is the old tail of l1
|
|
}
|
|
|
|
*l = concat(*l, l2); // any remainder
|
|
}
|
|
|
|
NodeList*
|
|
listtreecopy(NodeList *l)
|
|
{
|
|
NodeList *out;
|
|
|
|
out = nil;
|
|
for(; l; l=l->next)
|
|
out = list(out, treecopy(l->n));
|
|
return out;
|
|
}
|
|
|
|
Node*
|
|
liststmt(NodeList *l)
|
|
{
|
|
Node *n;
|
|
|
|
n = nod(OBLOCK, N, N);
|
|
n->list = l;
|
|
if(l)
|
|
n->lineno = l->n->lineno;
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* return nelem of list
|
|
*/
|
|
int
|
|
count(NodeList *l)
|
|
{
|
|
int n;
|
|
|
|
n = 0;
|
|
for(; l; l=l->next)
|
|
n++;
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* return nelem of list
|
|
*/
|
|
int
|
|
structcount(Type *t)
|
|
{
|
|
int v;
|
|
Iter s;
|
|
|
|
v = 0;
|
|
for(t = structfirst(&s, &t); t != T; t = structnext(&s))
|
|
v++;
|
|
return v;
|
|
}
|
|
|
|
/*
|
|
* return power of 2 of the constant
|
|
* operand. -1 if it is not a power of 2.
|
|
* 1000+ if it is a -(power of 2)
|
|
*/
|
|
int
|
|
powtwo(Node *n)
|
|
{
|
|
uvlong v, b;
|
|
int i;
|
|
|
|
if(n == N || n->op != OLITERAL || n->type == T)
|
|
goto no;
|
|
if(!isint[n->type->etype])
|
|
goto no;
|
|
|
|
v = mpgetfix(n->val.u.xval);
|
|
b = 1ULL;
|
|
for(i=0; i<64; i++) {
|
|
if(b == v)
|
|
return i;
|
|
b = b<<1;
|
|
}
|
|
|
|
if(!issigned[n->type->etype])
|
|
goto no;
|
|
|
|
v = -v;
|
|
b = 1ULL;
|
|
for(i=0; i<64; i++) {
|
|
if(b == v)
|
|
return i+1000;
|
|
b = b<<1;
|
|
}
|
|
|
|
no:
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* return the unsigned type for
|
|
* a signed integer type.
|
|
* returns T if input is not a
|
|
* signed integer type.
|
|
*/
|
|
Type*
|
|
tounsigned(Type *t)
|
|
{
|
|
|
|
// this is types[et+1], but not sure
|
|
// that this relation is immutable
|
|
switch(t->etype) {
|
|
default:
|
|
print("tounsigned: unknown type %T\n", t);
|
|
t = T;
|
|
break;
|
|
case TINT:
|
|
t = types[TUINT];
|
|
break;
|
|
case TINT8:
|
|
t = types[TUINT8];
|
|
break;
|
|
case TINT16:
|
|
t = types[TUINT16];
|
|
break;
|
|
case TINT32:
|
|
t = types[TUINT32];
|
|
break;
|
|
case TINT64:
|
|
t = types[TUINT64];
|
|
break;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
* magic number for signed division
|
|
* see hacker's delight chapter 10
|
|
*/
|
|
void
|
|
smagic(Magic *m)
|
|
{
|
|
int p;
|
|
uint64 ad, anc, delta, q1, r1, q2, r2, t;
|
|
uint64 mask, two31;
|
|
|
|
m->bad = 0;
|
|
switch(m->w) {
|
|
default:
|
|
m->bad = 1;
|
|
return;
|
|
case 8:
|
|
mask = 0xffLL;
|
|
break;
|
|
case 16:
|
|
mask = 0xffffLL;
|
|
break;
|
|
case 32:
|
|
mask = 0xffffffffLL;
|
|
break;
|
|
case 64:
|
|
mask = 0xffffffffffffffffLL;
|
|
break;
|
|
}
|
|
two31 = mask ^ (mask>>1);
|
|
|
|
p = m->w-1;
|
|
ad = m->sd;
|
|
if(m->sd < 0)
|
|
ad = -m->sd;
|
|
|
|
// bad denominators
|
|
if(ad == 0 || ad == 1 || ad == two31) {
|
|
m->bad = 1;
|
|
return;
|
|
}
|
|
|
|
t = two31;
|
|
ad &= mask;
|
|
|
|
anc = t - 1 - t%ad;
|
|
anc &= mask;
|
|
|
|
q1 = two31/anc;
|
|
r1 = two31 - q1*anc;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
|
|
q2 = two31/ad;
|
|
r2 = two31 - q2*ad;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
|
|
for(;;) {
|
|
p++;
|
|
q1 <<= 1;
|
|
r1 <<= 1;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
if(r1 >= anc) {
|
|
q1++;
|
|
r1 -= anc;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
}
|
|
|
|
q2 <<= 1;
|
|
r2 <<= 1;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
if(r2 >= ad) {
|
|
q2++;
|
|
r2 -= ad;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
}
|
|
|
|
delta = ad - r2;
|
|
delta &= mask;
|
|
if(q1 < delta || (q1 == delta && r1 == 0)) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
m->sm = q2+1;
|
|
if(m->sm & two31)
|
|
m->sm |= ~mask;
|
|
m->s = p-m->w;
|
|
}
|
|
|
|
/*
|
|
* magic number for unsigned division
|
|
* see hacker's delight chapter 10
|
|
*/
|
|
void
|
|
umagic(Magic *m)
|
|
{
|
|
int p;
|
|
uint64 nc, delta, q1, r1, q2, r2;
|
|
uint64 mask, two31;
|
|
|
|
m->bad = 0;
|
|
m->ua = 0;
|
|
|
|
switch(m->w) {
|
|
default:
|
|
m->bad = 1;
|
|
return;
|
|
case 8:
|
|
mask = 0xffLL;
|
|
break;
|
|
case 16:
|
|
mask = 0xffffLL;
|
|
break;
|
|
case 32:
|
|
mask = 0xffffffffLL;
|
|
break;
|
|
case 64:
|
|
mask = 0xffffffffffffffffLL;
|
|
break;
|
|
}
|
|
two31 = mask ^ (mask>>1);
|
|
|
|
m->ud &= mask;
|
|
if(m->ud == 0 || m->ud == two31) {
|
|
m->bad = 1;
|
|
return;
|
|
}
|
|
nc = mask - (-m->ud&mask)%m->ud;
|
|
p = m->w-1;
|
|
|
|
q1 = two31/nc;
|
|
r1 = two31 - q1*nc;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
|
|
q2 = (two31-1) / m->ud;
|
|
r2 = (two31-1) - q2*m->ud;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
|
|
for(;;) {
|
|
p++;
|
|
if(r1 >= nc-r1) {
|
|
q1 <<= 1;
|
|
q1++;
|
|
r1 <<= 1;
|
|
r1 -= nc;
|
|
} else {
|
|
q1 <<= 1;
|
|
r1 <<= 1;
|
|
}
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
if(r2+1 >= m->ud-r2) {
|
|
if(q2 >= two31-1) {
|
|
m->ua = 1;
|
|
}
|
|
q2 <<= 1;
|
|
q2++;
|
|
r2 <<= 1;
|
|
r2++;
|
|
r2 -= m->ud;
|
|
} else {
|
|
if(q2 >= two31) {
|
|
m->ua = 1;
|
|
}
|
|
q2 <<= 1;
|
|
r2 <<= 1;
|
|
r2++;
|
|
}
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
|
|
delta = m->ud - 1 - r2;
|
|
delta &= mask;
|
|
|
|
if(p < m->w+m->w)
|
|
if(q1 < delta || (q1 == delta && r1 == 0)) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
m->um = q2+1;
|
|
m->s = p-m->w;
|
|
}
|
|
|
|
Sym*
|
|
ngotype(Node *n)
|
|
{
|
|
if(n->sym != S && n->realtype != T)
|
|
if(strncmp(n->sym->name, "autotmp_", 8) != 0)
|
|
if(strncmp(n->sym->name, "statictmp_", 8) != 0)
|
|
return typename(n->realtype)->left->sym;
|
|
|
|
return S;
|
|
}
|
|
|
|
/*
|
|
* Convert raw string to the prefix that will be used in the symbol table.
|
|
* Invalid bytes turn into %xx. Right now the only bytes that need
|
|
* escaping are %, ., and ", but we escape all control characters too.
|
|
*/
|
|
static char*
|
|
pathtoprefix(char *s)
|
|
{
|
|
static char hex[] = "0123456789abcdef";
|
|
char *p, *r, *w;
|
|
int n;
|
|
|
|
// check for chars that need escaping
|
|
n = 0;
|
|
for(r=s; *r; r++)
|
|
if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
|
|
n++;
|
|
|
|
// quick exit
|
|
if(n == 0)
|
|
return s;
|
|
|
|
// escape
|
|
p = mal((r-s)+1+2*n);
|
|
for(r=s, w=p; *r; r++) {
|
|
if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {
|
|
*w++ = '%';
|
|
*w++ = hex[(*r>>4)&0xF];
|
|
*w++ = hex[*r&0xF];
|
|
} else
|
|
*w++ = *r;
|
|
}
|
|
*w = '\0';
|
|
return p;
|
|
}
|
|
|
|
Pkg*
|
|
mkpkg(Strlit *path)
|
|
{
|
|
Pkg *p;
|
|
int h;
|
|
|
|
if(strlen(path->s) != path->len) {
|
|
yyerror("import path contains NUL byte");
|
|
errorexit();
|
|
}
|
|
|
|
h = stringhash(path->s) & (nelem(phash)-1);
|
|
for(p=phash[h]; p; p=p->link)
|
|
if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
|
|
return p;
|
|
|
|
p = mal(sizeof *p);
|
|
p->path = path;
|
|
p->prefix = pathtoprefix(path->s);
|
|
p->link = phash[h];
|
|
phash[h] = p;
|
|
return p;
|
|
}
|
|
|
|
Strlit*
|
|
strlit(char *s)
|
|
{
|
|
Strlit *t;
|
|
|
|
t = mal(sizeof *t + strlen(s));
|
|
strcpy(t->s, s);
|
|
t->len = strlen(s);
|
|
return t;
|
|
}
|