|
|
1.1 root 1: /* Subroutines for insn-output.c for Tahoe.
2: Copyright (C) 1989 Free Software Foundation, Inc.
3:
4: This file is part of GNU CC.
5:
6: GNU CC is free software; you can redistribute it and/or modify
7: it under the terms of the GNU General Public License as published by
8: the Free Software Foundation; either version 1, or (at your option)
9: any later version.
10:
11: GNU CC is distributed in the hope that it will be useful,
12: but WITHOUT ANY WARRANTY; without even the implied warranty of
13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: GNU General Public License for more details.
15:
16: You should have received a copy of the GNU General Public License
17: along with GNU CC; see the file COPYING. If not, write to
18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19:
20:
21: /*
22: * File: output-tahoe.c
23: *
24: * This port made at the University of Buffalo by Devon Bowen,
25: * Dale Wiles and Kevin Zachmann.
26: *
27: * Mail bugs reports or fixes to: [email protected]
28: */
29:
30:
31: /* most of the print_operand_address function was taken from the vax */
32: /* since the modes are basically the same. I had to add a special case, */
33: /* though, for symbol references with offsets. */
34:
35: #include <stdio.h>
36:
37: print_operand_address (file, addr)
38: FILE *file;
39: register rtx addr;
40: {
41: register rtx reg1, reg2, breg, ireg;
42: rtx offset;
43: static char *reg_name[] = REGISTER_NAMES;
44:
45: retry:
46: switch (GET_CODE (addr))
47: {
48: case MEM:
49: fprintf (file, "*");
50: addr = XEXP (addr, 0);
51: goto retry;
52:
53: case REG:
54: fprintf (file, "(%s)", reg_name [REGNO (addr)]);
55: break;
56:
57: case PRE_DEC:
58: fprintf (file, "-(%s)", reg_name [REGNO (XEXP (addr, 0))]);
59: break;
60:
61: case POST_INC:
62: fprintf (file, "(%s)+", reg_name [REGNO (XEXP (addr, 0))]);
63: break;
64:
65: case PLUS:
66: reg1 = 0; reg2 = 0;
67: ireg = 0; breg = 0;
68: offset = 0;
69:
70: if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
71: && GET_CODE (XEXP (addr, 1)) == CONST_INT)
72: output_addr_const (file, addr);
73:
74: if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
75: && GET_CODE (XEXP (addr, 0)) == CONST_INT)
76: output_addr_const (file, addr);
77:
78: if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
79: || GET_CODE (XEXP (addr, 0)) == MEM)
80: {
81: offset = XEXP (addr, 0);
82: addr = XEXP (addr, 1);
83: }
84: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
85: || GET_CODE (XEXP (addr, 1)) == MEM)
86: {
87: offset = XEXP (addr, 1);
88: addr = XEXP (addr, 0);
89: }
90: if (GET_CODE (addr) != PLUS)
91: ;
92: else if (GET_CODE (XEXP (addr, 0)) == MULT)
93: {
94: reg1 = XEXP (addr, 0);
95: addr = XEXP (addr, 1);
96: }
97: else if (GET_CODE (XEXP (addr, 1)) == MULT)
98: {
99: reg1 = XEXP (addr, 1);
100: addr = XEXP (addr, 0);
101: }
102: else if (GET_CODE (XEXP (addr, 0)) == REG)
103: {
104: reg1 = XEXP (addr, 0);
105: addr = XEXP (addr, 1);
106: }
107: else if (GET_CODE (XEXP (addr, 1)) == REG)
108: {
109: reg1 = XEXP (addr, 1);
110: addr = XEXP (addr, 0);
111: }
112: if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
113: {
114: if (reg1 == 0)
115: reg1 = addr;
116: else
117: reg2 = addr;
118: addr = 0;
119: }
120: if (offset != 0)
121: {
122: if (addr != 0) abort ();
123: addr = offset;
124: }
125: if (reg1 != 0 && GET_CODE (reg1) == MULT)
126: {
127: breg = reg2;
128: ireg = reg1;
129: }
130: else if (reg2 != 0 && GET_CODE (reg2) == MULT)
131: {
132: breg = reg1;
133: ireg = reg2;
134: }
135: else if (reg2 != 0 || GET_CODE (addr) == MEM)
136: {
137: breg = reg2;
138: ireg = reg1;
139: }
140: else
141: {
142: breg = reg1;
143: ireg = reg2;
144: }
145: if (addr != 0)
146: output_address (offset);
147: if (breg != 0)
148: {
149: if (GET_CODE (breg) != REG)
150: abort ();
151: fprintf (file, "(%s)", reg_name[REGNO (breg)]);
152: }
153: if (ireg != 0)
154: {
155: if (GET_CODE (ireg) == MULT)
156: ireg = XEXP (ireg, 0);
157: if (GET_CODE (ireg) != REG)
158: abort ();
159: fprintf (file, "[%s]", reg_name[REGNO (ireg)]);
160: }
161: break;
162:
163: default:
164: output_addr_const (file, addr);
165: }
166: }
167:
168:
169: /* Do a quick check and find out what the best way to do the */
170: /* mini-move is. Could be a push or a move..... */
171:
172: static char *
173: singlemove_string (operands)
174: rtx *operands;
175: {
176: if (GET_CODE (operands[0]) == MEM
177: && GET_CODE (XEXP (operands[0],0)) == PRE_DEC)
178: return "pushl %1";
179: return "movl %1,%0";
180: }
181:
182:
183: /* given the rtx for an address, return true if the given */
184: /* register number is used in the address somewhere. */
185:
186: int
187: regisused (addr,regnum)
188: rtx addr;
189: int regnum;
190: {
191: if (GET_CODE (addr) == REG)
192: {
193: if (REGNO (addr) == regnum)
194: return (1);
195: else
196: return (0);
197: }
198:
199: if (GET_CODE (addr) == MEM)
200: return regisused (XEXP (addr,0),regnum);
201:
202: if (GET_CODE (addr) == MULT || GET_CODE (addr) == PLUS)
203: return (regisused (XEXP (addr,0),regnum)
204: || regisused (XEXP (addr,1),regnum));
205:
206: return 0;
207: }
208:
209:
210: /* Given some rtx, traverse it and return the register used in a */
211: /* index. If no index is found, return 0. */
212:
213: rtx
214: index_reg (addr)
215: rtx addr;
216: {
217: rtx temp;
218:
219: if (GET_CODE (addr) == MEM)
220: return index_reg (XEXP (addr,0));
221:
222: if (GET_CODE (addr) == MULT)
223: {
224: if (GET_CODE (XEXP (addr,0)) == REG)
225: return XEXP (addr,0);
226: else
227: return XEXP (addr,1);
228: }
229:
230: if (GET_CODE (addr) == PLUS)
231: {
232: if (temp = index_reg (XEXP (addr,0)))
233: return temp;
234: else
235: return index_reg (XEXP (addr,1));
236: }
237:
238: return 0;
239: }
240:
241:
242: /* simulate the move double by generating two movl's. You have */
243: /* to be careful about mixing modes here. A future improvement */
244: /* would be to allow immediate doubles. */
245:
246: char *
247: output_move_double (operands)
248: rtx *operands;
249: {
250: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, INDOP, CNSTOP, RNDOP } optype0, optype1;
251: rtx latehalf[2];
252: rtx shftreg0 = 0, shftreg1 = 0;
253: rtx temp0 = 0, temp1 = 0;
254: rtx addreg0 = 0, addreg1 = 0;
255: int dohighfirst = 0;
256:
257: /* First classify both operands. */
258:
259: if (REG_P (operands[0]))
260: optype0 = REGOP;
261: else if ((GET_CODE (operands[0])==MEM) && (shftreg0=index_reg (operands[0])))
262: optype0 = INDOP;
263: else if (offsettable_memref_p (operands[0]))
264: optype0 = OFFSOP;
265: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
266: {
267: optype0 = PUSHOP;
268: dohighfirst++;
269: }
270: else if (GET_CODE (operands[0]) == MEM)
271: optype0 = MEMOP;
272: else
273: optype0 = RNDOP;
274:
275: if (REG_P (operands[1]))
276: optype1 = REGOP;
277: else if ((GET_CODE (operands[1])==MEM) && (shftreg1=index_reg (operands[1])))
278: optype1 = INDOP;
279: else if (offsettable_memref_p (operands[1]))
280: optype1 = OFFSOP;
281: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
282: optype1 = POPOP;
283: else if (GET_CODE (operands[1]) == MEM)
284: optype1 = MEMOP;
285: else if (GET_CODE (operands[1]) == CONST_DOUBLE || CONSTANT_P (operands[1]))
286: optype1 = CNSTOP;
287: else
288: optype1 = RNDOP;
289:
290: /* set up for the high byte move for operand zero */
291:
292: switch (optype0)
293: {
294:
295: /* if it's a register, just use the next highest in the */
296: /* high address move. */
297:
298: case REGOP:
299: latehalf[0] = gen_rtx (REG,SImode,REGNO (operands[0])+1);
300: break;
301:
302: /* for an offsettable address, use the gcc function to */
303: /* modify the operand to get an offset of 4 higher for */
304: /* the second move. */
305:
306: case OFFSOP:
307: latehalf[0] = adj_offsettable_operand (operands[0], 4);
308: break;
309:
310: /* if the operand is MEMOP type, it must be a pointer */
311: /* to a pointer. So just remember to increase the mem */
312: /* location and use the same operand. */
313:
314: case MEMOP:
315: latehalf[0] = operands[0];
316: addreg0 = XEXP (operands[0],0);
317: break;
318:
319: /* if we're dealing with a push instruction, just leave */
320: /* the operand alone since it auto-increments. */
321:
322: case PUSHOP:
323: latehalf[0] = operands[0];
324: break;
325:
326: /* YUCK! Indexed addressing!! If the address is considered */
327: /* offsettable, go use the offset in the high part. Otherwise */
328: /* find what exactly is being added to the mutiplication. If */
329: /* it's a mem reference, increment that with the high part */
330: /* being unchanged to cause the shift. If it's a reg, do the */
331: /* same. If you can't identify it, abort. Remember that the */
332: /* shift register was already set during identification. */
333:
334: case INDOP:
335: if (offsettable_memref_p (operands[0]))
336: {
337: latehalf[0] = adj_offsettable_operand (operands[0],4);
338: break;
339: }
340:
341: latehalf[0] = operands[0];
342:
343: temp0 = XEXP (XEXP (operands[0],0),0);
344: if (GET_CODE (temp0) == MULT)
345: {
346: temp1 = temp0;
347: temp0 = XEXP (XEXP (operands[0],0),1);
348: }
349: else
350: {
351: temp1 = XEXP (XEXP (operands[0],0),1);
352: if (GET_CODE (temp1) != MULT)
353: abort ();
354: }
355:
356: if (GET_CODE (temp0) == MEM)
357: addreg0 = temp0;
358: else if (GET_CODE (temp0) == REG)
359: addreg0 = temp0;
360: else
361: abort ();
362:
363: break;
364:
365: /* if we don't know the operand type, print a friendly */
366: /* little error message... 8-) */
367:
368: case RNDOP:
369: default:
370: abort ();
371: }
372:
373: /* do the same setup for operand one */
374:
375: switch (optype1)
376: {
377:
378: case REGOP:
379: latehalf[1] = gen_rtx (REG,SImode,REGNO (operands[1])+1);
380: break;
381:
382: case OFFSOP:
383: latehalf[1] = adj_offsettable_operand (operands[1], 4);
384: break;
385:
386: case MEMOP:
387: latehalf[1] = operands[1];
388: addreg1 = XEXP (operands[1],0);
389: break;
390:
391: case POPOP:
392: latehalf[1] = operands[1];
393: break;
394:
395: case INDOP:
396: if (offsettable_memref_p (operands[1]))
397: {
398: latehalf[1] = adj_offsettable_operand (operands[1],4);
399: break;
400: }
401:
402: latehalf[1] = operands[1];
403:
404: temp0 = XEXP (XEXP (operands[1],0),0);
405: if (GET_CODE (temp0) == MULT)
406: {
407: temp1 = temp0;
408: temp0 = XEXP (XEXP (operands[1],0),1);
409: }
410: else
411: {
412: temp1 = XEXP (XEXP (operands[1],0),1);
413: if (GET_CODE (temp1) != MULT)
414: abort ();
415: }
416:
417: if (GET_CODE (temp0) == MEM)
418: addreg1 = temp0;
419: else if (GET_CODE (temp0) == REG)
420: addreg1 = temp0;
421: else
422: abort ();
423:
424: break;
425:
426: case CNSTOP:
427: /* Since this machine is big-endian,
428: the late half must be the low-order word for an integer,
429: or the latter word for a float. */
430: if (GET_CODE (operands[1]) == CONST_DOUBLE)
431: {
432: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
433: {
434: latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
435: CONST_DOUBLE_HIGH (operands[1]));
436: operands[1] = gen_rtx (CONST_INT, VOIDmode,
437: CONST_DOUBLE_LOW (operands[1]));
438: }
439: else
440: {
441: latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
442: CONST_DOUBLE_LOW (operands[1]));
443: operands[1] = gen_rtx (CONST_INT, VOIDmode,
444: CONST_DOUBLE_HIGH (operands[1]));
445: }
446: }
447: else
448: {
449: latehalf[1] = operands[1];
450: operands[1] = const0_rtx;
451: }
452: break;
453:
454: case RNDOP:
455: default:
456: abort ();
457: }
458:
459:
460: /* double the register used for shifting in both of the operands */
461: /* but make sure the same register isn't doubled twice! */
462:
463: if (shftreg0 && shftreg1 && rtx_equal_p (shftreg0, shftreg1))
464: output_asm_insn ("addl2 %0,%0", &shftreg0);
465: else
466: {
467: if (shftreg0)
468: output_asm_insn ("addl2 %0,%0", &shftreg0);
469: if (shftreg1)
470: output_asm_insn ("addl2 %0,%0", &shftreg1);
471: }
472:
473: /* if the destination is a register and that register is needed in */
474: /* the source addressing mode, swap the order of the moves since we */
475: /* don't want this destroyed til last. If both regs are used, not */
476: /* much we can do, so abort. If these becomes a problem, maybe we */
477: /* can do it on the stack? */
478:
479: if (GET_CODE (operands[0])==REG && regisused (operands[1],REGNO (operands[0])))
480: if (regisused (latehalf[1],REGNO (latehalf[0])))
481: 8;
482: else
483: dohighfirst++;
484:
485: /* if we're pushing, do the high address part first. */
486:
487: if (dohighfirst)
488: {
489:
490: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
491: output_asm_insn ("addl2 $4,%0", &addreg0);
492: else
493: {
494: if (addreg0)
495: output_asm_insn ("addl2 $4,%0", &addreg0);
496: if (addreg1)
497: output_asm_insn ("addl2 $4,%0", &addreg1);
498: }
499:
500: output_asm_insn (singlemove_string (latehalf), latehalf);
501:
502: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
503: output_asm_insn ("subl2 $4,%0", &addreg0);
504: else
505: {
506: if (addreg0)
507: output_asm_insn ("subl2 $4,%0", &addreg0);
508: if (addreg1)
509: output_asm_insn ("subl2 $4,%0", &addreg1);
510: }
511:
512: return singlemove_string (operands);
513: }
514:
515: output_asm_insn (singlemove_string (operands), operands);
516:
517: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
518: output_asm_insn ("addl2 $4,%0", &addreg0);
519: else
520: {
521: if (addreg0)
522: output_asm_insn ("addl2 $4,%0", &addreg0);
523: if (addreg1)
524: output_asm_insn ("addl2 $4,%0", &addreg1);
525: }
526:
527: output_asm_insn (singlemove_string (latehalf), latehalf);
528:
529: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
530: output_asm_insn ("subl2 $4,%0", &addreg0);
531: else
532: {
533: if (addreg0)
534: output_asm_insn ("subl2 $4,%0", &addreg0);
535: if (addreg1)
536: output_asm_insn ("subl2 $4,%0", &addreg1);
537: }
538:
539: if (shftreg0 && shftreg1 && (rtx_equal_p (shftreg0,shftreg1)))
540: output_asm_insn ("shar $1,%0,%0", &shftreg0);
541: else
542: {
543: if (shftreg0)
544: output_asm_insn ("shar $1,%0,%0", &shftreg0);
545: if (shftreg1)
546: output_asm_insn ("shar $1,%0,%0", &shftreg1);
547: }
548:
549: return "";
550: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.