xref: /netbsd-src/external/bsd/pcc/dist/pcc/arch/i86/code.c (revision 411dcbec990c8aa9c57d3bd2f4bcacadec0b1ab5)
1 /*	Id: code.c,v 1.2 2014/11/11 07:43:07 ragge Exp 	*/
2 /*	$NetBSD: code.c,v 1.1.1.1 2016/02/09 20:28:37 plunky Exp $	*/
3 /*
4  * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 
31 # include "pass1.h"
32 
33 /*
34  * Print out assembler segment name.
35  */
36 void
setseg(int seg,char * name)37 setseg(int seg, char *name)
38 {
39 	switch (seg) {
40 	case PROG: name = ".TEXT"; break;
41 	case DATA:
42 	case LDATA: name = ".DATA"; break;
43 	case UDATA: break;
44 	case STRNG:
45 	case RDATA: name = ".DATA"; break;
46 	case CTORS: name = ".section\t.ctors,\"aw\",@progbits"; break;
47 	case DTORS: name = ".section\t.dtors,\"aw\",@progbits"; break;
48 	case NMSEG:
49 		printf("\t.section %s,\"a%c\",@progbits\n", name,
50 		    cftnsp ? 'x' : 'w');
51 		return;
52 	}
53 	printf("\t%s\n", name);
54 }
55 
56 /*
57  * Define everything needed to print out some data (or text).
58  * This means segment, alignment, visibility, etc.
59  */
60 void
defloc(struct symtab * sp)61 defloc(struct symtab *sp)
62 {
63 	char *name;
64 
65 	if ((name = sp->soname) == NULL)
66 		name = exname(sp->sname);
67 	if (sp->sclass == EXTDEF) {
68 		printf("	.globl %s\n", name);
69 	}
70 	if (sp->slevel == 0)
71 		printf("%s:\n", name);
72 	else
73 		printf(LABFMT ":\n", sp->soffset);
74 }
75 
76 int structrettemp;
77 
78 /*
79  * code for the end of a function
80  * deals with struct return here
81  */
82 void
efcode(void)83 efcode(void)
84 {
85 	extern int gotnr;
86 	NODE *p, *q;
87 
88 	gotnr = 0;	/* new number for next fun */
89 	if (cftnsp->stype != STRTY+FTN && cftnsp->stype != UNIONTY+FTN)
90 		return;
91 	/* Create struct assignment */
92 	q = tempnode(structrettemp, PTR+STRTY, 0, cftnsp->sap);
93 	q = buildtree(UMUL, q, NIL);
94 	p = block(REG, NIL, NIL, PTR+STRTY, 0, cftnsp->sap);
95 	p = buildtree(UMUL, p, NIL);
96 	p = buildtree(ASSIGN, q, p);
97 	ecomp(p);
98 
99 	/* put hidden arg in ax on return */
100 	q = tempnode(structrettemp, INT, 0, 0);
101 	p = block(REG, NIL, NIL, INT, 0, 0);
102 	regno(p) = AX;
103 	ecomp(buildtree(ASSIGN, p, q));
104 }
105 
106 static TWORD longregs[] = { AXDX, DXCX };
107 static TWORD regpregs[] = { AX, DX, CX };
108 static TWORD charregs[] = { AL, DL, CL };
109 
110 /*
111  * code for the beginning of a function; a is an array of
112  * indices in symtab for the arguments; n is the number
113  *
114  * Classifying args on i386; not simple:
115  * - Args may be on stack or in registers (regparm)
116  * - There may be a hidden first arg, unless OpenBSD struct return.
117  * - Regparm syntax is not well documented.
118  * - There may be stdcall functions, where the called function pops stack
119  * - ...probably more
120  */
121 void
bfcode(struct symtab ** sp,int cnt)122 bfcode(struct symtab **sp, int cnt)
123 {
124 	extern int argstacksize;
125 #ifdef GCC_COMPAT
126 	struct attr *ap;
127 #endif
128 	struct symtab *sp2;
129 	extern int gotnr;
130 	NODE *n, *p;
131 	int i, regparmarg;
132 	int argbase, nrarg, sz;
133 
134 	argbase = ARGINIT;
135 	nrarg = regparmarg = 0;
136 
137 #ifdef GCC_COMPAT
138         if (attr_find(cftnsp->sap, GCC_ATYP_STDCALL) != NULL)
139                 cftnsp->sflags |= SSTDCALL;
140         if ((ap = attr_find(cftnsp->sap, GCC_ATYP_REGPARM)))
141                 regparmarg = ap->iarg(0);
142 #endif
143 
144 	/* Function returns struct, create return arg node */
145 	if (cftnsp->stype == STRTY+FTN || cftnsp->stype == UNIONTY+FTN) {
146 		{
147 			if (regparmarg) {
148 				n = block(REG, 0, 0, INT, 0, 0);
149 				regno(n) = regpregs[nrarg++];
150 			} else {
151 				n = block(OREG, 0, 0, INT, 0, 0);
152 				n->n_lval = argbase/SZCHAR;
153 				argbase += SZINT;
154 				regno(n) = FPREG;
155 			}
156 			p = tempnode(0, INT, 0, 0);
157 			structrettemp = regno(p);
158 			p = buildtree(ASSIGN, p, n);
159 			ecomp(p);
160 		}
161 	}
162 
163 	/*
164 	 * Find where all params are so that they end up at the right place.
165 	 * At the same time recalculate their arg offset on stack.
166 	 * We also get the "pop size" for stdcall.
167 	 */
168 	for (i = 0; i < cnt; i++) {
169 		sp2 = sp[i];
170 		sz = tsize(sp2->stype, sp2->sdf, sp2->sap);
171 
172 		SETOFF(sz, SZINT);
173 
174 		if (cisreg(sp2->stype) == 0 ||
175 		    ((regparmarg - nrarg) * SZINT < sz)) {	/* not in reg */
176 			sp2->soffset = argbase;
177 			argbase += sz;
178 			nrarg = regparmarg;	/* no more in reg either */
179 		} else {					/* in reg */
180 			sp2->soffset = nrarg;
181 			nrarg += sz/SZINT;
182 			sp2->sclass = REGISTER;
183 		}
184 	}
185 
186 	/*
187 	 * Now (argbase - ARGINIT) is used space on stack.
188 	 * Move (if necessary) the args to something new.
189 	 */
190 	for (i = 0; i < cnt; i++) {
191 		int reg, j;
192 
193 		sp2 = sp[i];
194 
195 		if (ISSOU(sp2->stype) && sp2->sclass == REGISTER) {
196 			/* must move to stack */
197 			sz = tsize(sp2->stype, sp2->sdf, sp2->sap);
198 			SETOFF(sz, SZINT);
199 			SETOFF(autooff, SZINT);
200 			reg = sp2->soffset;
201 			sp2->sclass = AUTO;
202 			sp2->soffset = NOOFFSET;
203 			oalloc(sp2, &autooff);
204                         for (j = 0; j < sz/SZCHAR; j += 4) {
205                                 p = block(OREG, 0, 0, INT, 0, 0);
206                                 p->n_lval = sp2->soffset/SZCHAR + j;
207                                 regno(p) = FPREG;
208                                 n = block(REG, 0, 0, INT, 0, 0);
209                                 regno(n) = regpregs[reg++];
210                                 p = block(ASSIGN, p, n, INT, 0, 0);
211                                 ecomp(p);
212                         }
213 		} else if (cisreg(sp2->stype) && !ISSOU(sp2->stype) &&
214 		    ((cqual(sp2->stype, sp2->squal) & VOL) == 0)) {
215 			/* just put rest in temps */
216 			if (sp2->sclass == REGISTER) {
217 				n = block(REG, 0, 0, sp2->stype,
218 				    sp2->sdf, sp2->sap);
219 				if (ISLONGLONG(sp2->stype)|| sp2->stype == LONG || sp2->stype == ULONG)
220 					regno(n) = longregs[sp2->soffset];
221 				else if (DEUNSIGN(sp2->stype) == CHAR || sp2->stype == BOOL)
222 					regno(n) = charregs[sp2->soffset];
223 				else
224 					regno(n) = regpregs[sp2->soffset];
225 			} else {
226                                 n = block(OREG, 0, 0, sp2->stype,
227 				    sp2->sdf, sp2->sap);
228                                 n->n_lval = sp2->soffset/SZCHAR;
229                                 regno(n) = FPREG;
230 			}
231 			p = tempnode(0, sp2->stype, sp2->sdf, sp2->sap);
232 			sp2->soffset = regno(p);
233 			sp2->sflags |= STNODE;
234 			n = buildtree(ASSIGN, p, n);
235 			ecomp(n);
236 		}
237 	}
238 
239         argstacksize = 0;
240         if (cftnsp->sflags & SSTDCALL) {
241 		argstacksize = (argbase - ARGINIT)/SZCHAR;
242         }
243 
244 }
245 
246 
247 /* called just before final exit */
248 /* flag is 1 if errors, 0 if none */
249 void
ejobcode(int flag)250 ejobcode(int flag)
251 {
252 	printf("\t.asciz \"PCC: %s\"\n", VERSSTR);
253 }
254 
255 void
bjobcode(void)256 bjobcode(void)
257 {
258 	astypnames[INT] = astypnames[UNSIGNED] = "\t.long";
259 }
260 
261 /*
262  * Convert FUNARG to assign in case of regparm.
263  */
264 static int regcvt, rparg;
265 static void
addreg(NODE * p)266 addreg(NODE *p)
267 {
268 	TWORD t;
269 	NODE *q;
270 	int sz, r;
271 
272 	sz = tsize(p->n_type, p->n_df, p->n_ap)/SZCHAR;
273 	sz = (sz + 3) >> 2;	/* sz in regs */
274 	if ((regcvt+sz) > rparg) {
275 		regcvt = rparg;
276 		return;
277 	}
278 	if (sz > 2)
279 		uerror("cannot put struct in 3 regs (yet)");
280 
281 	if (sz == 2)
282 		r = regcvt == 0 ? AXDX : DXCX;
283 	else
284 		r = regcvt == 0 ? AX : regcvt == 1 ? DX : CX;
285 
286 	if (p->n_op == FUNARG) {
287 		/* at most 2 regs */
288 		if (p->n_type < INT) {
289 			p->n_left = ccast(p->n_left, INT, 0, 0, 0);
290 			p->n_type = INT;
291 		}
292 
293 		p->n_op = ASSIGN;
294 		p->n_right = p->n_left;
295 	} else if (p->n_op == STARG) {
296 		/* convert to ptr, put in reg */
297 		q = p->n_left;
298 		t = sz == 2 ? LONGLONG : INT;
299 		q = cast(q, INCREF(t), 0);
300 		q = buildtree(UMUL, q, NIL);
301 		p->n_op = ASSIGN;
302 		p->n_type = t;
303 		p->n_right = q;
304 	} else
305 		cerror("addreg");
306 	p->n_left = block(REG, 0, 0, p->n_type, 0, 0);
307 	regno(p->n_left) = r;
308 	regcvt += sz;
309 }
310 
311 /*
312  * Called with a function call with arguments as argument.
313  * This is done early in buildtree() and only done once.
314  * Returns p.
315  */
316 NODE *
funcode(NODE * p)317 funcode(NODE *p)
318 {
319 	extern int gotnr;
320 #ifdef GCC_COMPAT
321 	struct attr *ap;
322 #endif
323 	NODE *r, *l;
324 	TWORD t = DECREF(DECREF(p->n_left->n_type));
325 	int stcall;
326 
327 	stcall = ISSOU(t);
328 	/*
329 	 * We may have to prepend:
330 	 * - Hidden arg0 for struct return (in reg or on stack).
331 	 * - ebx in case of PIC code.
332 	 */
333 
334 	/* Fix function call arguments. On x86, just add funarg */
335 	for (r = p->n_right; r->n_op == CM; r = r->n_left) {
336 		if (r->n_right->n_op != STARG) {
337 			r->n_right = intprom(r->n_right);
338 			r->n_right = block(FUNARG, r->n_right, NIL,
339 			    r->n_right->n_type, r->n_right->n_df,
340 			    r->n_right->n_ap);
341 		}
342 	}
343 	if (r->n_op != STARG) {
344 		l = talloc();
345 		*l = *r;
346 		r->n_op = FUNARG;
347 		r->n_left = l;
348 		r->n_left = intprom(r->n_left);
349 		r->n_type = r->n_left->n_type;
350 	}
351 	if (stcall) {
352 		/* Prepend a placeholder for struct address. */
353 		/* Use BP, can never show up under normal circumstances */
354 		l = talloc();
355 		*l = *r;
356 		r->n_op = CM;
357 		r->n_right = l;
358 		r->n_type = INT;
359 		l = block(REG, 0, 0, INCREF(VOID), 0, 0);
360 		regno(l) = BP;
361 		l = block(FUNARG, l, 0, INCREF(VOID), 0, 0);
362 		r->n_left = l;
363 	}
364 
365 #ifdef GCC_COMPAT
366 	if ((ap = attr_find(p->n_left->n_ap, GCC_ATYP_REGPARM)))
367 		rparg = ap->iarg(0);
368 	else
369 #endif
370 		rparg = 0;
371 
372 	regcvt = 0;
373 	if (rparg)
374 		listf(p->n_right, addreg);
375 
376 	return p;
377 }
378 
379 /* fix up type of field p */
380 void
fldty(struct symtab * p)381 fldty(struct symtab *p)
382 {
383 }
384 
385 /*
386  * XXX - fix genswitch.
387  */
388 int
mygenswitch(int num,TWORD type,struct swents ** p,int n)389 mygenswitch(int num, TWORD type, struct swents **p, int n)
390 {
391 	return 0;
392 }
393 
394 NODE *
builtin_return_address(const struct bitable * bt,NODE * a)395 builtin_return_address(const struct bitable *bt, NODE *a)
396 {
397 	int nframes;
398 	NODE *f;
399 
400 	if (a->n_op != ICON)
401 		goto bad;
402 
403 	nframes = (int)a->n_lval;
404 
405 	tfree(a);
406 
407 	f = block(REG, NIL, NIL, PTR+VOID, 0, 0);
408 	regno(f) = FPREG;
409 
410 	while (nframes--)
411 		f = block(UMUL, f, NIL, PTR+VOID, 0, 0);
412 
413 	f = block(PLUS, f, bcon(2), INCREF(PTR+VOID), 0, 0);
414 	f = buildtree(UMUL, f, NIL);
415 
416 	return f;
417 bad:
418 	uerror("bad argument to __builtin_return_address");
419 	return bcon(0);
420 }
421 
422 NODE *
builtin_frame_address(const struct bitable * bt,NODE * a)423 builtin_frame_address(const struct bitable *bt, NODE *a)
424 {
425 	int nframes;
426 	NODE *f;
427 
428 	if (a->n_op != ICON)
429 		goto bad;
430 
431 	nframes = (int)a->n_lval;
432 
433 	tfree(a);
434 
435 	f = block(REG, NIL, NIL, PTR+VOID, 0, 0);
436 	regno(f) = FPREG;
437 
438 	while (nframes--)
439 		f = block(UMUL, f, NIL, PTR+VOID, 0, 0);
440 
441 	return f;
442 bad:
443 	uerror("bad argument to __builtin_frame_address");
444 	return bcon(0);
445 }
446 
447 /*
448  * Return "canonical frame address".
449  */
450 NODE *
builtin_cfa(const struct bitable * bt,NODE * a)451 builtin_cfa(const struct bitable *bt, NODE *a)
452 {
453 	uerror("missing builtin_cfa");
454 	return bcon(0);
455 }
456 
457