1/*	Id: code.c,v 1.27 2016/01/06 16:11:24 ragge Exp 	*/
2/*	$NetBSD: code.c,v 1.1.1.6 2016/02/09 20:28:21 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/*
32 * MIPS port by Jan Enoksson (janeno-1@student.ltu.se) and
33 * Simon Olsson (simols-1@student.ltu.se) 2005.
34 */
35
36#include <assert.h>
37#include "pass1.h"
38
39#ifndef LANG_CXX
40#undef NIL
41#define NIL NULL
42#define NODE P1ND
43#define nfree p1nfree
44#define ccopy p1tcopy
45#define tfree p1tfree
46#endif
47
48/*
49 * Print out assembler segment name.
50 */
51void
52setseg(int seg, char *name)
53{
54	switch (seg) {
55	case PROG: name = ".text"; break;
56	case DATA:
57	case LDATA: name = ".data"; break;
58	case STRNG:
59	case RDATA: name = ".section .rodata"; break;
60	case UDATA: break;
61	case PICLDATA:
62	case PICDATA: name = ".section .data.rel.rw,\"aw\",@progbits"; break;
63	case PICRDATA: name = ".section .data.rel.ro,\"aw\",@progbits"; break;
64	case TLSDATA: name = ".section .tdata,\"awT\",@progbits"; break;
65	case TLSUDATA: name = ".section .tbss,\"awT\",@nobits"; break;
66	case CTORS: name = ".section\t.ctors,\"aw\",@progbits"; break;
67	case DTORS: name = ".section\t.dtors,\"aw\",@progbits"; break;
68	case NMSEG:
69		printf("\t.section %s,\"a%c\",@progbits\n", name,
70		    cftnsp ? 'x' : 'w');
71		return;
72	}
73	printf("\t%s\n", name);
74}
75
76/*
77 * Define everything needed to print out some data (or text).
78 * This means segment, alignment, visibility, etc.
79 */
80void
81defloc(struct symtab *sp)
82{
83	char *n;
84
85	if (ISFTN(sp->stype))
86		return; /* XXX until fixed */
87
88	n = getexname(sp);
89
90	if (sp->sclass == EXTDEF)
91		printf("	.globl %s\n", n);
92	if (sp->slevel == 0) {
93#ifdef USE_GAS
94		printf("\t.type %s,@%s\n", n,
95		    ISFTN(sp->stype) ? "function" : "object");
96		if (!ISFTN(sp->stype))
97			printf("\t.size %s," CONFMT "\n", n,
98			    tsize(sp->stype, sp->sdf, sp->sap));
99#endif
100		printf("%s:\n", n);
101	} else
102		printf(LABFMT ":\n", sp->soffset);
103}
104
105
106/*
107 * cause the alignment to become a multiple of n
108 */
109void
110defalign(int n)
111{
112	n = ispow2(n / SZCHAR);
113	if (n == -1)
114		cerror("defalign: n != 2^i");
115	printf("\t.p2align %d\n", n);
116}
117
118static int rvnr;
119
120/*
121 * code for the end of a function
122 * deals with struct return here
123 */
124void
125efcode(void)
126{
127	NODE *p, *q;
128	int tempnr;
129	int ty;
130
131	if (cftnsp->stype != STRTY+FTN && cftnsp->stype != UNIONTY+FTN)
132		return;
133
134	ty = cftnsp->stype - FTN;
135
136	q = block(REG, NIL, NIL, INCREF(ty), 0, cftnsp->sap);
137	q->n_rval = V0;
138	p = tempnode(0, INCREF(ty), 0, cftnsp->sap);
139	tempnr = regno(p);
140	p = buildtree(ASSIGN, p, q);
141	ecomp(p);
142
143	q = tempnode(tempnr, INCREF(ty), 0, cftnsp->sap);
144	q = buildtree(UMUL, q, NIL);
145
146	p = tempnode(rvnr, INCREF(ty), 0, cftnsp->sap);
147	p = buildtree(UMUL, p, NIL);
148
149	p = buildtree(ASSIGN, p, q);
150	ecomp(p);
151
152	q = tempnode(rvnr, INCREF(ty), 0, cftnsp->sap);
153	p = block(REG, NIL, NIL, INCREF(ty), 0, cftnsp->sap);
154	p->n_rval = V0;
155	p = buildtree(ASSIGN, p, q);
156	ecomp(p);
157}
158
159/* Put a symbol in a temporary
160 * used by bfcode() and its helpers */
161static void
162putintemp(struct symtab *sym)
163{
164	NODE *p;
165	p = tempnode(0, sym->stype, sym->sdf, sym->sap);
166	p = buildtree(ASSIGN, p, nametree(sym));
167	sym->soffset = regno(p->n_left);
168	sym->sflags |= STNODE;
169	ecomp(p);
170}
171
172/* setup the hidden pointer to struct return parameter
173 * used by bfcode() */
174static void
175param_retptr(void)
176{
177	NODE *p, *q;
178
179	p = tempnode(0, PTR+STRTY, 0, cftnsp->sap);
180	rvnr = regno(p);
181	q = block(REG, NIL, NIL, PTR+STRTY, 0, cftnsp->sap);
182	q->n_rval = A0;
183	p = buildtree(ASSIGN, p, q);
184	ecomp(p);
185}
186
187/* setup struct parameter
188 * push the registers out to memory
189 * used by bfcode() */
190static void
191param_struct(struct symtab *sym, int *regp)
192{
193	int reg = *regp;
194	NODE *p, *q;
195	int navail;
196	int sz;
197	int off;
198	int num;
199	int i;
200
201	navail = nargregs - (reg - A0);
202	sz = tsize(sym->stype, sym->sdf, sym->sap) / SZINT;
203	off = ARGINIT/SZINT + (reg - A0);
204	num = sz > navail ? navail : sz;
205	for (i = 0; i < num; i++) {
206		q = block(REG, NIL, NIL, INT, 0, 0);
207		q->n_rval = reg++;
208		p = block(REG, NIL, NIL, INT, 0, 0);
209		p->n_rval = FP;
210		p = block(PLUS, p, bcon(4*off++), INT, 0, 0);
211		p = block(UMUL, p, NIL, INT, 0, 0);
212		p = buildtree(ASSIGN, p, q);
213		ecomp(p);
214	}
215
216	*regp = reg;
217}
218
219/* setup a 64-bit parameter (double/ldouble/longlong)
220 * used by bfcode() */
221static void
222param_64bit(struct symtab *sym, int *regp, int dotemps)
223{
224	int reg = *regp;
225	NODE *p, *q;
226	int navail;
227
228	/* alignment */
229	++reg;
230	reg &= ~1;
231
232	navail = nargregs - (reg - A0);
233
234	if (navail < 2) {
235		/* would have appeared half in registers/half
236		 * on the stack, but alignment ensures it
237		 * appears on the stack */
238		if (dotemps)
239			putintemp(sym);
240		*regp = reg;
241		return;
242	}
243
244	q = block(REG, NIL, NIL, sym->stype, sym->sdf, sym->sap);
245	q->n_rval = A0A1 + (reg - A0);
246	if (dotemps) {
247		p = tempnode(0, sym->stype, sym->sdf, sym->sap);
248		sym->soffset = regno(p);
249		sym->sflags |= STNODE;
250	} else {
251		p = nametree(sym);
252	}
253	p = buildtree(ASSIGN, p, q);
254	ecomp(p);
255	*regp = reg + 2;
256}
257
258/* setup a 32-bit param on the stack
259 * used by bfcode() */
260static void
261param_32bit(struct symtab *sym, int *regp, int dotemps)
262{
263	NODE *p, *q;
264
265	q = block(REG, NIL, NIL, sym->stype, sym->sdf, sym->sap);
266	q->n_rval = (*regp)++;
267	if (dotemps) {
268		p = tempnode(0, sym->stype, sym->sdf, sym->sap);
269		sym->soffset = regno(p);
270		sym->sflags |= STNODE;
271	} else {
272		p = nametree(sym);
273	}
274	p = buildtree(ASSIGN, p, q);
275	ecomp(p);
276}
277
278/*
279 * XXX This is a hack.  We cannot have (l)doubles in more than one
280 * register class.  So we bounce them in and out of temps to
281 * move them in and out of the right registers.
282 */
283static void
284param_double(struct symtab *sym, int *regp, int dotemps)
285{
286	int reg = *regp;
287	NODE *p, *q, *t;
288	int navail;
289	int tmpnr;
290
291	/* alignment */
292	++reg;
293	reg &= ~1;
294
295	navail = nargregs - (reg - A0);
296
297	if (navail < 2) {
298		/* would have appeared half in registers/half
299		 * on the stack, but alignment ensures it
300		 * appears on the stack */
301		if (dotemps)
302			putintemp(sym);
303		*regp = reg;
304		return;
305	}
306
307	t = tempnode(0, LONGLONG, 0, 0);
308	tmpnr = regno(t);
309	q = block(REG, NIL, NIL, LONGLONG, 0, 0);
310	q->n_rval = A0A1 + (reg - A0);
311	p = buildtree(ASSIGN, t, q);
312	ecomp(p);
313
314	if (dotemps) {
315		sym->soffset = tmpnr;
316		sym->sflags |= STNODE;
317	} else {
318		q = tempnode(tmpnr, sym->stype, sym->sdf, sym->sap);
319		p = nametree(sym);
320		p = buildtree(ASSIGN, p, q);
321		ecomp(p);
322	}
323	*regp = reg + 2;
324}
325
326/*
327 * XXX This is a hack.  We cannot have floats in more than one
328 * register class.  So we bounce them in and out of temps to
329 * move them in and out of the right registers.
330 */
331static void
332param_float(struct symtab *sym, int *regp, int dotemps)
333{
334	NODE *p, *q, *t;
335	int tmpnr;
336
337	t = tempnode(0, INT, 0, 0);
338	tmpnr = regno(t);
339	q = block(REG, NIL, NIL, INT, 0, 0);
340	q->n_rval = (*regp)++;
341	p = buildtree(ASSIGN, t, q);
342	ecomp(p);
343
344	if (dotemps) {
345		sym->soffset = tmpnr;
346		sym->sflags |= STNODE;
347	} else {
348		q = tempnode(tmpnr, sym->stype, sym->sdf, sym->sap);
349		p = nametree(sym);
350		p = buildtree(ASSIGN, p, q);
351		ecomp(p);
352	}
353}
354
355/*
356 * code for the beginning of a function; a is an array of
357 * indices in symtab for the arguments; n is the number
358 */
359void
360bfcode(struct symtab **sp, int cnt)
361{
362	union arglist *usym;
363	int lastreg = A0 + nargregs - 1;
364	int saveallargs = 0;
365	int i, reg;
366
367	/*
368	 * Detect if this function has ellipses and save all
369	 * argument register onto stack.
370	 */
371	usym = cftnsp->sdf->dfun;
372	while (usym && usym->type != TNULL) {
373		if (usym->type == TELLIPSIS) {
374			saveallargs = 1;
375			break;
376		}
377		++usym;
378	}
379
380	reg = A0;
381
382	/* assign hidden return structure to temporary */
383	if (cftnsp->stype == STRTY+FTN || cftnsp->stype == UNIONTY+FTN) {
384		param_retptr();
385		++reg;
386	}
387
388        /* recalculate the arg offset and create TEMP moves */
389        for (i = 0; i < cnt; i++) {
390
391		if ((reg > lastreg) && !xtemps)
392			break;
393		else if (reg > lastreg)
394			putintemp(sp[i]);
395		else if (sp[i]->stype == STRTY || sp[i]->stype == UNIONTY)
396			param_struct(sp[i], &reg);
397		else if (DEUNSIGN(sp[i]->stype) == LONGLONG)
398			param_64bit(sp[i], &reg, xtemps && !saveallargs);
399		else if (sp[i]->stype == DOUBLE || sp[i]->stype == LDOUBLE)
400			param_double(sp[i], &reg, xtemps && !saveallargs);
401		else if (sp[i]->stype == FLOAT)
402			param_float(sp[i], &reg, xtemps && !saveallargs);
403		else
404			param_32bit(sp[i], &reg, xtemps && !saveallargs);
405	}
406
407	/* if saveallargs, save the rest of the args onto the stack */
408	if (!saveallargs)
409		return;
410	while (reg <= lastreg) {
411		NODE *p, *q;
412		int off = ARGINIT/SZINT + (reg - A0);
413		q = block(REG, NIL, NIL, INT, 0, 0);
414		q->n_rval = reg++;
415		p = block(REG, NIL, NIL, INT, 0, 0);
416		p->n_rval = FP;
417		p = block(PLUS, p, bcon(4*off), INT, 0, 0);
418		p = block(UMUL, p, NIL, INT, 0, 0);
419		p = buildtree(ASSIGN, p, q);
420		ecomp(p);
421	}
422
423}
424
425
426/* called just before final exit */
427/* flag is 1 if errors, 0 if none */
428void
429ejobcode(int flag)
430{
431}
432
433void
434bjobcode(void)
435{
436	printf("\t.section .mdebug.abi32\n");
437	printf("\t.previous\n");
438
439	/* only if -fpic or -fPIC */
440	if (kflag > 0)
441		printf("\t.abicalls\n");
442}
443
444#ifdef notdef
445/*
446 * Print character t at position i in one string, until t == -1.
447 * Locctr & label is already defined.
448 */
449void
450bycode(int t, int i)
451{
452	static int lastoctal = 0;
453
454	/* put byte i+1 in a string */
455
456	if (t < 0) {
457		if (i != 0)
458			puts("\\000\"");
459	} else {
460		if (i == 0)
461			printf("\t.ascii \"");
462		if (t == 0)
463			return;
464		else if (t == '\\' || t == '"') {
465			lastoctal = 0;
466			putchar('\\');
467			putchar(t);
468		} else if (t == 011) {
469			printf("\\t");
470		} else if (t == 012) {
471			printf("\\n");
472		} else if (t < 040 || t >= 0177) {
473			lastoctal++;
474			printf("\\%o",t);
475		} else if (lastoctal && '0' <= t && t <= '9') {
476			lastoctal = 0;
477			printf("\"\n\t.ascii \"%c", t);
478		} else {
479			lastoctal = 0;
480			putchar(t);
481		}
482	}
483}
484#endif
485
486/* fix up type of field p */
487void
488fldty(struct symtab *p)
489{
490}
491
492/*
493 * XXX - fix genswitch.
494 */
495int
496mygenswitch(int num, TWORD type, struct swents **p, int n)
497{
498	return 0;
499}
500
501
502/* setup call stack with a structure */
503/* called from moveargs() */
504static NODE *
505movearg_struct(NODE *p, NODE *parent, int *regp)
506{
507	int reg = *regp;
508	NODE *l, *q, *t, *r;
509	int tmpnr;
510	int navail;
511	int off;
512	int num;
513        int sz;
514	int ty;
515	int i;
516
517	navail = nargregs - (reg - A0);
518	sz = tsize(p->n_type, p->n_df, p->n_ap) / SZINT;
519	num = sz > navail ? navail : sz;
520
521	l = p->n_left;
522	nfree(p);
523	ty = l->n_type;
524	t = tempnode(0, l->n_type, l->n_df, l->n_ap);
525	tmpnr = regno(t);
526	l = buildtree(ASSIGN, t, l);
527
528	if (p != parent) {
529		q = parent->n_left;
530	} else
531		q = NULL;
532
533	/* copy structure into registers */
534	for (i = 0; i < num; i++) {
535		t = tempnode(tmpnr, ty, 0, 0);
536		t = block(SCONV, t, NIL, PTR+INT, 0, 0);
537		t = block(PLUS, t, bcon(4*i), PTR+INT, 0, 0);
538		t = buildtree(UMUL, t, NIL);
539
540		r = block(REG, NIL, NIL, INT, 0, 0);
541		r->n_rval = reg++;
542
543               	r = buildtree(ASSIGN, r, t);
544		if (q == NULL)
545			q = r;
546		else
547			q = block(CM, q, r, INT, 0, 0);
548	}
549	off = ARGINIT/SZINT + nargregs;
550	for (i = num; i < sz; i++) {
551		t = tempnode(tmpnr, ty, 0, 0);
552		t = block(SCONV, t, NIL, PTR+INT, 0, 0);
553		t = block(PLUS, t, bcon(4*i), PTR+INT, 0, 0);
554		t = buildtree(UMUL, t, NIL);
555
556		r = block(REG, NIL, NIL, INT, 0, 0);
557		r->n_rval = FP;
558		r = block(PLUS, r, bcon(4*off++), INT, 0, 0);
559		r = block(UMUL, r, NIL, INT, 0, 0);
560
561               	r = buildtree(ASSIGN, r, t);
562		if (q == NULL)
563			q = r;
564		else
565			q = block(CM, q, r, INT, 0, 0);
566	}
567
568	if (parent->n_op == CM) {
569		parent->n_left = q;
570		q = l;
571	} else {
572		q = block(CM, q, l, INT, 0, 0);
573	}
574
575	*regp = reg;
576	return q;
577}
578
579/* setup call stack with 64-bit argument */
580/* called from moveargs() */
581static NODE *
582movearg_64bit(NODE *p, int *regp)
583{
584	int reg = *regp;
585	NODE *q;
586	int lastarg;
587
588	/* alignment */
589	++reg;
590	reg &= ~1;
591
592	lastarg = A0 + nargregs - 1;
593	if (reg > lastarg) {
594		*regp = reg;
595		return block(FUNARG, p, NIL, p->n_type, p->n_df, p->n_ap);
596	}
597
598	q = block(REG, NIL, NIL, p->n_type, p->n_df, p->n_ap);
599	q->n_rval = A0A1 + (reg - A0);
600	q = buildtree(ASSIGN, q, p);
601
602	*regp = reg + 2;
603	return q;
604}
605
606/* setup call stack with 32-bit argument */
607/* called from moveargs() */
608static NODE *
609movearg_32bit(NODE *p, int *regp)
610{
611	int reg = *regp;
612	NODE *q;
613
614	q = block(REG, NIL, NIL, p->n_type, p->n_df, p->n_ap);
615	q->n_rval = reg++;
616	q = buildtree(ASSIGN, q, p);
617
618	*regp = reg;
619	return q;
620}
621
622static NODE *
623moveargs(NODE *p, int *regp)
624{
625        NODE *r, **rp;
626	int lastreg;
627	int reg;
628
629        if (p->n_op == CM) {
630                p->n_left = moveargs(p->n_left, regp);
631                r = p->n_right;
632		rp = &p->n_right;
633        } else {
634		r = p;
635		rp = &p;
636	}
637
638 	lastreg = A0 + nargregs - 1;
639        reg = *regp;
640
641	if (reg > lastreg && r->n_op != STARG)
642		*rp = block(FUNARG, r, NIL, r->n_type, r->n_df, r->n_ap);
643	else if (r->n_op == STARG) {
644		*rp = movearg_struct(r, p, regp);
645	} else if (DEUNSIGN(r->n_type) == LONGLONG) {
646		*rp = movearg_64bit(r, regp);
647	} else if (r->n_type == DOUBLE || r->n_type == LDOUBLE) {
648		/* XXX bounce in and out of temporary to change to longlong */
649		NODE *t1 = tempnode(0, LONGLONG, 0, 0);
650		int tmpnr = regno(t1);
651		NODE *t2 = tempnode(tmpnr, r->n_type, r->n_df, r->n_ap);
652		t1 =  movearg_64bit(t1, regp);
653		r = block(ASSIGN, t2, r, r->n_type, r->n_df, r->n_ap);
654		if (p->n_op == CM) {
655			p->n_left = buildtree(CM, p->n_left, t1);
656			p->n_right = r;
657		} else {
658			p = buildtree(CM, t1, r);
659		}
660	} else if (r->n_type == FLOAT) {
661		/* XXX bounce in and out of temporary to change to int */
662		NODE *t1 = tempnode(0, INT, 0, 0);
663		int tmpnr = regno(t1);
664		NODE *t2 = tempnode(tmpnr, r->n_type, r->n_df, r->n_ap);
665		t1 =  movearg_32bit(t1, regp);
666		r = block(ASSIGN, t2, r, r->n_type, r->n_df, r->n_ap);
667		if (p->n_op == CM) {
668			p->n_left = buildtree(CM, p->n_left, t1);
669			p->n_right = r;
670		} else {
671			p = buildtree(CM, t1, r);
672		}
673	} else {
674		*rp = movearg_32bit(r, regp);
675	}
676
677	return p;
678}
679
680/*
681 * Called with a function call with arguments as argument.
682 * This is done early in buildtree() and only done once.
683 */
684NODE *
685funcode(NODE *p)
686{
687	int regnum = A0;
688	NODE *l, *r, *t, *q;
689	int ty;
690
691	l = p->n_left;
692	r = p->n_right;
693
694	/*
695	 * if returning a structure, make the first argument
696	 * a hidden pointer to return structure.
697	 */
698	ty = DECREF(l->n_type);
699	if (ty == STRTY+FTN || ty == UNIONTY+FTN) {
700		ty = DECREF(l->n_type) - FTN;
701		q = tempnode(0, ty, l->n_df, l->n_ap);
702		q = buildtree(ADDROF, q, NIL);
703		if (r->n_op != CM) {
704			p->n_right = block(CM, q, r, INCREF(ty),
705			    l->n_df, l->n_ap);
706		} else {
707			for (t = r; t->n_left->n_op == CM; t = t->n_left)
708				;
709			t->n_left = block(CM, q, t->n_left, INCREF(ty),
710			    l->n_df, l->n_ap);
711		}
712	}
713
714	p->n_right = moveargs(p->n_right, &regnum);
715
716	return p;
717}
718
719NODE *
720builtin_cfa(const struct bitable *bt, NODE *a)
721{
722	uerror("missing builtin_cfa");
723	return bcon(0);
724}
725
726NODE *
727builtin_frame_address(const struct bitable *bt, NODE *a)
728{
729	uerror("missing builtin_frame_address");
730	return bcon(0);
731}
732
733NODE *
734builtin_return_address(const struct bitable *bt, NODE *a)
735{
736	uerror("missing builtin_return_address");
737	return bcon(0);
738}
739
740