|
|
1.1 root 1:
2: #ifndef lint
3: static char sccsid[] = "@(#)stackops.c 1.1 86/02/03 Copyr 1985 Sun Micro";
4: #endif
5:
6: /*
7: * Copyright (c) 1985 by Sun Microsystems, Inc.
8: */
9:
10: #include "as.h"
11: #include "c2.h"
12:
13: extern struct oper *newoperand();
14:
15: #define previ( p ) { do p=p->back; while( ISDIRECTIVE(p->op) ); }
16:
17: #define ispushop(o) ((o)->type_o == T_PREDEC && (o)->value_o == SPREG)
18: #define ispopop(o) ((o)->type_o == T_POSTINC && (o)->value_o == SPREG)
19:
20: extern int bytesize[];
21: #define BYTESIZE( s ) (bytesize[ (int)(s)])
22:
23: /*
24: * replace
25: * (b)->move X,sp@-
26: * ...
27: * (n)->move sp@+,Rn
28: * with
29: * (b)->move X,Rn
30: * ...
31: * (n) deleted
32: * Note that if Rn is live after (n), it now becomes live
33: * at all of the points in between (b) and (n).
34: */
35:
36: static NODE *
37: movedef(b,n,ip)
38: register NODE *b;
39: NODE *n;
40: struct ins_bkt *ip;
41: {
42: register NODE *p;
43: regmask l;
44:
45: if (b->nref == 2)
46: freeoperand(b->ref[1]);
47: else
48: b->nref = 2;
49: b->ref[1] = newoperand(n->ref[1]);
50: installinstruct(b,ip);
51: n = deletenode(n);
52: l = n->forw->rlive;
53: for (p = n; p != b; p = p->back) {
54: l = p->rlive = compute_normal(p, l);
55: }
56: return n;
57: }
58:
59: /*
60: * replace
61: * (b)->pea X
62: * ...
63: * (n)->move sp@+,dn
64: * with
65: * (b)->lea X,ar
66: * movl ar,dn
67: * ...
68: * (n) (deleted)
69: *
70: * This is like the previous transformation with an
71: * adjustment for the fact that 'lea' does not work
72: * with a d-register destination.
73: */
74: static NODE*
75: movedef2(b, n, ar, ip)
76: register NODE *b;
77: register NODE *n;
78: int ar;
79: struct ins_bkt *ip;
80: {
81: register NODE *p;
82: register struct oper *o;
83: regmask l;
84:
85: /*
86: * rewrite (b) as "lea X,ar"
87: */
88: b->nref = 2;
89: o = newoperand(n->ref[1]);
90: o->value_o = ar;
91: b->ref[1] = o;
92: installinstruct(b,ip);
93: /*
94: * insert "movl ar,dn" following (b)
95: */
96: p = new();
97: p->nref = 2;
98: o = newoperand(o);
99: o->value_o = ar;
100: p->ref[0] = o;
101: o = newoperand(o);
102: o->value_o = n->ref[1]->value_o;
103: p->ref[1] = o;
104: cannibalize(p, "movl");
105: insert(p,b);
106: /*
107: * delete (n), update register lifetime information
108: */
109: n = deletenode(n);
110: l = n->forw->rlive;
111: for (p = n; p != b; p = p->back) {
112: l = p->rlive = compute_normal(p, l);
113: }
114: return n;
115: }
116:
117:
118: /*
119: * replace
120: * (b)->move X,sp@-
121: * ...
122: * (n)-><op> sp@+,...
123: * with
124: * (b) (deleted)
125: * ...
126: * (n)-><op> X,...
127: * Note that if X is a register, it now becomes live
128: * at all of the points in between (b) and (n).
129: */
130: static NODE *
131: moveuse(b, n, srce, ip)
132: NODE *b,*n;
133: struct oper *srce;
134: struct ins_bkt *ip;
135: {
136: register NODE *p;
137: regmask l;
138:
139: freeoperand(n->ref[0]);
140: n->ref[0] = srce;
141: b = deletenode(b);
142: installinstruct(n,ip);
143: l = n->forw->rlive;
144: for (p = n; p != b; p = p->back) {
145: l = p->rlive = compute_normal(p, l);
146: }
147: return(n);
148: }
149:
150:
151: /*
152: * returns the "extended mode" instruction, if any,
153: * corresponding to the the instruction described by (ip).
154: */
155: struct ins_bkt *
156: extendedop(ip)
157: struct ins_bkt *ip;
158: {
159: char buf[64];
160:
161: (void)strcpy(buf, ip->text_i);
162: buf[strlen(buf)-1] = 'x';
163: return(sopcode(buf));
164: }
165:
166: /*
167: * returns 1 if operands (op1) and (op2) may overlap
168: */
169: int
170: mayoverlap(op1, op2, subop1, subop2)
171: register struct oper *op1;
172: register struct oper *op2;
173: subop_t subop1;
174: subop_t subop2;
175: {
176: int r1, r2;
177: struct sym_bkt *sym1, *sym2;
178: int lb1, lb2, ub1, ub2; /* byte offset bounds of op1 and op2 */
179: int glb, lub; /* greatest lower & least upper bounds */
180:
181: r1 = -1;
182: r2 = -1;
183: lb1 = 0;
184: lb2 = 0;
185: sym1 = NULL;
186: sym2 = NULL;
187: switch(op1->type_o) {
188: case T_IMMED:
189: return(0);
190: case T_REG:
191: return(op2->type_o == T_REG && op1->value_o == op2->value_o);
192: case T_POSTINC:
193: case T_PREDEC:
194: /* assume sp@+ and sp@- only overlap with each other */
195: if (op1->value_o == SPREG) {
196: if (op2->type_o == T_POSTINC || op2->type_o == T_PREDEC)
197: return(op2->value_o == SPREG);
198: return(0);
199: }
200: return(1);
201: case T_DISPL:
202: r1 = op1->reg_o;
203: /* fall through */
204: case T_ABSS:
205: case T_ABSL:
206: case T_NORMAL:
207: sym1 = op1->sym_o;
208: lb1 = op1->value_o;
209: ub1 = lb1 + BYTESIZE(subop1)-1;
210: break;
211: case T_DEFER:
212: r1 = op1->value_o;
213: ub1 = lb1 + BYTESIZE(subop1)-1;
214: break;
215: }
216: switch(op2->type_o) {
217: case T_IMMED:
218: case T_REG:
219: return(0);
220: case T_POSTINC:
221: case T_PREDEC:
222: return(op2->value_o != SPREG);
223: case T_DISPL:
224: r2 = op2->reg_o;
225: /* fall through */
226: case T_ABSS:
227: case T_ABSL:
228: case T_NORMAL:
229: sym2 = op2->sym_o;
230: lb2 = op2->value_o;
231: ub2 = lb2 + BYTESIZE(subop2)-1;
232: goto compute_overlap;
233: case T_DEFER:
234: r2 = op2->value_o;
235: ub2 = lb2 + BYTESIZE(subop2)-1;
236: /* fall through */
237: compute_overlap:
238: if (r1 != r2 || sym1 != sym2)
239: return(1);
240: ub1 = lb1 + BYTESIZE(subop1)-1;
241: ub2 = lb2 + BYTESIZE(subop2)-1;
242: glb = lb1 > lb2 ? lb1 : lb2;
243: lub = ub1 < ub2 ? ub1 : ub2;
244: return(glb <= lub);
245: default:
246: return(1);
247: }
248: }
249:
250: /*
251: * returns 1 if two operands are in adjacent *addressable* words
252: */
253: int
254: adjacent(op1, op2)
255: register struct oper *op1,*op2;
256: {
257: int w;
258: struct oper temp;
259:
260: if (op1->type_o != op2->type_o) {
261: /* make DEFER operands look like DISPL operands */
262: if (op1->type_o == T_DEFER) {
263: temp.type_o = T_DISPL;
264: temp.reg_o = op1->value_o;
265: temp.value_o = 0;
266: op1 = &temp;
267: } else if (op2->type_o == T_DEFER) {
268: temp.type_o = T_DISPL;
269: temp.reg_o = op2->value_o;
270: temp.value_o = 0;
271: op2 = &temp;
272: } else {
273: return 0;
274: }
275: }
276: switch (op1->type_o){
277: case T_IMMED:
278: case T_NORMAL:
279: case T_ABSS:
280: case T_ABSL:
281: if (op1->sym_o != op2->sym_o) return 0; /* oops -- complex*/
282: /* fall through */
283: case T_REG:
284: case T_DEFER:
285: case T_POSTINC:
286: case T_PREDEC:
287: if (op1->value_o + sizeof(long) != op2->value_o) return 0;
288: break;
289: case T_INDEX:
290: if (op1->disp_o + sizeof(long) != op2->disp_o) return 0;
291: /* fall through */
292: case T_DISPL:
293: if (op1->reg_o != op2->reg_o) return 0;
294: if (op1->value_o + sizeof(long) != op2->value_o) return 0;
295: break;
296: default:
297: return 0;
298: }
299: return 1;
300: }
301:
302: /*
303: * returns 1 if node (n) may use/modify operand (o)
304: */
305: int
306: may_touch(n, o, osubop, touchmask)
307: register NODE *n;
308: register struct oper *o;
309: int osubop; /* subop of instruction using (o) */
310: register touchmask;
311: {
312: register int i,v;
313: regmask l;
314:
315: switch(o->type_o) {
316: case T_IMMED:
317: return 0;
318: case T_REG:
319: if (touchmask&RMASK == touchmask)
320: return inmask(o->value_o,n->ruse);
321: if (touchmask&WMASK == touchmask)
322: return inmask(o->value_o,n->rset);
323: if (touchmask)
324: return inmask(o->value_o,addmask(n->rset,n->ruse));
325: return 0;
326: default:
327: /*
328: * for each operand modified by (n), see if
329: * a potential overlap with (o) exists.
330: */
331: if (n->op == OP_CALL)
332: return 1;
333: v = make_touchop(n, n->instr->touchop_i);
334: for (i = 0; i < n->nref; i++) {
335: if (v&touchmask) {
336: if (mayoverlap(o, n->ref[i], osubop, n->subop))
337: return 1;
338: }
339: v >>= TOUCHWIDTH;
340: }
341: }
342: return 0;
343: }
344:
345:
346: /*
347: * reduce stack traffic exposed by inline expansion
348: * of procedures
349: */
350: int
351: stackops()
352: {
353: register NODE *n;
354: register NODE *p,*b;
355: register struct ins_bkt *ip;
356: register struct oper *o;
357: register int regno;
358: extern NODE *deletenode();
359: int changes;
360: regmask l;
361: int offset;
362: int r;
363:
364: changes = 0;
365: for (n=first.forw; n != &first; n=n->forw) {
366:
367: /*
368: * Pattern #0:
369: * (0) sub #X,sp
370: * (1) add #Y,sp -or- (1) lea sp@(Y),sp
371: * Rewrite:
372: * delete both, or use a single addql, subql, or lea
373: */
374: if (n->op == OP_SUB
375: && n->ref[1]->type_o == T_REG && n->ref[1]->value_o == SPREG
376: && n->ref[0]->type_o == T_IMMED) {
377: p = n->forw;
378: switch(p->op) {
379: case OP_ADD:
380: o = p->ref[1];
381: if (o->type_o == T_REG && o->value_o == SPREG
382: && p->ref[0]->type_o == T_IMMED) {
383: goto delete_adjust;
384: }
385: break;
386: case OP_LEA:
387: o = p->ref[1];
388: if (o->type_o == T_REG && o->value_o == SPREG
389: && p->ref[0]->type_o == T_DISPL
390: && p->ref[0]->reg_o == SPREG ) {
391: goto delete_adjust;
392: }
393: break;
394: delete_adjust:
395: offset = n->ref[0]->value_o - p->ref[0]->value_o;
396: p = deletenode(p);
397: if (offset == 0) {
398: n = deletenode(n);
399: } else if (offset >= 1 && offset <= 8) {
400: n->ref[0]->value_o = offset;
401: installinstruct(n, sopcode("addql"));
402: } else if (offset <= -8 && offset >= -1) {
403: n->ref[0]->value_o = -offset;
404: installinstruct(n, sopcode("subql"));
405: } else {
406: o = n->ref[0];
407: o->type_o = T_DISPL;
408: o->reg_o = SPREG;
409: o->value_o = offset;
410: installinstruct(n, sopcode("lea"));
411: }
412: continue;
413: }
414: }
415:
416: /*
417: * Pattern #1:
418: * (b) movl X,sp@-
419: * ...
420: * (n) <op> sp@+,...
421: *
422: * if possible, rewrite as
423: * (b) (deleted)
424: * ...
425: * (n) <op> X,...
426: *
427: * Requirements:
428: * X must not be a control register or i/o device.
429: * X must not be modified in the interim.
430: * Side effects of X must not be used in the interim.
431: */
432: if (ISINSTRUC(n->op) && n->ref[0] != NULL && ispopop(n->ref[0])) {
433: b = n;
434: o = NULL;
435: do {
436: previ(b);
437: if (b->op == OP_FIRST || b->op == OP_LABEL
438: || ISBRANCH(b->op)) {
439: /* flow of control to reach this point is unknown */
440: break;
441: }
442: if (b->op == OP_PEA) {
443: switch(b->ref[0]->type_o) {
444: case T_ABSS:
445: case T_ABSL:
446: case T_NORMAL:
447: /*
448: * srce is #X
449: */
450: o = newoperand(b->ref[0]);
451: o->type_o = T_IMMED;
452: b->subop = SUBOP_L;
453: }
454: break;
455: }
456: if (b->op == OP_CLR && ispushop(b->ref[0])) {
457: /*
458: * srce is #0
459: */
460: o = newoperand(b->ref[0]);
461: o->type_o = T_IMMED;
462: o->value_o = 0;
463: break;
464: }
465: if (b->op == OP_MOVE && ispushop(b->ref[1])) {
466: o = newoperand(b->ref[0]);
467: break;
468: }
469: if (inmask(SPREG,b->rset) || inmask(SPREG,b->ruse)) {
470: /* unknown use/modification of sp */
471: break;
472: }
473: } while(1);
474: /*
475: * If the preceding search yielded a usable operand,
476: * determine whether its use can be moved from node
477: * (b) to (n).
478: */
479: if (o != NULL) {
480: l = submask(b->rset, MAKEWMASK(SPREG, LW));
481: if ( o->type_o == T_REG && !datareg(o->value_o)
482: || o->type_o != T_IMMED && o->type_o != T_REG && !cancache(o)
483: || !emptymask(andmask(l, b->forw->rlive))) {
484: /*
485: * x is a control register or might be an i/o device,
486: * or has non-trivial side effects: cannot move.
487: */
488: freeoperand(o);
489: o = NULL;
490: } else {
491: /* search for defs of X and defs affecting access to X */
492: l = submask(b->ruse, MAKERMASK(SPREG, LR));
493: p = n;
494: previ(p);
495: while (p != b) {
496: if (may_touch(p, o, b->subop, WMASK)) {
497: freeoperand(o); /* bail out */
498: o = NULL;
499: break;
500: }
501: if (!emptymask(andmask(l, p->rset))) {
502: /*
503: * (p) defines register used at (b); if (p)
504: * is a simple register load, and if the lifetime
505: * of the register is restricted to (p)..(n),
506: * we may be able to use a different register,
507: * eliminating the conflict
508: */
509: int r1,r2;
510: if (p->op != OP_MOVE
511: || p->ref[1]->type_o != T_REG ) {
512: freeoperand(o); /* bail out */
513: o = NULL;
514: break;
515: }
516: r1 = p->ref[1]->value_o;
517: r2 = find_freereg(p, n, reg_access[r1]);
518: if (r2 == -1 || !can_reassign_reg(p, n, r1, r2)) {
519: freeoperand(o); /* bail out */
520: o = NULL;
521: break;
522: }
523: reassign_reg(p, n, r1, r2);
524: }
525: previ(p);
526: }
527: }
528: }
529: /*
530: * If it is feasible to move the use of the operand X,
531: * find out whether X is a suitable source operand of
532: * the instruction at (n).
533: */
534: if (o != NULL) {
535: if (BYTESIZE(b->subop) == BYTESIZE(n->subop)
536: && operand_ok(n->instr, o, n->ref[1], n->ref[2])) {
537: n = moveuse(b, n, o, n->instr);
538: changes++;
539: continue;
540: }
541: /*
542: * if X is a floating point register, try rewriting
543: * node (n) using an extended floating point opcode
544: */
545: if (o->type_o == T_REG && freg(o->value_o)) {
546: ip = extendedop(n->instr);
547: if (ip != NULL
548: && operand_ok(ip, o, n->ref[1], n->ref[2])) {
549: n = moveuse(b, n, o, ip);
550: changes++;
551: continue;
552: }
553: }
554: /*
555: * if X is an addressable double operand and the
556: * the use of sp@+ occurs in a double precision
557: * floating point operation, use X directly.
558: */
559: if (n->subop == SUBOP_D && o->type_o != T_REG
560: && b->back->op == OP_MOVE
561: && b->back->subop == SUBOP_L
562: && ispushop(b->back->ref[1])
563: && adjacent(o, b->back->ref[0])) {
564: (void) deletenode(b->back);
565: n = moveuse(b, n, o, n->instr);
566: changes++;
567: continue;
568: }
569: freeoperand(o); /* give up */
570: } /* if o != NULL */
571: } /* if ISINSTRUCT... */
572:
573: /*
574: * Other patterns assume the instruction at (n) is a move.
575: */
576: if (n->op != OP_MOVE)
577: continue;
578:
579: /*
580: * Pattern #2:
581: * (b) move X,sp@-
582: * ....
583: * (n) move sp@+,rn
584: * If possible, rewrite as:
585: * (b) move X,rn
586: * ....
587: * (n) (deleted)
588: * Requirements:
589: * Rn must neither be used nor set in the interim.
590: * If rn is an a-register, condition codes must not
591: * be live immediately following (b).
592: * If rn is not an a-register, condition codes must not
593: * be live immediately following (n).
594: */
595: o = n->ref[0];
596: regno = n->ref[1]->value_o;
597: if(ispopop(o) && n->ref[1]->type_o == T_REG
598: && !(dreg(regno) && inmask(CCREG,n->forw->rlive))
599: && !(freg(regno) && inmask(FPCCREG, n->forw->rlive))) {
600: b = n;
601: do {
602: previ(b);
603: if (b->op == OP_FIRST || b->op == OP_LABEL
604: || ISBRANCH(b->op)) {
605: /* flow of control to reach this point is unknown */
606: break;
607: }
608: if (b->op == OP_PEA) {
609: /* note: PEA does not set the condition codes */
610: if (areg(regno)) {
611: /*
612: * rewrite as: lea <srce>,areg
613: */
614: n = movedef(b,n,sopcode("lea"));
615: changes++;
616: } else if ((r = dead_areg(b)) != -1
617: && !inmask(CCREG,b->forw->rlive)) {
618: /*
619: * rewrite as: lea <srce>,areg;
620: * movl areg,rn
621: */
622: n = movedef2(b,n,r,sopcode("lea"));
623: changes++;
624: } else {
625: switch(b->ref[0]->type_o) {
626: case T_ABSS:
627: case T_ABSL:
628: case T_NORMAL:
629: /*
630: * rewrite as: movl #<srce>,reg
631: */
632: b->ref[0]->type_o = T_IMMED;
633: n = movedef(b,n,sopcode("movl"));
634: changes++;
635: }
636: }
637: break;
638: }
639: if (b->op == OP_CLR && ispushop(b->ref[0])
640: && b->subop == SUBOP_L
641: && !(areg(regno) && inmask(CCREG,b->forw->rlive))) {
642: /*
643: * rewrite as: movl #0,reg
644: */
645: b->ref[0]->type_o = T_IMMED;
646: b->ref[0]->value_o = 0;
647: n = movedef(b,n,sopcode("movl"));
648: changes++;
649: break;
650: }
651: if (b->op == OP_MOVE && ispushop(b->ref[1])
652: && b->subop == SUBOP_L
653: && !(areg(regno) && inmask(CCREG,b->forw->rlive))) {
654: /*
655: * try to change into MOVE <srce>,reg
656: * beware floating point register operands
657: */
658: if (!freg(regno)) {
659: /* reg is a-reg or d-reg */
660: if (b->subop == SUBOP_D || b->subop == SUBOP_X) {
661: /* can't move doubles to a register pair */
662: } else if (areg(regno)
663: && b->ref[0]->type_o == T_REG
664: && freg(b->ref[0]->value_o)) {
665: /* can't move fp reg to a-reg */
666: } else if (b->subop == n->subop) {
667: /* easy case */
668: n = movedef(b,n,n->instr);
669: changes++;
670: }
671: } else {
672: o = b->ref[0];
673: if (o->type_o == T_REG && freg(o->value_o)) {
674: /*
675: * freg-freg move
676: */
677: n = movedef(b,n,sopcode("fmovex"));
678: } else if (b->subop == n->subop
679: && BYTESIZE(n->subop) <= sizeof(long)
680: && !(o->type_o == T_REG && areg(o->value_o))) {
681: /*
682: * move single word to freg
683: */
684: n = movedef(b,n,n->instr);
685: changes++;
686: } else if (n->subop == SUBOP_D
687: && o->type_o != T_REG
688: && b->back->op == OP_MOVE
689: && b->back->subop == SUBOP_L
690: && ispushop(b->back->ref[1])
691: && adjacent(o, b->back->ref[0])) {
692: /*
693: * move addressable double operand to freg
694: */
695: (void) deletenode(b->back);
696: b->subop = SUBOP_D;
697: n = movedef(b,n,n->instr);
698: changes++;
699: }
700: /* else forget it! */
701: }
702: break;
703: }
704: if (inmask(regno,b->rset) || inmask(regno,b->ruse)) {
705: /* dest reg was used or modified along the way */
706: break;
707: }
708: if (inmask(SPREG,b->rset) || inmask(SPREG,b->ruse)) {
709: /* some other use/modification of sp */
710: break;
711: }
712: } while(1);
713: }
714:
715: /*
716: * Pattern #3:
717: * (b) move sp@+,rn
718: * ....
719: * (n) move rn,sp@-
720: *
721: * if possible, rewrite as
722: * (b) move sp@,rn
723: * ....
724: * (n) (deleted)
725: *
726: * requirements:
727: * Rn must not be used or set in the interim.
728: * Condition codes must not be live after either (b) or (n).
729: */
730: o = n->ref[1];
731: if (ispushop(o) && n->ref[0]->type_o == T_REG
732: && datareg(n->ref[0]->value_o)
733: && !inmask(CCREG,n->forw->rlive)
734: && !inmask(FPCCREG, n->forw->rlive)) {
735: regno = n->ref[0]->value_o;
736: b = n;
737: do {
738: previ(b);
739: if (b->op == OP_FIRST || b->op == OP_LABEL
740: || ISBRANCH(b->op)) {
741: /* flow of control to reach this point is unknown */
742: break;
743: }
744: if (b->op == OP_MOVE && ispopop(b->ref[0])
745: && b->subop == n->subop
746: && sameops(b->ref[1], n->ref[0])
747: && !inmask(CCREG, b->forw->rlive)
748: && !inmask(FPCCREG, b->forw->rlive)) {
749: /* pushing same value that was just popped */
750: n = deletenode(n);
751: b->ref[0]->type_o = T_DEFER;
752: installinstruct(b, b->instr);
753: l = n->forw->rlive;
754: for (p = n; p != b; p = p->back) {
755: l = p->rlive = compute_normal(p, l);
756: }
757: changes++;
758: break;
759: }
760: if (inmask(regno,b->rset) || inmask(regno,b->ruse)) {
761: /* reg was used/modified along the way */
762: break;
763: }
764: if (inmask(SPREG,b->rset) || inmask(SPREG,b->ruse)) {
765: /* some other use/modification of sp */
766: break;
767: }
768: } while(1);
769: } /* if */
770:
771: } /* for */
772: return(changes);
773: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.