github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/cmd/cc/pgen.c (about)

     1  // Inferno utils/6c/sgen.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/6c/sgen.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  //	Portions Copyright © 2004,2006 Bruce Ellis
     9  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  //	Portions Copyright © 2009 The Go Authors.  All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  #include "gc.h"
    32  #include "../../pkg/runtime/funcdata.h"
    33  
    34  enum { BitsPerPointer = 2 };
    35  
    36  static void dumpgcargs(Type *fn, Sym *sym);
    37  
    38  int
    39  hasdotdotdot(void)
    40  {
    41  	Type *t;
    42  
    43  	for(t=thisfn->down; t!=T; t=t->down)
    44  		if(t->etype == TDOT)
    45  			return 1;
    46  	return 0;
    47  }
    48  
    49  vlong
    50  argsize(void)
    51  {
    52  	Type *t;
    53  	int32 s;
    54  
    55  //print("t=%T\n", thisfn);
    56  	s = align(0, thisfn->link, Aarg0, nil);
    57  	for(t=thisfn->down; t!=T; t=t->down) {
    58  		switch(t->etype) {
    59  		case TVOID:
    60  			break;
    61  		case TDOT:
    62  			if((textflag & NOSPLIT) == 0)
    63  				yyerror("function takes ... without textflag NOSPLIT");
    64  			return ArgsSizeUnknown;
    65  		default:
    66  			s = align(s, t, Aarg1, nil);
    67  			s = align(s, t, Aarg2, nil);
    68  			break;
    69  		}
    70  //print("	%d %T\n", s, t);
    71  	}
    72  	if(thechar == '6')
    73  		s = (s+7) & ~7;
    74  	else
    75  		s = (s+3) & ~3;
    76  	return s;
    77  }
    78  
    79  void
    80  codgen(Node *n, Node *nn)
    81  {
    82  	Prog *sp;
    83  	Node *n1, nod, nod1, nod2;
    84  	Sym *gcsym, *gclocalssym;
    85  	static int ngcsym, ngclocalssym;
    86  	static char namebuf[40];
    87  
    88  	cursafe = 0;
    89  	curarg = 0;
    90  	maxargsafe = 0;
    91  
    92  	/*
    93  	 * isolate name
    94  	 */
    95  	for(n1 = nn;; n1 = n1->left) {
    96  		if(n1 == Z) {
    97  			diag(nn, "can't find function name");
    98  			return;
    99  		}
   100  		if(n1->op == ONAME)
   101  			break;
   102  	}
   103  	nearln = nn->lineno;
   104  
   105  	p = gtext(n1->sym, stkoff);
   106  	sp = p;
   107  
   108  	/*
   109  	 * generate funcdata symbol for this function.
   110  	 * data is filled in at the end of codgen().
   111  	 */
   112  	snprint(namebuf, sizeof namebuf, "gc·%d", ngcsym++);
   113  	gcsym = slookup(namebuf);
   114  	gcsym->class = CSTATIC;
   115  
   116  	memset(&nod, 0, sizeof nod);
   117  	nod.op = ONAME;
   118  	nod.sym = gcsym;
   119  	nod.class = CSTATIC;
   120  	gins(AFUNCDATA, nodconst(FUNCDATA_GCArgs), &nod);
   121  
   122  	snprint(namebuf, sizeof(namebuf), "gclocalssym·%d", ngclocalssym++);
   123  	gclocalssym = slookup(namebuf);
   124  	gclocalssym->class = CSTATIC;
   125  
   126  	memset(&nod2, 0, sizeof(nod2));
   127  	nod2.op = ONAME;
   128  	nod2.sym = gclocalssym;
   129  	nod2.class = CSTATIC;
   130  	gins(AFUNCDATA, nodconst(FUNCDATA_GCLocals), &nod2);
   131  
   132  	/*
   133  	 * isolate first argument
   134  	 */
   135  	if(REGARG >= 0) {
   136  		if(typesuv[thisfn->link->etype]) {
   137  			nod1 = *nodret->left;
   138  			nodreg(&nod, &nod1, REGARG);
   139  			gmove(&nod, &nod1);
   140  		} else
   141  		if(firstarg && typechlp[firstargtype->etype]) {
   142  			nod1 = *nodret->left;
   143  			nod1.sym = firstarg;
   144  			nod1.type = firstargtype;
   145  			nod1.xoffset = align(0, firstargtype, Aarg1, nil);
   146  			nod1.etype = firstargtype->etype;
   147  			nodreg(&nod, &nod1, REGARG);
   148  			gmove(&nod, &nod1);
   149  		}
   150  	}
   151  
   152  	retok = 0;
   153  
   154  	canreach = 1;
   155  	warnreach = 1;
   156  	gen(n);
   157  	if(canreach && thisfn->link->etype != TVOID)
   158  		diag(Z, "no return at end of function: %s", n1->sym->name);
   159  	noretval(3);
   160  	gbranch(ORETURN);
   161  
   162  	if(!debug['N'] || debug['R'] || debug['P'])
   163  		regopt(sp);
   164  
   165  	if(thechar=='6' || thechar=='7')	/* [sic] */
   166  		maxargsafe = xround(maxargsafe, 8);
   167  	sp->to.offset += maxargsafe;
   168  
   169  	dumpgcargs(thisfn, gcsym);
   170  
   171  	// TODO(rsc): "stkoff" is not right. It does not account for
   172  	// the possibility of data stored in .safe variables.
   173  	// Unfortunately those move up and down just like
   174  	// the argument frame (and in fact dovetail with it)
   175  	// so the number we need is not available or even
   176  	// well-defined. Probably we need to make the safe
   177  	// area its own section.
   178  	// That said, we've been using stkoff for months
   179  	// and nothing too terrible has happened.
   180  	gextern(gclocalssym, nodconst(-stkoff), 0, 4); // locals
   181  	gclocalssym->type = typ(0, T);
   182  	gclocalssym->type->width = 4;
   183  }
   184  
   185  void
   186  supgen(Node *n)
   187  {
   188  	int owarn;
   189  	long spc;
   190  	Prog *sp;
   191  
   192  	if(n == Z)
   193  		return;
   194  	suppress++;
   195  	owarn = warnreach;
   196  	warnreach = 0;
   197  	spc = pc;
   198  	sp = lastp;
   199  	gen(n);
   200  	lastp = sp;
   201  	pc = spc;
   202  	sp->link = nil;
   203  	suppress--;
   204  	warnreach = owarn;
   205  }
   206  
   207  void
   208  gen(Node *n)
   209  {
   210  	Node *l, nod;
   211  	Prog *sp, *spc, *spb;
   212  	Case *cn;
   213  	long sbc, scc;
   214  	int snbreak, sncontin;
   215  	int f, o, oldreach;
   216  
   217  loop:
   218  	if(n == Z)
   219  		return;
   220  	nearln = n->lineno;
   221  	o = n->op;
   222  	if(debug['G'])
   223  		if(o != OLIST)
   224  			print("%L %O\n", nearln, o);
   225  
   226  	if(!canreach) {
   227  		switch(o) {
   228  		case OLABEL:
   229  		case OCASE:
   230  		case OLIST:
   231  		case OBREAK:
   232  		case OFOR:
   233  		case OWHILE:
   234  		case ODWHILE:
   235  			/* all handled specially - see switch body below */
   236  			break;
   237  		default:
   238  			if(warnreach) {
   239  				warn(n, "unreachable code %O", o);
   240  				warnreach = 0;
   241  			}
   242  		}
   243  	}
   244  
   245  	switch(o) {
   246  
   247  	default:
   248  		complex(n);
   249  		cgen(n, Z);
   250  		break;
   251  
   252  	case OLIST:
   253  		gen(n->left);
   254  
   255  	rloop:
   256  		n = n->right;
   257  		goto loop;
   258  
   259  	case ORETURN:
   260  		canreach = 0;
   261  		warnreach = !suppress;
   262  		complex(n);
   263  		if(n->type == T)
   264  			break;
   265  		l = n->left;
   266  		if(l == Z) {
   267  			noretval(3);
   268  			gbranch(ORETURN);
   269  			break;
   270  		}
   271  		if(typecmplx[n->type->etype]) {
   272  			sugen(l, nodret, n->type->width);
   273  			noretval(3);
   274  			gbranch(ORETURN);
   275  			break;
   276  		}
   277  		regret(&nod, n);
   278  		cgen(l, &nod);
   279  		regfree(&nod);
   280  		if(typefd[n->type->etype])
   281  			noretval(1);
   282  		else
   283  			noretval(2);
   284  		gbranch(ORETURN);
   285  		break;
   286  
   287  	case OLABEL:
   288  		canreach = 1;
   289  		l = n->left;
   290  		if(l) {
   291  			l->pc = pc;
   292  			if(l->label)
   293  				patch(l->label, pc);
   294  		}
   295  		gbranch(OGOTO);	/* prevent self reference in reg */
   296  		patch(p, pc);
   297  		goto rloop;
   298  
   299  	case OGOTO:
   300  		canreach = 0;
   301  		warnreach = !suppress;
   302  		n = n->left;
   303  		if(n == Z)
   304  			return;
   305  		if(n->complex == 0) {
   306  			diag(Z, "label undefined: %s", n->sym->name);
   307  			return;
   308  		}
   309  		if(suppress)
   310  			return;
   311  		gbranch(OGOTO);
   312  		if(n->pc) {
   313  			patch(p, n->pc);
   314  			return;
   315  		}
   316  		if(n->label)
   317  			patch(n->label, pc-1);
   318  		n->label = p;
   319  		return;
   320  
   321  	case OCASE:
   322  		canreach = 1;
   323  		l = n->left;
   324  		if(cases == C)
   325  			diag(n, "case/default outside a switch");
   326  		if(l == Z) {
   327  			newcase();
   328  			cases->val = 0;
   329  			cases->def = 1;
   330  			cases->label = pc;
   331  			cases->isv = 0;
   332  			goto rloop;
   333  		}
   334  		complex(l);
   335  		if(l->type == T)
   336  			goto rloop;
   337  		if(l->op == OCONST)
   338  		if(typeword[l->type->etype] && l->type->etype != TIND) {
   339  			newcase();
   340  			cases->val = l->vconst;
   341  			cases->def = 0;
   342  			cases->label = pc;
   343  			cases->isv = typev[l->type->etype];
   344  			goto rloop;
   345  		}
   346  		diag(n, "case expression must be integer constant");
   347  		goto rloop;
   348  
   349  	case OSWITCH:
   350  		l = n->left;
   351  		complex(l);
   352  		if(l->type == T)
   353  			break;
   354  		if(!typechlvp[l->type->etype] || l->type->etype == TIND) {
   355  			diag(n, "switch expression must be integer");
   356  			break;
   357  		}
   358  
   359  		gbranch(OGOTO);		/* entry */
   360  		sp = p;
   361  
   362  		cn = cases;
   363  		cases = C;
   364  		newcase();
   365  
   366  		sbc = breakpc;
   367  		breakpc = pc;
   368  		snbreak = nbreak;
   369  		nbreak = 0;
   370  		gbranch(OGOTO);
   371  		spb = p;
   372  
   373  		gen(n->right);		/* body */
   374  		if(canreach){
   375  			gbranch(OGOTO);
   376  			patch(p, breakpc);
   377  			nbreak++;
   378  		}
   379  
   380  		patch(sp, pc);
   381  		doswit(l);
   382  		patch(spb, pc);
   383  
   384  		cases = cn;
   385  		breakpc = sbc;
   386  		canreach = nbreak!=0;
   387  		if(canreach == 0)
   388  			warnreach = !suppress;
   389  		nbreak = snbreak;
   390  		break;
   391  
   392  	case OWHILE:
   393  	case ODWHILE:
   394  		l = n->left;
   395  		gbranch(OGOTO);		/* entry */
   396  		sp = p;
   397  
   398  		scc = continpc;
   399  		continpc = pc;
   400  		gbranch(OGOTO);
   401  		spc = p;
   402  
   403  		sbc = breakpc;
   404  		breakpc = pc;
   405  		snbreak = nbreak;
   406  		nbreak = 0;
   407  		gbranch(OGOTO);
   408  		spb = p;
   409  
   410  		patch(spc, pc);
   411  		if(n->op == OWHILE)
   412  			patch(sp, pc);
   413  		bcomplex(l, Z);		/* test */
   414  		patch(p, breakpc);
   415  		if(l->op != OCONST || vconst(l) == 0)
   416  			nbreak++;
   417  
   418  		if(n->op == ODWHILE)
   419  			patch(sp, pc);
   420  		gen(n->right);		/* body */
   421  		gbranch(OGOTO);
   422  		patch(p, continpc);
   423  
   424  		patch(spb, pc);
   425  		continpc = scc;
   426  		breakpc = sbc;
   427  		canreach = nbreak!=0;
   428  		if(canreach == 0)
   429  			warnreach = !suppress;
   430  		nbreak = snbreak;
   431  		break;
   432  
   433  	case OFOR:
   434  		l = n->left;
   435  		if(!canreach && l->right->left && warnreach) {
   436  			warn(n, "unreachable code FOR");
   437  			warnreach = 0;
   438  		}
   439  		gen(l->right->left);	/* init */
   440  		gbranch(OGOTO);		/* entry */
   441  		sp = p;
   442  
   443  		/*
   444  		 * if there are no incoming labels in the
   445  		 * body and the top's not reachable, warn
   446  		 */
   447  		if(!canreach && warnreach && deadheads(n)) {
   448  			warn(n, "unreachable code %O", o);
   449  			warnreach = 0;
   450  		}
   451  
   452  		scc = continpc;
   453  		continpc = pc;
   454  		gbranch(OGOTO);
   455  		spc = p;
   456  
   457  		sbc = breakpc;
   458  		breakpc = pc;
   459  		snbreak = nbreak;
   460  		nbreak = 0;
   461  		sncontin = ncontin;
   462  		ncontin = 0;
   463  		gbranch(OGOTO);
   464  		spb = p;
   465  
   466  		patch(spc, pc);
   467  		gen(l->right->right);	/* inc */
   468  		patch(sp, pc);
   469  		if(l->left != Z) {	/* test */
   470  			bcomplex(l->left, Z);
   471  			patch(p, breakpc);
   472  			if(l->left->op != OCONST || vconst(l->left) == 0)
   473  				nbreak++;
   474  		}
   475  		canreach = 1;
   476  		gen(n->right);		/* body */
   477  		if(canreach){
   478  			gbranch(OGOTO);
   479  			patch(p, continpc);
   480  			ncontin++;
   481  		}
   482  		if(!ncontin && l->right->right && warnreach) {
   483  			warn(l->right->right, "unreachable FOR inc");
   484  			warnreach = 0;
   485  		}
   486  
   487  		patch(spb, pc);
   488  		continpc = scc;
   489  		breakpc = sbc;
   490  		canreach = nbreak!=0;
   491  		if(canreach == 0)
   492  			warnreach = !suppress;
   493  		nbreak = snbreak;
   494  		ncontin = sncontin;
   495  		break;
   496  
   497  	case OCONTINUE:
   498  		if(continpc < 0) {
   499  			diag(n, "continue not in a loop");
   500  			break;
   501  		}
   502  		gbranch(OGOTO);
   503  		patch(p, continpc);
   504  		ncontin++;
   505  		canreach = 0;
   506  		warnreach = !suppress;
   507  		break;
   508  
   509  	case OBREAK:
   510  		if(breakpc < 0) {
   511  			diag(n, "break not in a loop");
   512  			break;
   513  		}
   514  		/*
   515  		 * Don't complain about unreachable break statements.
   516  		 * There are breaks hidden in yacc's output and some people
   517  		 * write return; break; in their switch statements out of habit.
   518  		 * However, don't confuse the analysis by inserting an
   519  		 * unreachable reference to breakpc either.
   520  		 */
   521  		if(!canreach)
   522  			break;
   523  		gbranch(OGOTO);
   524  		patch(p, breakpc);
   525  		nbreak++;
   526  		canreach = 0;
   527  		warnreach = !suppress;
   528  		break;
   529  
   530  	case OIF:
   531  		l = n->left;
   532  		if(bcomplex(l, n->right)) {
   533  			if(typefd[l->type->etype])
   534  				f = !l->fconst;
   535  			else
   536  				f = !l->vconst;
   537  			if(debug['c'])
   538  				print("%L const if %s\n", nearln, f ? "false" : "true");
   539  			if(f) {
   540  				canreach = 1;
   541  				supgen(n->right->left);
   542  				oldreach = canreach;
   543  				canreach = 1;
   544  				gen(n->right->right);
   545  				/*
   546  				 * treat constant ifs as regular ifs for
   547  				 * reachability warnings.
   548  				 */
   549  				if(!canreach && oldreach && debug['w'] < 2)
   550  					warnreach = 0;
   551  			}
   552  			else {
   553  				canreach = 1;
   554  				gen(n->right->left);
   555  				oldreach = canreach;
   556  				canreach = 1;
   557  				supgen(n->right->right);
   558  				/*
   559  				 * treat constant ifs as regular ifs for
   560  				 * reachability warnings.
   561  				 */
   562  				if(!oldreach && canreach && debug['w'] < 2)
   563  					warnreach = 0;
   564  				canreach = oldreach;
   565  			}
   566  		}
   567  		else {
   568  			sp = p;
   569  			canreach = 1;
   570  			if(n->right->left != Z)
   571  				gen(n->right->left);
   572  			oldreach = canreach;
   573  			canreach = 1;
   574  			if(n->right->right != Z) {
   575  				gbranch(OGOTO);
   576  				patch(sp, pc);
   577  				sp = p;
   578  				gen(n->right->right);
   579  			}
   580  			patch(sp, pc);
   581  			canreach = canreach || oldreach;
   582  			if(canreach == 0)
   583  				warnreach = !suppress;
   584  		}
   585  		break;
   586  
   587  	case OSET:
   588  	case OUSED:
   589  	case OPREFETCH:
   590  		usedset(n->left, o);
   591  		break;
   592  	}
   593  }
   594  
   595  void
   596  usedset(Node *n, int o)
   597  {
   598  	if(n->op == OLIST) {
   599  		usedset(n->left, o);
   600  		usedset(n->right, o);
   601  		return;
   602  	}
   603  	complex(n);
   604  	if(o == OPREFETCH) {
   605  		gprefetch(n);
   606  		return;
   607  	}
   608  	switch(n->op) {
   609  	case OADDR:	/* volatile */
   610  		gins(ANOP, n, Z);
   611  		break;
   612  	case ONAME:
   613  		if(o == OSET)
   614  			gins(ANOP, Z, n);
   615  		else
   616  			gins(ANOP, n, Z);
   617  		break;
   618  	}
   619  }
   620  
   621  int
   622  bcomplex(Node *n, Node *c)
   623  {
   624  	Node *b, nod;
   625  
   626  	complex(n);
   627  	if(n->type != T)
   628  	if(tcompat(n, T, n->type, tnot))
   629  		n->type = T;
   630  	if(n->type == T) {
   631  		gbranch(OGOTO);
   632  		return 0;
   633  	}
   634  	if(c != Z && n->op == OCONST && deadheads(c))
   635  		return 1;
   636  	if(typev[n->type->etype] && machcap(Z)) {
   637  		b = &nod;
   638  		b->op = ONE;
   639  		b->left = n;
   640  		b->right = new(0, Z, Z);
   641  		*b->right = *nodconst(0);
   642  		b->right->type = n->type;
   643  		b->type = types[TLONG];
   644  		n = b;
   645  	}
   646  	bool64(n);
   647  	boolgen(n, 1, Z);
   648  	return 0;
   649  }
   650  
   651  // Updates the bitvector with a set bit for each pointer containing
   652  // value in the type description starting at offset.
   653  static void
   654  walktype1(Type *t, int32 offset, Bvec *bv, int param)
   655  {
   656  	Type *t1;
   657  	int32 o;
   658  
   659  	switch(t->etype) {
   660  	case TCHAR:
   661  	case TUCHAR:
   662  	case TSHORT:
   663  	case TUSHORT:
   664  	case TINT:
   665  	case TUINT:
   666  	case TLONG:
   667  	case TULONG:
   668  	case TVLONG:
   669  	case TUVLONG:
   670  	case TFLOAT:
   671  	case TDOUBLE:
   672  		// non-pointer types
   673  		break;
   674  
   675  	case TIND:
   676  	pointer:
   677  		// pointer types
   678  		if((offset + t->offset) % ewidth[TIND] != 0)
   679  			yyerror("unaligned pointer");
   680  		bvset(bv, ((offset + t->offset) / ewidth[TIND])*BitsPerPointer);
   681  		break;
   682  
   683  	case TARRAY:
   684  		if(param)	// unlike Go, C passes arrays by reference
   685  			goto pointer;
   686  		// array in struct or union is an actual array
   687  		for(o = 0; o < t->width; o += t->link->width)
   688  			walktype1(t->link, offset+o, bv, 0);
   689  		break;
   690  
   691  	case TSTRUCT:
   692  		// build map recursively
   693  		for(t1 = t->link; t1 != T; t1 = t1->down)
   694  			walktype1(t1, offset, bv, 0);
   695  		break;
   696  
   697  	case TUNION:
   698  		walktype1(t->link, offset, bv, 0);
   699  		break;
   700  
   701  	default:
   702  		yyerror("can't handle arg type %s\n", tnames[t->etype]);
   703  	}
   704  }
   705  
   706  // Compute a bit vector to describe the pointer containing locations
   707  // in the argument list.  Adds the data to gcsym and returns the offset
   708  // of end of the bit vector.
   709  static void
   710  dumpgcargs(Type *fn, Sym *sym)
   711  {
   712  	Bvec *bv;
   713  	Type *t;
   714  	int32 i;
   715  	int32 argbytes;
   716  	int32 symoffset, argoffset;
   717  
   718  	if(hasdotdotdot()) {
   719  		// give up for C vararg functions.
   720  		// TODO: maybe make a map just for the args we do know?
   721  		gextern(sym, nodconst(0), 0, 4); // nptrs=0
   722  		symoffset = 4;
   723  	} else {
   724  		argbytes = (argsize() + ewidth[TIND] - 1);
   725  		bv = bvalloc((argbytes  / ewidth[TIND]) * BitsPerPointer);
   726  		argoffset = align(0, fn->link, Aarg0, nil);
   727  		if(argoffset > 0) {
   728  			// The C calling convention returns structs by
   729  			// copying them to a location pointed to by a
   730  			// hidden first argument.  This first argument
   731  			// is a pointer.
   732  			if(argoffset != ewidth[TIND])
   733  				yyerror("passbyptr arg not the right size");
   734  			bvset(bv, 0);
   735  		}
   736  		for(t = fn->down; t != T; t = t->down) {
   737  			if(t->etype == TVOID)
   738  				continue;
   739  			argoffset = align(argoffset, t, Aarg1, nil);
   740  			walktype1(t, argoffset, bv, 1);
   741  			argoffset = align(argoffset, t, Aarg2, nil);
   742  		}
   743  		gextern(sym, nodconst(bv->n), 0, 4);
   744  		symoffset = 4;
   745  		for(i = 0; i < bv->n; i += 32) {
   746  			gextern(sym, nodconst(bv->b[i/32]), symoffset, 4);
   747  			symoffset += 4;
   748  		}
   749  		free(bv);
   750  	}
   751  	sym->type = typ(0, T);
   752  	sym->type->width = symoffset;
   753  }