xref: /plan9-contrib/sys/src/cmd/5l/span.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 #include	"l.h"
2 
3 static struct {
4 	ulong	start;
5 	ulong	size;
6 } pool;
7 
8 void	checkpool(Prog*);
9 void 	flushpool(Prog*, int);
10 
11 void
12 span(void)
13 {
14 	Prog *p;
15 	Sym *setext, *s;
16 	Optab *o;
17 	int m, bflag, i;
18 	long c, otxt, v;
19 
20 	if(debug['v'])
21 		Bprint(&bso, "%5.2f span\n", cputime());
22 	Bflush(&bso);
23 
24 	bflag = 0;
25 	c = INITTEXT;
26 	otxt = c;
27 	for(p = firstp; p != P; p = p->link) {
28 		p->pc = c;
29 		o = oplook(p);
30 		m = o->size;
31 		if(m == 0) {
32 			if(p->as == ATEXT) {
33 				curtext = p;
34 				autosize = p->to.offset + 4;
35 				if(p->from.sym != S)
36 					p->from.sym->value = c;
37 				/* need passes to resolve branches */
38 				if(c-otxt >= 1L<<17)
39 					bflag = 1;
40 				otxt = c;
41 				continue;
42 			}
43 			diag("zero-width instruction\n%P\n", p);
44 			continue;
45 		}
46 		switch(o->flag & (LFROM|LTO|LPOOL)) {
47 		case LFROM:
48 			addpool(p, &p->from);
49 			break;
50 		case LTO:
51 			addpool(p, &p->to);
52 			break;
53 		case LPOOL:
54 			if ((p->scond&C_SCOND) == 14)
55 				flushpool(p, 0);
56 			break;
57 		}
58 		if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14)
59 			flushpool(p, 0);
60 		c += m;
61 		if(blitrl)
62 			checkpool(p);
63 	}
64 
65 	/*
66 	 * if any procedure is large enough to
67 	 * generate a large SBRA branch, then
68 	 * generate extra passes putting branches
69 	 * around jmps to fix. this is rare.
70 	 */
71 	while(bflag) {
72 		if(debug['v'])
73 			Bprint(&bso, "%5.2f span1\n", cputime());
74 		bflag = 0;
75 		c = INITTEXT;
76 		for(p = firstp; p != P; p = p->link) {
77 			p->pc = c;
78 			o = oplook(p);
79 /* very larg branches
80 			if(o->type == 6 && p->cond) {
81 				otxt = p->cond->pc - c;
82 				if(otxt < 0)
83 					otxt = -otxt;
84 				if(otxt >= (1L<<17) - 10) {
85 					q = prg();
86 					q->link = p->link;
87 					p->link = q;
88 					q->as = AB;
89 					q->to.type = D_BRANCH;
90 					q->cond = p->cond;
91 					p->cond = q;
92 					q = prg();
93 					q->link = p->link;
94 					p->link = q;
95 					q->as = AB;
96 					q->to.type = D_BRANCH;
97 					q->cond = q->link->link;
98 					bflag = 1;
99 				}
100 			}
101  */
102 			m = o->size;
103 			if(m == 0) {
104 				if(p->as == ATEXT) {
105 					curtext = p;
106 					autosize = p->to.offset + 4;
107 					if(p->from.sym != S)
108 						p->from.sym->value = c;
109 					continue;
110 				}
111 				diag("zero-width instruction\n%P\n", p);
112 				continue;
113 			}
114 			c += m;
115 		}
116 	}
117 
118 	if(debug['t']) {
119 		/*
120 		 * add strings to text segment
121 		 */
122 		c = rnd(c, 8);
123 		for(i=0; i<NHASH; i++)
124 		for(s = hash[i]; s != S; s = s->link) {
125 			if(s->type != SSTRING)
126 				continue;
127 			v = s->value;
128 			while(v & 3)
129 				v++;
130 			s->value = c;
131 			c += v;
132 		}
133 	}
134 
135 	c = rnd(c, 8);
136 
137 	setext = lookup("etext", 0);
138 	if(setext != S) {
139 		setext->value = c;
140 		textsize = c - INITTEXT;
141 	}
142 	if(INITRND)
143 		INITDAT = rnd(c, INITRND);
144 	if(debug['v'])
145 		Bprint(&bso, "tsize = %lux\n", textsize);
146 	Bflush(&bso);
147 }
148 
149 /*
150  * when the first reference to the literal pool threatens
151  * to go out of range of a 12-bit PC-relative offset,
152  * drop the pool now, and branch round it.
153  * this happens only in extended basic blocks that exceed 4k.
154  */
155 void
156 checkpool(Prog *p)
157 {
158 	if(pool.size >= 0xffc || immaddr((p->pc+4)+4+pool.size - pool.start+8) == 0)
159 		flushpool(p, 1);
160 	else if(p->link == P)
161 		flushpool(p, 2);
162 }
163 
164 void
165 flushpool(Prog *p, int skip)
166 {
167 	Prog *q;
168 
169 	if(blitrl) {
170 		if(skip){
171 			if(debug['v'] && skip == 1)
172 				print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start);
173 			q = prg();
174 			q->as = AB;
175 			q->to.type = D_BRANCH;
176 			q->cond = p->link;
177 			q->link = blitrl;
178 			blitrl = q;
179 		}
180 		else if(p->pc+pool.size-pool.start < 2048)
181 			return;
182 		elitrl->link = p->link;
183 		p->link = blitrl;
184 		blitrl = 0;	/* BUG: should refer back to values until out-of-range */
185 		elitrl = 0;
186 		pool.size = 0;
187 		pool.start = 0;
188 	}
189 }
190 
191 void
192 addpool(Prog *p, Adr *a)
193 {
194 	Prog *q, t;
195 	int c;
196 
197 	c = aclass(a);
198 
199 	t = zprg;
200 	t.as = AWORD;
201 
202 	switch(c) {
203 	default:
204 		t.to = *a;
205 		break;
206 
207 	case C_SROREG:
208 	case C_LOREG:
209 	case C_ROREG:
210 	case C_FOREG:
211 	case C_SOREG:
212 	case C_FAUTO:
213 	case C_SAUTO:
214 	case C_LAUTO:
215 	case C_LACON:
216 		t.to.type = D_CONST;
217 		t.to.offset = instoffset;
218 		break;
219 	}
220 
221 	for(q = blitrl; q != P; q = q->link)	/* could hash on t.t0.offset */
222 		if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) {
223 			p->cond = q;
224 			return;
225 		}
226 
227 	q = prg();
228 	*q = t;
229 	q->pc = pool.size;
230 
231 	if(blitrl == P) {
232 		blitrl = q;
233 		pool.start = p->pc;
234 	} else
235 		elitrl->link = q;
236 	elitrl = q;
237 	pool.size += 4;
238 
239 	p->cond = q;
240 }
241 
242 void
243 xdefine(char *p, int t, long v)
244 {
245 	Sym *s;
246 
247 	s = lookup(p, 0);
248 	if(s->type == 0 || s->type == SXREF) {
249 		s->type = t;
250 		s->value = v;
251 	}
252 }
253 
254 long
255 regoff(Adr *a)
256 {
257 
258 	instoffset = 0;
259 	aclass(a);
260 	return instoffset;
261 }
262 
263 long
264 immrot(ulong v)
265 {
266 	int i;
267 
268 	for(i=0; i<16; i++) {
269 		if((v & ~0xff) == 0)
270 			return (i<<8) | v | (1<<25);
271 		v = (v<<2) | (v>>30);
272 	}
273 	return 0;
274 }
275 
276 long
277 immaddr(long v)
278 {
279 	if(v >= 0 && v <= 0xfff)
280 		return (v & 0xfff) |
281 			(1<<24) |	/* pre indexing */
282 			(1<<23);	/* pre indexing, up */
283 	if(v >= -0xfff && v < 0)
284 		return (-v & 0xfff) |
285 			(1<<24);	/* pre indexing */
286 	return 0;
287 }
288 
289 int
290 immfloat(long v)
291 {
292 	return (v & 0xC03) == 0;	/* offset will fit in floating-point load/store */
293 }
294 
295 int
296 immhalf(long v)
297 {
298 	if(v >= 0 && v <= 0xff)
299 		return v|
300 			(1<<24)|	/* pre indexing */
301 			(1<<23);	/* pre indexing, up */
302 	if(v >= -0xff && v < 0)
303 		return (-v & 0xff)|
304 			(1<<24);	/* pre indexing */
305 	return 0;
306 }
307 
308 int
309 aclass(Adr *a)
310 {
311 	Sym *s;
312 	int t;
313 
314 	switch(a->type) {
315 	case D_NONE:
316 		return C_NONE;
317 
318 	case D_REG:
319 		return C_REG;
320 
321 	case D_REGREG:
322 		return C_REGREG;
323 
324 	case D_SHIFT:
325 		return C_SHIFT;
326 
327 	case D_FREG:
328 		return C_FREG;
329 
330 	case D_FPCR:
331 		return C_FCR;
332 
333 	case D_OREG:
334 		switch(a->name) {
335 		case D_EXTERN:
336 		case D_STATIC:
337 			if(a->sym == 0 || a->sym->name == 0) {
338 				print("null sym external\n");
339 				print("%D\n", a);
340 				return C_GOK;
341 			}
342 			s = a->sym;
343 			t = s->type;
344 			if(t == 0 || t == SXREF) {
345 				diag("undefined external: %s in %s\n",
346 					s->name, TNAME);
347 				s->type = SDATA;
348 			}
349 			if(reloc) {
350 				switch(t) {
351 				default:
352 					instoffset = s->value + a->offset + INITDAT;
353 					break;
354 				case STEXT:
355 					if(s->value == -1)
356 						undefsym(s);
357 				case SCONST:
358 				case SLEAF:
359 				case SSTRING:
360 					instoffset = s->value + a->offset;
361 					break;
362 				}
363 				return C_ADDR;
364 			}
365 			instoffset = s->value + a->offset - BIG;
366 			t = immaddr(instoffset);
367 			if(t) {
368 				if(immhalf(instoffset))
369 					return immfloat(t) ? C_HFEXT : C_HEXT;
370 				if(immfloat(t))
371 					return C_FEXT;
372 				return C_SEXT;
373 			}
374 			return C_LEXT;
375 		case D_AUTO:
376 			instoffset = autosize + a->offset;
377 			t = immaddr(instoffset);
378 			if(t){
379 				if(immhalf(instoffset))
380 					return immfloat(t) ? C_HFAUTO : C_HAUTO;
381 				if(immfloat(t))
382 					return C_FAUTO;
383 				return C_SAUTO;
384 			}
385 			return C_LAUTO;
386 
387 		case D_PARAM:
388 			instoffset = autosize + a->offset + 4L;
389 			t = immaddr(instoffset);
390 			if(t){
391 				if(immhalf(instoffset))
392 					return immfloat(t) ? C_HFAUTO : C_HAUTO;
393 				if(immfloat(t))
394 					return C_FAUTO;
395 				return C_SAUTO;
396 			}
397 			return C_LAUTO;
398 		case D_NONE:
399 			instoffset = a->offset;
400 			t = immaddr(instoffset);
401 			if(t) {
402 				if(immhalf(instoffset))		 /* n.b. that it will also satisfy immrot */
403 					return immfloat(t) ? C_HFOREG : C_HOREG;
404 				if(immfloat(t))
405 					return C_FOREG; /* n.b. that it will also satisfy immrot */
406 				t = immrot(instoffset);
407 				if(t)
408 					return C_SROREG;
409 				if(immhalf(instoffset))
410 					return C_HOREG;
411 				return C_SOREG;
412 			}
413 			t = immrot(instoffset);
414 			if(t)
415 				return C_ROREG;
416 			return C_LOREG;
417 		}
418 		return C_GOK;
419 
420 	case D_PSR:
421 		return C_PSR;
422 
423 	case D_OCONST:
424 		switch(a->name) {
425 		case D_EXTERN:
426 		case D_STATIC:
427 			s = a->sym;
428 			t = s->type;
429 			if(t == 0 || t == SXREF) {
430 				diag("undefined external: %s in %s\n",
431 					s->name, TNAME);
432 				s->type = SDATA;
433 			}
434 			instoffset = s->value + a->offset + INITDAT;
435 			if(s->type == STEXT || s->type == SLEAF) {
436 				if(s->value == -1)
437 					undefsym(s);
438 				instoffset = s->value + a->offset;
439 			}
440 			return C_LCON;
441 		}
442 		return C_GOK;
443 
444 	case D_FCONST:
445 		return C_FCON;
446 
447 	case D_CONST:
448 		switch(a->name) {
449 
450 		case D_NONE:
451 			instoffset = a->offset;
452 			if(a->reg != NREG)
453 				goto aconsize;
454 
455 			t = immrot(instoffset);
456 			if(t)
457 				return C_RCON;
458 			t = immrot(~instoffset);
459 			if(t)
460 				return C_NCON;
461 			return C_LCON;
462 
463 		case D_EXTERN:
464 		case D_STATIC:
465 			s = a->sym;
466 			if(s == S)
467 				break;
468 			t = s->type;
469 			switch(t) {
470 			case 0:
471 			case SXREF:
472 				diag("undefined external: %s in %s\n",
473 					s->name, TNAME);
474 				s->type = SDATA;
475 				break;
476 			case STEXT:
477 				if(s->value == -1)
478 					undefsym(s);
479 			case SSTRING:
480 			case SCONST:
481 			case SLEAF:
482 				instoffset = s->value + a->offset;
483 				return C_LCON;
484 			}
485 			if(!reloc) {
486 				instoffset = s->value + a->offset - BIG;
487 				t = immrot(instoffset);
488 				if(t && instoffset != 0)
489 					return C_RECON;
490 			}
491 			instoffset = s->value + a->offset + INITDAT;
492 			return C_LCON;
493 
494 		case D_AUTO:
495 			instoffset = autosize + a->offset;
496 			goto aconsize;
497 
498 		case D_PARAM:
499 			instoffset = autosize + a->offset + 4L;
500 		aconsize:
501 			t = immrot(instoffset);
502 			if(t)
503 				return C_RACON;
504 			return C_LACON;
505 		}
506 		return C_GOK;
507 
508 	case D_BRANCH:
509 		return C_SBRA;
510 	}
511 	return C_GOK;
512 }
513 
514 Optab*
515 oplook(Prog *p)
516 {
517 	int a1, a2, a3, r;
518 	char *c1, *c3;
519 	Optab *o, *e;
520 
521 	a1 = p->optab;
522 	if(a1)
523 		return optab+(a1-1);
524 	a1 = p->from.class;
525 	if(a1 == 0) {
526 		a1 = aclass(&p->from) + 1;
527 		p->from.class = a1;
528 	}
529 	a1--;
530 	a3 = p->to.class;
531 	if(a3 == 0) {
532 		a3 = aclass(&p->to) + 1;
533 		p->to.class = a3;
534 	}
535 	a3--;
536 	a2 = C_NONE;
537 	if(p->reg != NREG)
538 		a2 = C_REG;
539 	r = p->as;
540 	o = oprange[r].start;
541 	if(o == 0) {
542 		a1 = opcross[repop[r]][a1][a2][a3];
543 		if(a1) {
544 			p->optab = a1+1;
545 			return optab+a1;
546 		}
547 		o = oprange[r].stop; /* just generate an error */
548 	}
549 	if(0) {
550 		print("oplook %A %d %d %d\n",
551 			(int)p->as, a1, a2, a3);
552 		print("		%d %d\n", p->from.type, p->to.type);
553 	}
554 	e = oprange[r].stop;
555 	c1 = xcmp[a1];
556 	c3 = xcmp[a3];
557 	for(; o<e; o++)
558 		if(o->a2 == a2)
559 		if(c1[o->a1])
560 		if(c3[o->a3]) {
561 			p->optab = (o-optab)+1;
562 			return o;
563 		}
564 	diag("illegal combination %A %d %d %d\n",
565 		p->as, a1, a2, a3);
566 	prasm(p);
567 	if(o == 0)
568 		o = optab;
569 	return o;
570 }
571 
572 int
573 cmp(int a, int b)
574 {
575 
576 	if(a == b)
577 		return 1;
578 	switch(a) {
579 	case C_LCON:
580 		if(b == C_RCON || b == C_NCON)
581 			return 1;
582 		break;
583 	case C_LACON:
584 		if(b == C_RACON)
585 			return 1;
586 		break;
587 	case C_LECON:
588 		if(b == C_RECON)
589 			return 1;
590 		break;
591 
592 	case C_HFEXT:
593 		return b == C_HEXT || b == C_FEXT;
594 	case C_FEXT:
595 	case C_HEXT:
596 		return b == C_HFEXT;
597 	case C_SEXT:
598 		return cmp(C_HFEXT, b);
599 	case C_LEXT:
600 		return cmp(C_SEXT, b);
601 
602 	case C_HFAUTO:
603 		return b == C_HAUTO || b == C_FAUTO;
604 	case C_FAUTO:
605 	case C_HAUTO:
606 		return b == C_HFAUTO;
607 	case C_SAUTO:
608 		return cmp(C_HFAUTO, b);
609 	case C_LAUTO:
610 		return cmp(C_SAUTO, b);
611 
612 	case C_HFOREG:
613 		return b == C_HOREG || b == C_FOREG;
614 	case C_FOREG:
615 	case C_HOREG:
616 		return b == C_HFOREG;
617 	case C_SROREG:
618 		return cmp(C_SOREG, b) || cmp(C_ROREG, b);
619 	case C_SOREG:
620 	case C_ROREG:
621 		return b == C_SROREG || cmp(C_HFOREG, b);
622 	case C_LOREG:
623 		return cmp(C_SROREG, b);
624 
625 	case C_LBRA:
626 		if(b == C_SBRA)
627 			return 1;
628 		break;
629 	}
630 	return 0;
631 }
632 
633 int
634 ocmp(const void *a1, const void *a2)
635 {
636 	Optab *p1, *p2;
637 	int n;
638 
639 	p1 = (Optab*)a1;
640 	p2 = (Optab*)a2;
641 	n = p1->as - p2->as;
642 	if(n)
643 		return n;
644 	n = (p2->flag&V4) - (p1->flag&V4);	/* architecture version */
645 	if(n)
646 		return n;
647 	n = p1->a1 - p2->a1;
648 	if(n)
649 		return n;
650 	n = p1->a2 - p2->a2;
651 	if(n)
652 		return n;
653 	n = p1->a3 - p2->a3;
654 	if(n)
655 		return n;
656 	return 0;
657 }
658 
659 void
660 buildop(void)
661 {
662 	int i, n, r;
663 
664 	armv4 = !debug['h'];
665 	for(i=0; i<C_GOK; i++)
666 		for(n=0; n<C_GOK; n++)
667 			xcmp[i][n] = cmp(n, i);
668 	for(n=0; optab[n].as != AXXX; n++)
669 		if((optab[n].flag & V4) && !armv4) {
670 			optab[n].as = AXXX;
671 			break;
672 		}
673 	qsort(optab, n, sizeof(optab[0]), ocmp);
674 	for(i=0; i<n; i++) {
675 		r = optab[i].as;
676 		oprange[r].start = optab+i;
677 		while(optab[i].as == r)
678 			i++;
679 		oprange[r].stop = optab+i;
680 		i--;
681 
682 		switch(r)
683 		{
684 		default:
685 			diag("unknown op in build: %A\n", r);
686 			errorexit();
687 		case AADD:
688 			oprange[AAND] = oprange[r];
689 			oprange[AEOR] = oprange[r];
690 			oprange[ASUB] = oprange[r];
691 			oprange[ARSB] = oprange[r];
692 			oprange[AADC] = oprange[r];
693 			oprange[ASBC] = oprange[r];
694 			oprange[ARSC] = oprange[r];
695 			oprange[AORR] = oprange[r];
696 			oprange[ABIC] = oprange[r];
697 			break;
698 		case ACMP:
699 			oprange[ATST] = oprange[r];
700 			oprange[ATEQ] = oprange[r];
701 			oprange[ACMN] = oprange[r];
702 			break;
703 		case AMVN:
704 			break;
705 		case ABEQ:
706 			oprange[ABNE] = oprange[r];
707 			oprange[ABCS] = oprange[r];
708 			oprange[ABHS] = oprange[r];
709 			oprange[ABCC] = oprange[r];
710 			oprange[ABLO] = oprange[r];
711 			oprange[ABMI] = oprange[r];
712 			oprange[ABPL] = oprange[r];
713 			oprange[ABVS] = oprange[r];
714 			oprange[ABVC] = oprange[r];
715 			oprange[ABHI] = oprange[r];
716 			oprange[ABLS] = oprange[r];
717 			oprange[ABGE] = oprange[r];
718 			oprange[ABLT] = oprange[r];
719 			oprange[ABGT] = oprange[r];
720 			oprange[ABLE] = oprange[r];
721 			break;
722 		case ASLL:
723 			oprange[ASRL] = oprange[r];
724 			oprange[ASRA] = oprange[r];
725 			break;
726 		case AMUL:
727 			oprange[AMULU] = oprange[r];
728 			break;
729 		case ADIV:
730 			oprange[AMOD] = oprange[r];
731 			oprange[AMODU] = oprange[r];
732 			oprange[ADIVU] = oprange[r];
733 			break;
734 		case AMOVW:
735 		case AMOVB:
736 		case AMOVBU:
737 		case AMOVH:
738 		case AMOVHU:
739 			break;
740 		case ASWPW:
741 			oprange[ASWPBU] = oprange[r];
742 			break;
743 		case AB:
744 		case ABL:
745 		case ASWI:
746 		case AWORD:
747 		case AMOVM:
748 		case ARFE:
749 		case ATEXT:
750 		case ACASE:
751 		case ABCASE:
752 			break;
753 		case AADDF:
754 			oprange[AADDD] = oprange[r];
755 			oprange[ASUBF] = oprange[r];
756 			oprange[ASUBD] = oprange[r];
757 			oprange[AMULF] = oprange[r];
758 			oprange[AMULD] = oprange[r];
759 			oprange[ADIVF] = oprange[r];
760 			oprange[ADIVD] = oprange[r];
761 			oprange[AMOVFD] = oprange[r];
762 			oprange[AMOVDF] = oprange[r];
763 			break;
764 
765 		case ACMPF:
766 			oprange[ACMPD] = oprange[r];
767 			break;
768 
769 		case AMOVF:
770 			oprange[AMOVD] = oprange[r];
771 			break;
772 
773 		case AMOVFW:
774 			oprange[AMOVWF] = oprange[r];
775 			oprange[AMOVWD] = oprange[r];
776 			oprange[AMOVDW] = oprange[r];
777 			break;
778 
779 		case AMULL:
780 			oprange[AMULA] = oprange[r];
781 			oprange[AMULAL] = oprange[r];
782 			oprange[AMULLU] = oprange[r];
783 			oprange[AMULALU] = oprange[r];
784 			break;
785 		}
786 	}
787 }
788 
789 /*
790 void
791 buildrep(int x, int as)
792 {
793 	Opcross *p;
794 	Optab *e, *s, *o;
795 	int a1, a2, a3, n;
796 
797 	if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
798 		diag("assumptions fail in buildrep");
799 		errorexit();
800 	}
801 	repop[as] = x;
802 	p = (opcross + x);
803 	s = oprange[as].start;
804 	e = oprange[as].stop;
805 	for(o=e-1; o>=s; o--) {
806 		n = o-optab;
807 		for(a2=0; a2<2; a2++) {
808 			if(a2) {
809 				if(o->a2 == C_NONE)
810 					continue;
811 			} else
812 				if(o->a2 != C_NONE)
813 					continue;
814 			for(a1=0; a1<32; a1++) {
815 				if(!xcmp[a1][o->a1])
816 					continue;
817 				for(a3=0; a3<32; a3++)
818 					if(xcmp[a3][o->a3])
819 						(*p)[a1][a2][a3] = n;
820 			}
821 		}
822 	}
823 	oprange[as].start = 0;
824 }
825 */
826