1 /*        Id: code.c,v 1.96 2015/11/17 19:19:40 ragge Exp   */
2 /*        $NetBSD: code.c,v 1.1.1.6 2016/02/09 20:28:17 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 #ifdef LANG_CXX
34 #define   p1listf   listf
35 #define   p1tfree tfree
36 #else
37 #define   NODE P1ND
38 #define   talloc p1alloc
39 #endif
40 
41 /*
42  * Print out assembler segment name.
43  */
44 void
setseg(int seg,char * name)45 setseg(int seg, char *name)
46 {
47           switch (seg) {
48           case PROG: name = ".text"; break;
49           case DATA:
50           case LDATA: name = ".data"; break;
51           case UDATA: break;
52 #ifdef MACHOABI
53           case PICLDATA:
54           case PICDATA: name = ".section .data.rel.rw,\"aw\""; break;
55           case PICRDATA: name = ".section .data.rel.ro,\"aw\""; break;
56           case STRNG: name = ".cstring"; break;
57           case RDATA: name = ".const_data"; break;
58 #else
59           case PICLDATA: name = ".section .data.rel.local,\"aw\",@progbits";break;
60           case PICDATA: name = ".section .data.rel.rw,\"aw\",@progbits"; break;
61           case PICRDATA: name = ".section .data.rel.ro,\"aw\",@progbits"; break;
62           case STRNG:
63 #ifdef AOUTABI
64           case RDATA: name = ".data"; break;
65 #else
66           case RDATA: name = ".section .rodata"; break;
67 #endif
68 #endif
69           case TLSDATA: name = ".section .tdata,\"awT\",@progbits"; break;
70           case TLSUDATA: name = ".section .tbss,\"awT\",@nobits"; break;
71 #ifdef MACHOABI
72           case CTORS: name = ".mod_init_func\n\t.align 2"; break;
73           case DTORS: name = ".mod_term_func\n\t.align 2"; break;
74 #else
75           case CTORS: name = ".section\t.ctors,\"aw\",@progbits"; break;
76           case DTORS: name = ".section\t.dtors,\"aw\",@progbits"; break;
77 #endif
78           case NMSEG:
79                     printf(PRTPREF "\t.section %s,\"a%c\",@progbits\n", name,
80                         cftnsp ? 'x' : 'w');
81                     return;
82           }
83           printf(PRTPREF "\t%s\n", name);
84 }
85 
86 #ifdef MACHOABI
87 void
defalign(int al)88 defalign(int al)
89 {
90           printf(PRTPREF "\t.align %d\n", ispow2(al/ALCHAR));
91 }
92 #endif
93 
94 /*
95  * Define everything needed to print out some data (or text).
96  * This means segment, alignment, visibility, etc.
97  */
98 void
defloc(struct symtab * sp)99 defloc(struct symtab *sp)
100 {
101           char *name;
102 
103           name = getexname(sp);
104           if (sp->sclass == EXTDEF) {
105                     printf(PRTPREF "    .globl %s\n", name);
106 #if defined(ELFABI)
107                     printf(PRTPREF "\t.type %s,@%s\n", name,
108                         ISFTN(sp->stype)? "function" : "object");
109 #endif
110           }
111 #if defined(ELFABI)
112           if (!ISFTN(sp->stype)) {
113                     if (sp->slevel == 0)
114                               printf(PRTPREF "\t.size %s,%d\n", name,
115                                   (int)tsize(sp->stype, sp->sdf, sp->sap)/SZCHAR);
116                     else
117                               printf(PRTPREF "\t.size " LABFMT ",%d\n", sp->soffset,
118                                   (int)tsize(sp->stype, sp->sdf, sp->sap)/SZCHAR);
119           }
120 #endif
121           if (sp->slevel == 0)
122                     printf(PRTPREF "%s:\n", name);
123           else
124                     printf(PRTPREF LABFMT ":\n", sp->soffset);
125 }
126 
127 int structrettemp;
128 
129 /*
130  * code for the end of a function
131  * deals with struct return here
132  */
133 void
efcode(void)134 efcode(void)
135 {
136           extern int gotnr;
137           NODE *p, *q;
138           int sz;
139 
140           gotnr = 0;          /* new number for next fun */
141           if (cftnsp->stype != STRTY+FTN && cftnsp->stype != UNIONTY+FTN)
142                     return;
143 
144           /* struct return for small structs */
145           sz = tsize(BTYPE(cftnsp->stype), cftnsp->sdf, cftnsp->sap);
146 #if defined(os_openbsd)
147           if (sz == SZCHAR || sz == SZSHORT || sz == SZINT || sz == SZLONGLONG) {
148 #else
149           if (sz == SZLONGLONG && attr_find(cftnsp->sap, ATTR_COMPLEX)) {
150 #endif
151                     /* Pointer to struct in eax */
152                     if (sz == SZLONGLONG) {
153                               q = block(OREG, NIL, NIL, INT, 0, 0);
154                               slval(q, 4);
155                               p = block(REG, NIL, NIL, INT, 0, 0);
156                               p->n_rval = EDX;
157                               ecomp(buildtree(ASSIGN, p, q));
158                     }
159                     if (sz < SZSHORT) sz = CHAR;
160                     else if (sz > SZSHORT) sz = INT;
161                     else sz = SHORT;
162                     q = block(OREG, NIL, NIL, sz, 0, 0);
163                     p = block(REG, NIL, NIL, sz, 0, 0);
164                     ecomp(buildtree(ASSIGN, p, q));
165                     return;
166           }
167 
168           /* Create struct assignment */
169           q = tempnode(structrettemp, PTR+STRTY, 0, cftnsp->sap);
170           q = buildtree(UMUL, q, NIL);
171           p = block(REG, NIL, NIL, PTR+STRTY, 0, cftnsp->sap);
172           p = buildtree(UMUL, p, NIL);
173           p = buildtree(ASSIGN, q, p);
174           ecomp(p);
175 
176           /* put hidden arg in eax on return */
177           q = tempnode(structrettemp, INT, 0, 0);
178           p = block(REG, NIL, NIL, INT, 0, 0);
179           regno(p) = EAX;
180           ecomp(buildtree(ASSIGN, p, q));
181 }
182 
183 #ifdef GCC_COMPAT
184 static TWORD reparegs[] = { EAX, EDX, ECX };
185 static TWORD fastregs[] = { ECX, EDX };
186 #endif
187 static TWORD longregs[] = { EAXEDX, EDXECX };
188 static TWORD charregs[] = { AL, DL, CL };
189 static TWORD *regpregs;
190 
191 /*
192  * code for the beginning of a function; a is an array of
193  * indices in symtab for the arguments; n is the number
194  *
195  * Classifying args on i386; not simple:
196  * - Args may be on stack or in registers (regparm)
197  * - There may be a hidden first arg, unless OpenBSD struct return.
198  * - Regparm syntax is not well documented.
199  * - There may be stdcall functions, where the called function pops stack
200  * - ...probably more
201  */
202 void
203 bfcode(struct symtab **sp, int cnt)
204 {
205           extern int argstacksize;
206 #ifdef GCC_COMPAT
207           struct attr *ap;
208 #endif
209           struct symtab *sp2;
210           extern int gotnr;
211           NODE *n, *p;
212           int i, regparmarg;
213           int argbase, nrarg, sz;
214 
215           /* Take care of PIC stuff first */
216         if (kflag) {
217 #define STL     200
218                 char *str = xmalloc(STL);
219 #if !defined(MACHOABI)
220                 int l = getlab();
221 #else
222                 char *name;
223 #endif
224 
225                 /* Generate extended assembler for PIC prolog */
226                 p = tempnode(0, INT, 0, 0);
227                 gotnr = regno(p);
228                 p = block(XARG, p, NIL, INT, 0, 0);
229                 p->n_name = "=g";
230                 p = block(XASM, p, bcon(0), INT, 0, 0);
231 
232 #if defined(MACHOABI)
233                 if ((name = cftnsp->soname) == NULL)
234                         name = cftnsp->sname;
235                 if (snprintf(str, STL, "call L%s$pb\nL%s$pb:\n\tpopl %%0\n",
236                     name, name) >= STL)
237                         cerror("bfcode");
238 #else
239                 if (snprintf(str, STL,
240                     "call " LABFMT ";" LABFMT ":;\tpopl %%0;"
241                     "\taddl $_GLOBAL_OFFSET_TABLE_+[.-" LABFMT "], %%0;",
242                     l, l, l) >= STL)
243                         cerror("bfcode");
244 #endif
245                 p->n_name = addstring(str);
246                 p->n_right->n_type = STRTY;
247                     free(str);
248                 ecomp(p);
249         }
250 
251           argbase = ARGINIT;
252           nrarg = regparmarg = 0;
253           argstacksize = 0;
254 
255 #ifdef GCC_COMPAT
256           regpregs = reparegs;
257         if (attr_find(cftnsp->sap, GCC_ATYP_STDCALL) != NULL)
258                 cftnsp->sflags |= SSTDCALL;
259         if ((ap = attr_find(cftnsp->sap, GCC_ATYP_REGPARM)))
260                 regparmarg = ap->iarg(0);
261         if ((ap = attr_find(cftnsp->sap, GCC_ATYP_FASTCALL)))
262                 regparmarg = 2, regpregs = fastregs;
263 #endif
264 
265           /* Function returns struct, create return arg node */
266           if (cftnsp->stype == STRTY+FTN || cftnsp->stype == UNIONTY+FTN) {
267                     sz = tsize(BTYPE(cftnsp->stype), cftnsp->sdf, cftnsp->sap);
268 #if defined(os_openbsd)
269                     /* OpenBSD uses non-standard return for small structs */
270                     if (sz > SZLONGLONG)
271 #else
272                     if (sz != SZLONGLONG ||
273                         attr_find(cftnsp->sap, ATTR_COMPLEX) == 0)
274 #endif
275                     {
276                               if (regparmarg) {
277                                         n = block(REG, 0, 0, INT, 0, 0);
278                                         regno(n) = regpregs[nrarg++];
279                               } else {
280                                         n = block(OREG, 0, 0, INT, 0, 0);
281                                         slval(n, argbase/SZCHAR);
282                                         argbase += SZINT;
283                                         regno(n) = FPREG;
284                                         argstacksize += 4; /* popped by callee */
285                               }
286                               p = tempnode(0, INT, 0, 0);
287                               structrettemp = regno(p);
288                               p = buildtree(ASSIGN, p, n);
289                               ecomp(p);
290                     }
291           }
292 
293           /*
294            * Find where all params are so that they end up at the right place.
295            * At the same time recalculate their arg offset on stack.
296            * We also get the "pop size" for stdcall.
297            */
298           for (i = 0; i < cnt; i++) {
299                     sp2 = sp[i];
300                     sz = tsize(sp2->stype, sp2->sdf, sp2->sap);
301 
302                     SETOFF(sz, SZINT);
303 
304                     if (cisreg(sp2->stype) == 0 ||
305                         ((regparmarg - nrarg) * SZINT < sz)) {        /* not in reg */
306                               sp2->soffset = argbase;
307                               argbase += sz;
308                               nrarg = regparmarg; /* no more in reg either */
309                     } else {                                          /* in reg */
310                               sp2->soffset = regpregs[nrarg];
311                               nrarg += sz/SZINT;
312                               sp2->sclass = REGISTER;
313                     }
314           }
315 
316           /*
317            * Now (argbase - ARGINIT) is used space on stack.
318            * Move (if necessary) the args to something new.
319            */
320           for (i = 0; i < cnt; i++) {
321                     int reg, j;
322 
323                     sp2 = sp[i];
324 
325                     if ((ISSOU(sp2->stype) && sp2->sclass == REGISTER) ||
326                         (sp2->sclass == REGISTER && xtemps == 0)) {
327                               /* must move to stack */
328                               sz = tsize(sp2->stype, sp2->sdf, sp2->sap);
329                               SETOFF(sz, SZINT);
330                               SETOFF(autooff, SZINT);
331                               reg = sp2->soffset;
332                               sp2->sclass = AUTO;
333                               sp2->soffset = NOOFFSET;
334                               oalloc(sp2, &autooff);
335                         for (j = 0; j < sz/SZCHAR; j += 4) {
336                                 p = block(OREG, 0, 0, INT, 0, 0);
337                                 slval(p, sp2->soffset/SZCHAR + j);
338                                 regno(p) = FPREG;
339                                 n = block(REG, 0, 0, INT, 0, 0);
340                                 regno(n) = regpregs[reg++];
341                                 p = block(ASSIGN, p, n, INT, 0, 0);
342                                 ecomp(p);
343                         }
344                     } else if (cisreg(sp2->stype) && !ISSOU(sp2->stype) &&
345                         ((cqual(sp2->stype, sp2->squal) & VOL) == 0) && xtemps) {
346                               /* just put rest in temps */
347                               if (sp2->sclass == REGISTER) {
348                                         n = block(REG, 0, 0, sp2->stype,
349                                             sp2->sdf, sp2->sap);
350                                         if (ISLONGLONG(sp2->stype))
351                                                   regno(n) = longregs[sp2->soffset];
352                                         else if (DEUNSIGN(sp2->stype) == CHAR ||
353                                             sp2->stype == BOOL)
354                                                   regno(n) = charregs[sp2->soffset];
355                                         else
356                                                   regno(n) = regpregs[sp2->soffset];
357                               } else {
358                                 n = block(OREG, 0, 0, sp2->stype,
359                                             sp2->sdf, sp2->sap);
360                                 slval(n, sp2->soffset/SZCHAR);
361                                 regno(n) = FPREG;
362                               }
363                               p = tempnode(0, sp2->stype, sp2->sdf, sp2->sap);
364                               sp2->soffset = regno(p);
365                               sp2->sflags |= STNODE;
366                               n = buildtree(ASSIGN, p, n);
367                               ecomp(n);
368                     }
369           }
370 
371         if (cftnsp->sflags & SSTDCALL) {
372 #ifdef PECOFFABI
373                 char buf[256];
374                 char *name;
375 #endif
376                     /* XXX interaction STDCALL and struct return? */
377                     argstacksize += (argbase - ARGINIT)/SZCHAR;
378 #ifdef PECOFFABI
379                 /*
380                  * mangle name in symbol table as a callee.
381                  */
382                 if ((name = cftnsp->soname) == NULL)
383                         name = exname(cftnsp->sname);
384                 snprintf(buf, 256, "%s@%d", name, argstacksize);
385                 cftnsp->soname = addname(buf);
386 #endif
387         }
388 
389 }
390 
391 #if defined(MACHOABI)
392 struct stub stublist;
393 struct stub nlplist;
394 #endif
395 
396 /* called just before final exit */
397 /* flag is 1 if errors, 0 if none */
398 void
399 ejobcode(int flag)
400 {
401 #if defined(MACHOABI)
402           /*
403            * iterate over the stublist and output the PIC stubs
404 `          */
405           if (kflag) {
406                     struct stub *p;
407 
408                     DLIST_FOREACH(p, &stublist, link) {
409                               printf(PRTPREF "\t.section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5\n");
410                               printf(PRTPREF "L%s$stub:\n", p->name);
411                               printf(PRTPREF "\t.indirect_symbol %s\n", p->name);
412                               printf(PRTPREF "\thlt ; hlt ; hlt ; hlt ; hlt\n");
413                               printf(PRTPREF "\t.subsections_via_symbols\n");
414                     }
415 
416                     printf(PRTPREF "\t.section __IMPORT,__pointers,non_lazy_symbol_pointers\n");
417                     DLIST_FOREACH(p, &nlplist, link) {
418                               printf(PRTPREF "L%s$non_lazy_ptr:\n", p->name);
419                               printf(PRTPREF "\t.indirect_symbol %s\n", p->name);
420                               printf(PRTPREF "\t.long 0\n");
421                   }
422 
423           }
424 #endif
425 
426           printf(PRTPREF "\t.ident \"PCC: %s\"\n", VERSSTR);
427 }
428 
429 void
430 bjobcode(void)
431 {
432 #ifdef os_sunos
433           astypnames[SHORT] = astypnames[USHORT] = "\t.2byte";
434 #endif
435           astypnames[INT] = astypnames[UNSIGNED] = "\t.long";
436 #if defined(MACHOABI)
437           DLIST_INIT(&stublist, link);
438           DLIST_INIT(&nlplist, link);
439 #endif
440 #if defined(__GNUC__) || defined(__PCC__)
441           /* Be sure that the compiler uses full x87 */
442           /* XXX cross-compiling will fail here */
443           int fcw;
444           __asm("fstcw (%0)" : : "r"(&fcw));
445           fcw |= 0x300;
446           __asm("fldcw (%0)" : : "r"(&fcw));
447 #endif
448 }
449 
450 /*
451  * Convert FUNARG to assign in case of regparm.
452  */
453 static int regcvt, rparg, fcall;
454 static void
455 addreg(NODE *p)
456 {
457           TWORD t;
458           NODE *q;
459           int sz, r;
460 
461           sz = tsize(p->n_type, p->n_df, p->n_ap)/SZCHAR;
462           sz = (sz + 3) >> 2; /* sz in regs */
463           if ((regcvt+sz) > rparg) {
464                     regcvt = rparg;
465                     return;
466           }
467           if (sz > 2)
468                     uerror("cannot put struct in 3 regs (yet)");
469 
470           if (sz == 2)
471                     r = regcvt == 0 ? EAXEDX : EDXECX;
472           else if (fcall)
473                     r = regcvt == 0 ? ECX : EDX;
474           else
475                     r = regcvt == 0 ? EAX : regcvt == 1 ? EDX : ECX;
476 
477           if (p->n_op == FUNARG) {
478                     /* at most 2 regs */
479                     if (p->n_type < INT) {
480                               p->n_left = ccast(p->n_left, INT, 0, 0, 0);
481                               p->n_type = INT;
482                     }
483 
484                     p->n_op = ASSIGN;
485                     p->n_right = p->n_left;
486           } else if (p->n_op == STARG) {
487                     /* convert to ptr, put in reg */
488                     q = p->n_left;
489                     t = sz == 2 ? LONGLONG : INT;
490                     q = cast(q, INCREF(t), 0);
491                     q = buildtree(UMUL, q, NIL);
492                     p->n_op = ASSIGN;
493                     p->n_type = t;
494                     p->n_right = q;
495           } else
496                     cerror("addreg");
497           p->n_left = block(REG, 0, 0, p->n_type, 0, 0);
498           regno(p->n_left) = r;
499           regcvt += sz;
500 }
501 
502 /*
503  * Called with a function call with arguments as argument.
504  * This is done early in buildtree() and only done once.
505  * Returns p.
506  */
507 NODE *
508 funcode(NODE *p)
509 {
510           extern int gotnr;
511           struct attr *ap;
512           NODE *r, *l;
513           TWORD t = DECREF(DECREF(p->n_left->n_type));
514           int stcall;
515 
516           stcall = ISSOU(t);
517           /*
518            * We may have to prepend:
519            * - Hidden arg0 for struct return (in reg or on stack).
520            * - ebx in case of PIC code.
521            */
522 
523           /* Fix function call arguments. On x86, just add funarg */
524           for (r = p->n_right; r->n_op == CM; r = r->n_left) {
525                     if (r->n_right->n_op != STARG)
526                               r->n_right = block(FUNARG, r->n_right, NIL,
527                                   r->n_right->n_type, r->n_right->n_df,
528                                   r->n_right->n_ap);
529           }
530           if (r->n_op != STARG) {
531                     l = talloc();
532                     *l = *r;
533                     r->n_op = FUNARG;
534                     r->n_left = l;
535                     r->n_type = l->n_type;
536           }
537 #ifdef os_openbsd
538           if (stcall && (ap = strattr(p->n_left->n_ap)) &&
539               ap->amsize != SZCHAR && ap->amsize != SZSHORT &&
540               ap->amsize != SZINT && ap->amsize != SZLONGLONG)
541 #else
542           if (stcall &&
543               (attr_find(p->n_left->n_ap, ATTR_COMPLEX) == 0 ||
544                ((ap = strattr(p->n_left->n_ap)) && ap->amsize > SZLONGLONG)))
545 #endif
546           {
547                     /* Prepend a placeholder for struct address. */
548                     /* Use EBP, can never show up under normal circumstances */
549                     l = talloc();
550                     *l = *r;
551                     r->n_op = CM;
552                     r->n_right = l;
553                     r->n_type = INT;
554                     l = block(REG, 0, 0, INCREF(VOID), 0, 0);
555                     regno(l) = EBP;
556                     l = block(FUNARG, l, 0, INCREF(VOID), 0, 0);
557                     r->n_left = l;
558           }
559 
560 #ifdef GCC_COMPAT
561           fcall = 0;
562           if ((ap = attr_find(p->n_left->n_ap, GCC_ATYP_REGPARM)))
563                     rparg = ap->iarg(0);
564           else if ((ap = attr_find(p->n_left->n_ap, GCC_ATYP_FASTCALL)))
565                     fcall = rparg = 2;
566           else
567 #endif
568                     rparg = 0;
569 
570           regcvt = 0;
571           if (rparg)
572                     p1listf(p->n_right, addreg);
573 
574           if (kflag == 0)
575                     return p;
576 
577 #if defined(ELFABI)
578           /* Create an ASSIGN node for ebx */
579           l = block(REG, NIL, NIL, INT, 0, 0);
580           l->n_rval = EBX;
581           l = buildtree(ASSIGN, l, tempnode(gotnr, INT, 0, 0));
582           if (p->n_right->n_op != CM) {
583                     p->n_right = block(CM, l, p->n_right, INT, 0, 0);
584           } else {
585                     for (r = p->n_right; r->n_left->n_op == CM; r = r->n_left)
586                               ;
587                     r->n_left = block(CM, l, r->n_left, INT, 0, 0);
588           }
589 #endif
590           return p;
591 }
592 
593 /* fix up type of field p */
594 void
595 fldty(struct symtab *p)
596 {
597 }
598 
599 /*
600  * XXX - fix genswitch.
601  */
602 int
603 mygenswitch(int num, TWORD type, struct swents **p, int n)
604 {
605           return 0;
606 }
607 
608 NODE *
609 builtin_return_address(const struct bitable *bt, NODE *a)
610 {
611           int nframes;
612           NODE *f;
613 
614           if (a->n_op != ICON)
615                     goto bad;
616 
617           nframes = (int)glval(a);
618 
619           p1tfree(a);
620 
621           f = block(REG, NIL, NIL, PTR+VOID, 0, 0);
622           regno(f) = FPREG;
623 
624           while (nframes--)
625                     f = block(UMUL, f, NIL, PTR+VOID, 0, 0);
626 
627           f = block(PLUS, f, bcon(4), INCREF(PTR+VOID), 0, 0);
628           f = buildtree(UMUL, f, NIL);
629 
630           return f;
631 bad:
632           uerror("bad argument to __builtin_return_address");
633           return bcon(0);
634 }
635 
636 NODE *
637 builtin_frame_address(const struct bitable *bt, NODE *a)
638 {
639           int nframes;
640           NODE *f;
641 
642           if (a->n_op != ICON)
643                     goto bad;
644 
645           nframes = (int)glval(a);
646 
647           p1tfree(a);
648 
649           f = block(REG, NIL, NIL, PTR+VOID, 0, 0);
650           regno(f) = FPREG;
651 
652           while (nframes--)
653                     f = block(UMUL, f, NIL, PTR+VOID, 0, 0);
654 
655           return f;
656 bad:
657           uerror("bad argument to __builtin_frame_address");
658           return bcon(0);
659 }
660 
661 /*
662  * Return "canonical frame address".
663  */
664 NODE *
665 builtin_cfa(const struct bitable *bt, NODE *a)
666 {
667           uerror("missing builtin_cfa");
668           return bcon(0);
669 }
670 
671