github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/gc/gen.c (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6   * portable half of code generator.
     7   * mainly statements and control flow.
     8   */
     9  
    10  #include <u.h>
    11  #include <libc.h>
    12  #include "go.h"
    13  
    14  static void	cgen_dcl(Node *n);
    15  static void	cgen_proc(Node *n, int proc);
    16  static void	checkgoto(Node*, Node*);
    17  
    18  static Label *labellist;
    19  static Label *lastlabel;
    20  
    21  Node*
    22  sysfunc(char *name)
    23  {
    24  	Node *n;
    25  
    26  	n = newname(pkglookup(name, runtimepkg));
    27  	n->class = PFUNC;
    28  	return n;
    29  }
    30  
    31  /*
    32   * the address of n has been taken and might be used after
    33   * the current function returns.  mark any local vars
    34   * as needing to move to the heap.
    35   */
    36  void
    37  addrescapes(Node *n)
    38  {
    39  	char buf[100];
    40  	Node *oldfn;
    41  
    42  	switch(n->op) {
    43  	default:
    44  		// probably a type error already.
    45  		// dump("addrescapes", n);
    46  		break;
    47  
    48  	case ONAME:
    49  		if(n == nodfp)
    50  			break;
    51  
    52  		// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
    53  		// on PPARAM it means something different.
    54  		if(n->class == PAUTO && n->esc == EscNever)
    55  			break;
    56  
    57  		if(debug['N'] && n->esc != EscUnknown)
    58  			fatal("without escape analysis, only PAUTO's should have esc: %N", n);
    59  
    60  		switch(n->class) {
    61  		case PPARAMREF:
    62  			addrescapes(n->defn);
    63  			break;
    64  		case PPARAM:
    65  		case PPARAMOUT:
    66  			// if func param, need separate temporary
    67  			// to hold heap pointer.
    68  			// the function type has already been checked
    69  			// (we're in the function body)
    70  			// so the param already has a valid xoffset.
    71  
    72  			// expression to refer to stack copy
    73  			n->stackparam = nod(OPARAM, n, N);
    74  			n->stackparam->type = n->type;
    75  			n->stackparam->addable = 1;
    76  			if(n->xoffset == BADWIDTH)
    77  				fatal("addrescapes before param assignment");
    78  			n->stackparam->xoffset = n->xoffset;
    79  			// fallthrough
    80  
    81  		case PAUTO:
    82  			n->class |= PHEAP;
    83  			n->addable = 0;
    84  			n->ullman = 2;
    85  			n->xoffset = 0;
    86  
    87  			// create stack variable to hold pointer to heap
    88  			oldfn = curfn;
    89  			curfn = n->curfn;
    90  			n->heapaddr = temp(ptrto(n->type));
    91  			snprint(buf, sizeof buf, "&%S", n->sym);
    92  			n->heapaddr->sym = lookup(buf);
    93  			n->heapaddr->orig->sym = n->heapaddr->sym;
    94  			if(!debug['N'])
    95  				n->esc = EscHeap;
    96  			if(debug['m'])
    97  				print("%L: moved to heap: %N\n", n->lineno, n);
    98  			curfn = oldfn;
    99  			break;
   100  		}
   101  		break;
   102  
   103  	case OIND:
   104  	case ODOTPTR:
   105  		break;
   106  
   107  	case ODOT:
   108  	case OINDEX:
   109  		// ODOTPTR has already been introduced,
   110  		// so these are the non-pointer ODOT and OINDEX.
   111  		// In &x[0], if x is a slice, then x does not
   112  		// escape--the pointer inside x does, but that
   113  		// is always a heap pointer anyway.
   114  		if(!isslice(n->left->type))
   115  			addrescapes(n->left);
   116  		break;
   117  	}
   118  }
   119  
   120  void
   121  clearlabels(void)
   122  {
   123  	Label *l;
   124  
   125  	for(l=labellist; l!=L; l=l->link)
   126  		l->sym->label = L;
   127  	
   128  	labellist = L;
   129  	lastlabel = L;
   130  }
   131  
   132  static Label*
   133  newlab(Node *n)
   134  {
   135  	Sym *s;
   136  	Label *lab;
   137  	
   138  	s = n->left->sym;
   139  	if((lab = s->label) == L) {
   140  		lab = mal(sizeof(*lab));
   141  		if(lastlabel == nil)
   142  			labellist = lab;
   143  		else
   144  			lastlabel->link = lab;
   145  		lastlabel = lab;
   146  		lab->sym = s;
   147  		s->label = lab;
   148  	}
   149  	
   150  	if(n->op == OLABEL) {
   151  		if(lab->def != N)
   152  			yyerror("label %S already defined at %L", s, lab->def->lineno);
   153  		else
   154  			lab->def = n;
   155  	} else
   156  		lab->use = list(lab->use, n);
   157  
   158  	return lab;
   159  }
   160  
   161  void
   162  checklabels(void)
   163  {
   164  	Label *lab;
   165  	NodeList *l;
   166  
   167  	for(lab=labellist; lab!=L; lab=lab->link) {
   168  		if(lab->def == N) {
   169  			for(l=lab->use; l; l=l->next)
   170  				yyerrorl(l->n->lineno, "label %S not defined", lab->sym);
   171  			continue;
   172  		}
   173  		if(lab->use == nil && !lab->used) {
   174  			yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym);
   175  			continue;
   176  		}
   177  		if(lab->gotopc != P)
   178  			fatal("label %S never resolved", lab->sym);
   179  		for(l=lab->use; l; l=l->next)
   180  			checkgoto(l->n, lab->def);
   181  	}
   182  }
   183  
   184  static void
   185  checkgoto(Node *from, Node *to)
   186  {
   187  	int nf, nt;
   188  	Sym *block, *dcl, *fs, *ts;
   189  	int lno;
   190  
   191  	if(from->sym == to->sym)
   192  		return;
   193  
   194  	nf = 0;
   195  	for(fs=from->sym; fs; fs=fs->link)
   196  		nf++;
   197  	nt = 0;
   198  	for(fs=to->sym; fs; fs=fs->link)
   199  		nt++;
   200  	fs = from->sym;
   201  	for(; nf > nt; nf--)
   202  		fs = fs->link;
   203  	if(fs != to->sym) {
   204  		lno = lineno;
   205  		setlineno(from);
   206  
   207  		// decide what to complain about.
   208  		// prefer to complain about 'into block' over declarations,
   209  		// so scan backward to find most recent block or else dcl.
   210  		block = S;
   211  		dcl = S;
   212  		ts = to->sym;
   213  		for(; nt > nf; nt--) {
   214  			if(ts->pkg == nil)
   215  				block = ts;
   216  			else
   217  				dcl = ts;
   218  			ts = ts->link;
   219  		}
   220  		while(ts != fs) {
   221  			if(ts->pkg == nil)
   222  				block = ts;
   223  			else
   224  				dcl = ts;
   225  			ts = ts->link;
   226  			fs = fs->link;
   227  		}
   228  
   229  		if(block)
   230  			yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno);
   231  		else
   232  			yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno);
   233  		lineno = lno;
   234  	}
   235  }
   236  
   237  static Label*
   238  stmtlabel(Node *n)
   239  {
   240  	Label *lab;
   241  
   242  	if(n->sym != S)
   243  	if((lab = n->sym->label) != L)
   244  	if(lab->def != N)
   245  	if(lab->def->defn == n)
   246  		return lab;
   247  	return L;
   248  }
   249  
   250  /*
   251   * compile statements
   252   */
   253  void
   254  genlist(NodeList *l)
   255  {
   256  	for(; l; l=l->next)
   257  		gen(l->n);
   258  }
   259  
   260  void
   261  gen(Node *n)
   262  {
   263  	int32 lno;
   264  	Prog *scontin, *sbreak;
   265  	Prog *p1, *p2, *p3;
   266  	Label *lab;
   267  	int32 wasregalloc;
   268  
   269  //dump("gen", n);
   270  
   271  	lno = setlineno(n);
   272  	wasregalloc = anyregalloc();
   273  
   274  	if(n == N)
   275  		goto ret;
   276  
   277  	if(n->ninit)
   278  		genlist(n->ninit);
   279  
   280  	setlineno(n);
   281  
   282  	switch(n->op) {
   283  	default:
   284  		fatal("gen: unknown op %+hN", n);
   285  		break;
   286  
   287  	case OCASE:
   288  	case OFALL:
   289  	case OXCASE:
   290  	case OXFALL:
   291  	case ODCLCONST:
   292  	case ODCLFUNC:
   293  	case ODCLTYPE:
   294  		break;
   295  
   296  	case OEMPTY:
   297  		break;
   298  
   299  	case OBLOCK:
   300  		genlist(n->list);
   301  		break;
   302  
   303  	case OLABEL:
   304  		lab = newlab(n);
   305  
   306  		// if there are pending gotos, resolve them all to the current pc.
   307  		for(p1=lab->gotopc; p1; p1=p2) {
   308  			p2 = unpatch(p1);
   309  			patch(p1, pc);
   310  		}
   311  		lab->gotopc = P;
   312  		if(lab->labelpc == P)
   313  			lab->labelpc = pc;
   314  
   315  		if(n->defn) {
   316  			switch(n->defn->op) {
   317  			case OFOR:
   318  			case OSWITCH:
   319  			case OSELECT:
   320  				// so stmtlabel can find the label
   321  				n->defn->sym = lab->sym;
   322  			}
   323  		}
   324  		break;
   325  
   326  	case OGOTO:
   327  		// if label is defined, emit jump to it.
   328  		// otherwise save list of pending gotos in lab->gotopc.
   329  		// the list is linked through the normal jump target field
   330  		// to avoid a second list.  (the jumps are actually still
   331  		// valid code, since they're just going to another goto
   332  		// to the same label.  we'll unwind it when we learn the pc
   333  		// of the label in the OLABEL case above.)
   334  		lab = newlab(n);
   335  		if(lab->labelpc != P)
   336  			gjmp(lab->labelpc);
   337  		else
   338  			lab->gotopc = gjmp(lab->gotopc);
   339  		break;
   340  
   341  	case OBREAK:
   342  		if(n->left != N) {
   343  			lab = n->left->sym->label;
   344  			if(lab == L) {
   345  				yyerror("break label not defined: %S", n->left->sym);
   346  				break;
   347  			}
   348  			lab->used = 1;
   349  			if(lab->breakpc == P) {
   350  				yyerror("invalid break label %S", n->left->sym);
   351  				break;
   352  			}
   353  			gjmp(lab->breakpc);
   354  			break;
   355  		}
   356  		if(breakpc == P) {
   357  			yyerror("break is not in a loop");
   358  			break;
   359  		}
   360  		gjmp(breakpc);
   361  		break;
   362  
   363  	case OCONTINUE:
   364  		if(n->left != N) {
   365  			lab = n->left->sym->label;
   366  			if(lab == L) {
   367  				yyerror("continue label not defined: %S", n->left->sym);
   368  				break;
   369  			}
   370  			lab->used = 1;
   371  			if(lab->continpc == P) {
   372  				yyerror("invalid continue label %S", n->left->sym);
   373  				break;
   374  			}
   375  			gjmp(lab->continpc);
   376  			break;
   377  		}
   378  		if(continpc == P) {
   379  			yyerror("continue is not in a loop");
   380  			break;
   381  		}
   382  		gjmp(continpc);
   383  		break;
   384  
   385  	case OFOR:
   386  		sbreak = breakpc;
   387  		p1 = gjmp(P);			//		goto test
   388  		breakpc = gjmp(P);		// break:	goto done
   389  		scontin = continpc;
   390  		continpc = pc;
   391  
   392  		// define break and continue labels
   393  		if((lab = stmtlabel(n)) != L) {
   394  			lab->breakpc = breakpc;
   395  			lab->continpc = continpc;
   396  		}
   397  		gen(n->nincr);				// contin:	incr
   398  		patch(p1, pc);				// test:
   399  		bgen(n->ntest, 0, -1, breakpc);		//		if(!test) goto break
   400  		genlist(n->nbody);				//		body
   401  		gjmp(continpc);
   402  		patch(breakpc, pc);			// done:
   403  		continpc = scontin;
   404  		breakpc = sbreak;
   405  		if(lab) {
   406  			lab->breakpc = P;
   407  			lab->continpc = P;
   408  		}
   409  		break;
   410  
   411  	case OIF:
   412  		p1 = gjmp(P);			//		goto test
   413  		p2 = gjmp(P);			// p2:		goto else
   414  		patch(p1, pc);				// test:
   415  		bgen(n->ntest, 0, -n->likely, p2);		//		if(!test) goto p2
   416  		genlist(n->nbody);				//		then
   417  		p3 = gjmp(P);			//		goto done
   418  		patch(p2, pc);				// else:
   419  		genlist(n->nelse);				//		else
   420  		patch(p3, pc);				// done:
   421  		break;
   422  
   423  	case OSWITCH:
   424  		sbreak = breakpc;
   425  		p1 = gjmp(P);			//		goto test
   426  		breakpc = gjmp(P);		// break:	goto done
   427  
   428  		// define break label
   429  		if((lab = stmtlabel(n)) != L)
   430  			lab->breakpc = breakpc;
   431  
   432  		patch(p1, pc);				// test:
   433  		genlist(n->nbody);				//		switch(test) body
   434  		patch(breakpc, pc);			// done:
   435  		breakpc = sbreak;
   436  		if(lab != L)
   437  			lab->breakpc = P;
   438  		break;
   439  
   440  	case OSELECT:
   441  		sbreak = breakpc;
   442  		p1 = gjmp(P);			//		goto test
   443  		breakpc = gjmp(P);		// break:	goto done
   444  
   445  		// define break label
   446  		if((lab = stmtlabel(n)) != L)
   447  			lab->breakpc = breakpc;
   448  
   449  		patch(p1, pc);				// test:
   450  		genlist(n->nbody);				//		select() body
   451  		patch(breakpc, pc);			// done:
   452  		breakpc = sbreak;
   453  		if(lab != L)
   454  			lab->breakpc = P;
   455  		break;
   456  
   457  	case OASOP:
   458  		cgen_asop(n);
   459  		break;
   460  
   461  	case ODCL:
   462  		cgen_dcl(n->left);
   463  		break;
   464  
   465  	case OAS:
   466  		if(gen_as_init(n))
   467  			break;
   468  		cgen_as(n->left, n->right);
   469  		break;
   470  
   471  	case OCALLMETH:
   472  		cgen_callmeth(n, 0);
   473  		break;
   474  
   475  	case OCALLINTER:
   476  		cgen_callinter(n, N, 0);
   477  		break;
   478  
   479  	case OCALLFUNC:
   480  		cgen_call(n, 0);
   481  		break;
   482  
   483  	case OPROC:
   484  		cgen_proc(n, 1);
   485  		break;
   486  
   487  	case ODEFER:
   488  		cgen_proc(n, 2);
   489  		break;
   490  
   491  	case ORETURN:
   492  		cgen_ret(n);
   493  		break;
   494  	
   495  	case OCHECKNOTNIL:
   496  		checkref(n->left, 1);
   497  	}
   498  
   499  ret:
   500  	if(anyregalloc() != wasregalloc) {
   501  		dump("node", n);
   502  		fatal("registers left allocated");
   503  	}
   504  
   505  	lineno = lno;
   506  }
   507  
   508  /*
   509   * generate call to non-interface method
   510   *	proc=0	normal call
   511   *	proc=1	goroutine run in new proc
   512   *	proc=2	defer call save away stack
   513   */
   514  void
   515  cgen_callmeth(Node *n, int proc)
   516  {
   517  	Node n2;
   518  	Node *l;
   519  
   520  	// generate a rewrite in n2 for the method call
   521  	// (p.f)(...) goes to (f)(p,...)
   522  
   523  	l = n->left;
   524  	if(l->op != ODOTMETH)
   525  		fatal("cgen_callmeth: not dotmethod: %N");
   526  
   527  	n2 = *n;
   528  	n2.op = OCALLFUNC;
   529  	n2.left = l->right;
   530  	n2.left->type = l->type;
   531  
   532  	if(n2.left->op == ONAME)
   533  		n2.left->class = PFUNC;
   534  	cgen_call(&n2, proc);
   535  }
   536  
   537  /*
   538   * generate code to start new proc running call n.
   539   */
   540  static void
   541  cgen_proc(Node *n, int proc)
   542  {
   543  	switch(n->left->op) {
   544  	default:
   545  		fatal("cgen_proc: unknown call %O", n->left->op);
   546  
   547  	case OCALLMETH:
   548  		cgen_callmeth(n->left, proc);
   549  		break;
   550  
   551  	case OCALLINTER:
   552  		cgen_callinter(n->left, N, proc);
   553  		break;
   554  
   555  	case OCALLFUNC:
   556  		cgen_call(n->left, proc);
   557  		break;
   558  	}
   559  
   560  }
   561  
   562  /*
   563   * generate declaration.
   564   * nothing to do for on-stack automatics,
   565   * but might have to allocate heap copy
   566   * for escaped variables.
   567   */
   568  static void
   569  cgen_dcl(Node *n)
   570  {
   571  	if(debug['g'])
   572  		dump("\ncgen-dcl", n);
   573  	if(n->op != ONAME) {
   574  		dump("cgen_dcl", n);
   575  		fatal("cgen_dcl");
   576  	}
   577  	if(!(n->class & PHEAP))
   578  		return;
   579  	if(n->alloc == nil)
   580  		n->alloc = callnew(n->type);
   581  	cgen_as(n->heapaddr, n->alloc);
   582  }
   583  
   584  /*
   585   * generate discard of value
   586   */
   587  static void
   588  cgen_discard(Node *nr)
   589  {
   590  	Node tmp;
   591  
   592  	if(nr == N)
   593  		return;
   594  
   595  	switch(nr->op) {
   596  	case ONAME:
   597  		if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF)
   598  			gused(nr);
   599  		break;
   600  
   601  	// unary
   602  	case OADD:
   603  	case OAND:
   604  	case ODIV:
   605  	case OEQ:
   606  	case OGE:
   607  	case OGT:
   608  	case OLE:
   609  	case OLSH:
   610  	case OLT:
   611  	case OMOD:
   612  	case OMUL:
   613  	case ONE:
   614  	case OOR:
   615  	case ORSH:
   616  	case OSUB:
   617  	case OXOR:
   618  		cgen_discard(nr->left);
   619  		cgen_discard(nr->right);
   620  		break;
   621  
   622  	// binary
   623  	case OCAP:
   624  	case OCOM:
   625  	case OLEN:
   626  	case OMINUS:
   627  	case ONOT:
   628  	case OPLUS:
   629  		cgen_discard(nr->left);
   630  		break;
   631  
   632  	// special enough to just evaluate
   633  	default:
   634  		tempname(&tmp, nr->type);
   635  		cgen_as(&tmp, nr);
   636  		gused(&tmp);
   637  	}
   638  }
   639  
   640  /*
   641   * clearslim generates code to zero a slim node.
   642   */
   643  void
   644  clearslim(Node *n)
   645  {
   646  	Node z;
   647  	Mpflt zero;
   648  
   649  	memset(&z, 0, sizeof(z));
   650  	z.op = OLITERAL;
   651  	z.type = n->type;
   652  	z.addable = 1;
   653  
   654  	switch(simtype[n->type->etype]) {
   655  	case TCOMPLEX64:
   656  	case TCOMPLEX128:
   657  		z.val.u.cval = mal(sizeof(*z.val.u.cval));
   658  		mpmovecflt(&z.val.u.cval->real, 0.0);
   659  		mpmovecflt(&z.val.u.cval->imag, 0.0);
   660  		break;
   661  
   662  	case TFLOAT32:
   663  	case TFLOAT64:
   664  		mpmovecflt(&zero, 0.0);
   665  		z.val.ctype = CTFLT;
   666  		z.val.u.fval = &zero;
   667  		break;
   668  
   669  	case TPTR32:
   670  	case TPTR64:
   671  	case TCHAN:
   672  	case TMAP:
   673  		z.val.ctype = CTNIL;
   674  		break;
   675  
   676  	case TBOOL:
   677  		z.val.ctype = CTBOOL;
   678  		break;
   679  
   680  	case TINT8:
   681  	case TINT16:
   682  	case TINT32:
   683  	case TINT64:
   684  	case TUINT8:
   685  	case TUINT16:
   686  	case TUINT32:
   687  	case TUINT64:
   688  		z.val.ctype = CTINT;
   689  		z.val.u.xval = mal(sizeof(*z.val.u.xval));
   690  		mpmovecfix(z.val.u.xval, 0);
   691  		break;
   692  
   693  	default:
   694  		fatal("clearslim called on type %T", n->type);
   695  	}
   696  
   697  	ullmancalc(&z);
   698  	cgen(&z, n);
   699  }
   700  
   701  /*
   702   * generate assignment:
   703   *	nl = nr
   704   * nr == N means zero nl.
   705   */
   706  void
   707  cgen_as(Node *nl, Node *nr)
   708  {
   709  	Type *tl;
   710  
   711  	if(debug['g']) {
   712  		dump("cgen_as", nl);
   713  		dump("cgen_as = ", nr);
   714  	}
   715  
   716  	while(nr != N && nr->op == OCONVNOP)
   717  		nr = nr->left;
   718  
   719  	if(nl == N || isblank(nl)) {
   720  		cgen_discard(nr);
   721  		return;
   722  	}
   723  
   724  	if(nr == N || isnil(nr)) {
   725  		// externals and heaps should already be clear
   726  		if(nr == N) {
   727  			if(nl->class == PEXTERN)
   728  				return;
   729  			if(nl->class & PHEAP)
   730  				return;
   731  		}
   732  
   733  		tl = nl->type;
   734  		if(tl == T)
   735  			return;
   736  		if(isfat(tl)) {
   737  			clearfat(nl);
   738  			return;
   739  		}
   740  		clearslim(nl);
   741  		return;
   742  	}
   743  
   744  	tl = nl->type;
   745  	if(tl == T)
   746  		return;
   747  
   748  	cgen(nr, nl);
   749  }
   750  
   751  /*
   752   * generate:
   753   *	res = iface{typ, data}
   754   * n->left is typ
   755   * n->right is data
   756   */
   757  void
   758  cgen_eface(Node *n, Node *res)
   759  {
   760  	/* 
   761  	 * the right node of an eface may contain function calls that uses res as an argument,
   762  	 * so it's important that it is done first
   763  	 */
   764  	Node dst;
   765  	dst = *res;
   766  	dst.type = types[tptr];
   767  	dst.xoffset += widthptr;
   768  	cgen(n->right, &dst);
   769  	dst.xoffset -= widthptr;
   770  	cgen(n->left, &dst);
   771  }
   772  
   773  /*
   774   * generate:
   775   *	res = s[lo, hi];
   776   * n->left is s
   777   * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
   778   * caller (cgen) guarantees res is an addable ONAME.
   779   */
   780  void
   781  cgen_slice(Node *n, Node *res)
   782  {
   783  	Node src, dst, *cap, *len, *offs, *add;
   784  
   785  	cap = n->list->n;
   786  	len = n->list->next->n;
   787  	offs = N;
   788  	if(n->list->next->next)
   789  		offs = n->list->next->next->n;
   790  
   791  	// dst.len = hi [ - lo ]
   792  	dst = *res;
   793  	dst.xoffset += Array_nel;
   794  	dst.type = types[simtype[TUINT]];
   795  	cgen(len, &dst);
   796  
   797  	if(n->op != OSLICESTR) {
   798  		// dst.cap = cap [ - lo ]
   799  		dst = *res;
   800  		dst.xoffset += Array_cap;
   801  		dst.type = types[simtype[TUINT]];
   802  		cgen(cap, &dst);
   803  	}
   804  
   805  	// dst.array = src.array  [ + lo *width ]
   806  	dst = *res;
   807  	dst.xoffset += Array_array;
   808  	dst.type = types[TUINTPTR];
   809  
   810  	if(n->op == OSLICEARR) {
   811  		if(!isptr[n->left->type->etype])
   812  			fatal("slicearr is supposed to work on pointer: %+N\n", n);
   813  		checkref(n->left, 0);
   814  	}
   815  
   816  	if(isnil(n->left)) {
   817  		tempname(&src, n->left->type);
   818  		cgen(n->left, &src);
   819  	} else
   820  		src = *n->left;
   821  	src.xoffset += Array_array;
   822  	src.type = types[TUINTPTR];
   823  
   824  	if(offs == N) {
   825  		cgen(&src, &dst);
   826  	} else {
   827  		add = nod(OADD, &src, offs);
   828  		typecheck(&add, Erv);
   829  		cgen(add, &dst);
   830  	}
   831  }
   832  
   833  /*
   834   * gather series of offsets
   835   * >=0 is direct addressed field
   836   * <0 is pointer to next field (+1)
   837   */
   838  int
   839  dotoffset(Node *n, int64 *oary, Node **nn)
   840  {
   841  	int i;
   842  
   843  	switch(n->op) {
   844  	case ODOT:
   845  		if(n->xoffset == BADWIDTH) {
   846  			dump("bad width in dotoffset", n);
   847  			fatal("bad width in dotoffset");
   848  		}
   849  		i = dotoffset(n->left, oary, nn);
   850  		if(i > 0) {
   851  			if(oary[i-1] >= 0)
   852  				oary[i-1] += n->xoffset;
   853  			else
   854  				oary[i-1] -= n->xoffset;
   855  			break;
   856  		}
   857  		if(i < 10)
   858  			oary[i++] = n->xoffset;
   859  		break;
   860  
   861  	case ODOTPTR:
   862  		if(n->xoffset == BADWIDTH) {
   863  			dump("bad width in dotoffset", n);
   864  			fatal("bad width in dotoffset");
   865  		}
   866  		i = dotoffset(n->left, oary, nn);
   867  		if(i < 10)
   868  			oary[i++] = -(n->xoffset+1);
   869  		break;
   870  
   871  	default:
   872  		*nn = n;
   873  		return 0;
   874  	}
   875  	if(i >= 10)
   876  		*nn = N;
   877  	return i;
   878  }
   879  
   880  /*
   881   * make a new off the books
   882   */
   883  void
   884  tempname(Node *nn, Type *t)
   885  {
   886  	Node *n;
   887  	Sym *s;
   888  
   889  	if(curfn == N)
   890  		fatal("no curfn for tempname");
   891  
   892  	if(t == T) {
   893  		yyerror("tempname called with nil type");
   894  		t = types[TINT32];
   895  	}
   896  
   897  	// give each tmp a different name so that there
   898  	// a chance to registerizer them
   899  	snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen);
   900  	statuniqgen++;
   901  	s = lookup(namebuf);
   902  	n = nod(ONAME, N, N);
   903  	n->sym = s;
   904  	s->def = n;
   905  	n->type = t;
   906  	n->class = PAUTO;
   907  	n->addable = 1;
   908  	n->ullman = 1;
   909  	n->esc = EscNever;
   910  	n->curfn = curfn;
   911  	curfn->dcl = list(curfn->dcl, n);
   912  
   913  	dowidth(t);
   914  	n->xoffset = 0;
   915  	*nn = *n;
   916  }
   917  
   918  Node*
   919  temp(Type *t)
   920  {
   921  	Node *n;
   922  	
   923  	n = nod(OXXX, N, N);
   924  	tempname(n, t);
   925  	n->sym->def->used = 1;
   926  	return n;
   927  }