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