|
|
1.1 root 1: /* Subroutines for gcc2 for pdp11.
2: Copyright (C) 1994 Free Software Foundation, Inc.
3: Contributed by Michael K. Gschwind ([email protected]).
4:
5: This file is part of GNU CC.
6:
7: GNU CC is free software; you can redistribute it and/or modify
8: it under the terms of the GNU General Public License as published by
9: the Free Software Foundation; either version 1, or (at your option)
10: any later version.
11:
12: GNU CC is distributed in the hope that it will be useful,
13: but WITHOUT ANY WARRANTY; without even the implied warranty of
14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15: GNU General Public License for more details.
16:
17: You should have received a copy of the GNU General Public License
18: along with GNU CC; see the file COPYING. If not, write to
19: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20:
21: #ifndef FILE
22: #include <stdio.h>
23: #endif
24: #include "config.h"
25: #include "rtl.h"
26: #include "regs.h"
27: #include "hard-reg-set.h"
28: #include "real.h"
29: #include "insn-config.h"
30: #include "conditions.h"
31: #include "insn-flags.h"
32: #include "output.h"
33: #include "insn-attr.h"
34:
35: /*
36: #define FPU_REG_P(X) ((X)>=8 && (X)<14)
37: #define CPU_REG_P(X) ((X)>=0 && (X)<8)
38: */
39:
40: /* this is the current value returned by the macro FIRST_PARM_OFFSET
41: defined in tm.h */
42: int current_first_parm_offset;
43:
44: /* This is where the condition code register lives. */
45: /* rtx cc0_reg_rtx; - no longer needed? */
46:
47: static rtx find_addr_reg ();
48:
49: /* Nonzero if OP is a valid second operand for an arithmetic insn. */
50:
51: int
52: arith_operand (op, mode)
53: rtx op;
54: enum machine_mode mode;
55: {
56: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
57: }
58:
59: int
60: const_immediate_operand (op, mode)
61: rtx op;
62: enum machine_mode mode;
63: {
64: return (GET_CODE (op) == CONST_INT);
65: }
66:
67: int
68: immediate15_operand (op, mode)
69: rtx op;
70: enum machine_mode mode;
71: {
72: return (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0x8000) == 0x0000));
73: }
74:
75: int
76: expand_shift_operand (op, mode)
77: rtx op;
78: enum machine_mode mode;
79: {
80: return (GET_CODE (op) == CONST_INT
81: && abs (INTVAL(op)) > 1
82: && abs (INTVAL(op)) <= 4);
83: }
84:
85: /*
86: stream is a stdio stream to output the code to.
87: size is an int: how many units of temporary storage to allocate.
88: Refer to the array `regs_ever_live' to determine which registers
89: to save; `regs_ever_live[I]' is nonzero if register number I
90: is ever used in the function. This macro is responsible for
91: knowing which registers should not be saved even if used.
92: */
93:
94: void
95: output_function_prologue(stream, size)
96: FILE *stream;
97: int size;
98: {
99: extern char call_used_regs[];
100: extern int frame_pointer_needed;
101:
102: int fsize = ((size) + 1) & ~1;
103: int regno, nregs, i;
104: int offset = 0;
105:
106: int via_ac = -1;
107:
108: fprintf (stream, "\n\t; /* function prologue %s*/\n", current_function_name);
109:
110: /* if we are outputting code for main,
111: the switch FPU to right mode if TARGET_FPU */
112: if ( (strcmp ("main", current_function_name) == 0)
113: && TARGET_FPU)
114: {
115: fprintf(stream, "\t;/* switch cpu to double float, single integer */\n");
116: fprintf(stream, "\tsetd\n");
117: fprintf(stream, "\tseti\n\n");
118: }
119:
120: if (frame_pointer_needed)
121: {
122: fprintf(stream, "\tmov fp, -(sp)\n");
123: fprintf(stream, "\tmov sp, fp\n");
124: }
125: else
126: {
127: /* DON'T SAVE FP */
128: }
129:
130: /* make frame */
131: if (fsize)
132: fprintf (stream, "\tsub $%d, sp\n", fsize);
133:
134: /* save CPU registers */
135: for (regno = 0; regno < 8; regno++)
136: if (regs_ever_live[regno] && ! call_used_regs[regno])
137: if (! ((regno == FRAME_POINTER_REGNUM)
138: && frame_pointer_needed))
139: fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]);
140: /* fpu regs saving */
141:
142: /* via_ac specifies the ac to use for saving ac4, ac5 */
143: via_ac = -1;
144:
145: for (regno = 8; regno < FIRST_PSEUDO_REGISTER ; regno++)
146: {
147: /* ac0 - ac3 */
148: if (LOAD_FPU_REG_P(regno)
149: && regs_ever_live[regno]
150: && ! call_used_regs[regno])
151: {
152: fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[regno]);
153: via_ac = regno;
154: }
155:
156: /* maybe make ac4, ac5 call used regs?? */
157: /* ac4 - ac5 */
158: if (NO_LOAD_FPU_REG_P(regno)
159: && regs_ever_live[regno]
160: && ! call_used_regs[regno])
161: {
162: if (via_ac == -1)
163: abort();
164:
165: fprintf (stream, "\tfldd %s, %s\n", reg_names[regno], reg_names[via_ac]);
166: fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[via_ac]);
167: }
168: }
169:
170: fprintf (stream, "\t;/* end of prologue */\n\n");
171: }
172:
173: /*
174: The function epilogue should not depend on the current stack pointer!
175: It should use the frame pointer only. This is mandatory because
176: of alloca; we also take advantage of it to omit stack adjustments
177: before returning. */
178:
179: /* maybe we can make leaf functions faster by switching to the
180: second register file - this way we don't have to save regs!
181: leaf functions are ~ 50% of all functions (dynamically!)
182:
183: set/clear bit 11 (dec. 2048) of status word for switching register files -
184: but how can we do this? the pdp11/45 manual says bit may only
185: be set (p.24), but not cleared!
186:
187: switching to kernel is probably more expensive, so we'll leave it
188: like this and not use the second set of registers...
189:
190: maybe as option if you want to generate code for kernel mode? */
191:
192:
193: void
194: output_function_epilogue(stream, size)
195: FILE *stream;
196: int size;
197: {
198: extern char call_used_regs[];
199: extern int may_call_alloca;
200:
201: int fsize = ((size) + 1) & ~1;
202: int nregs, regno, i, j, k, adjust_fp;
203:
204: int via_ac;
205:
206: fprintf (stream, "\n\t; /*function epilogue */\n");
207:
208: if (frame_pointer_needed)
209: {
210: /* hope this is safe - m68k does it also .... */
211: regs_ever_live[FRAME_POINTER_REGNUM] = 0;
212:
213: for (i =7, j = 0 ; i >= 0 ; i--)
214: if (regs_ever_live[i] && ! call_used_regs[i])
215: j++;
216:
217: /* remember # of pushed bytes for CPU regs */
218: k = 2*j;
219:
220: for (i =7 ; i >= 0 ; i--)
221: if (regs_ever_live[i] && ! call_used_regs[i])
222: fprintf(stream, "\tmov %d(fp), %s\n",-fsize-2*j--, reg_names[i]);
223:
224: /* get ACs */
225: via_ac = FIRST_PSEUDO_REGISTER -1;
226:
227: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
228: if (regs_ever_live[i] && ! call_used_regs[i])
229: {
230: via_ac = i;
231: k += 8;
232: }
233:
234: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
235: {
236: if (LOAD_FPU_REG_P(i)
237: && regs_ever_live[i]
238: && ! call_used_regs[i])
239: {
240: fprintf(stream, "\tfldd %d(fp), %s\n", -fsize-k, reg_names[i]);
241: k -= 8;
242: }
243:
244: if (NO_LOAD_FPU_REG_P(i)
245: && regs_ever_live[i]
246: && ! call_used_regs[i])
247: {
248: if (! LOAD_FPU_REG_P(via_ac))
249: abort();
250:
251: fprintf(stream, "\tfldd %d(fp), %s\n", -fsize-k, reg_names[via_ac]);
252: fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]);
253: k -= 8;
254: }
255: }
256:
257: fprintf(stream, "\tmov fp, sp\n");
258: fprintf (stream, "\tmov (sp)+, fp\n");
259: }
260: else
261: {
262: via_ac = FIRST_PSEUDO_REGISTER -1;
263:
264: /* get ACs */
265: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
266: if (regs_ever_live[i] && call_used_regs[i])
267: via_ac = i;
268:
269: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
270: {
271: if (LOAD_FPU_REG_P(i)
272: && regs_ever_live[i]
273: && ! call_used_regs[i])
274: fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[i]);
275:
276: if (NO_LOAD_FPU_REG_P(i)
277: && regs_ever_live[i]
278: && ! call_used_regs[i])
279: {
280: if (! LOAD_FPU_REG_P(via_ac))
281: abort();
282:
283: fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[via_ac]);
284: fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]);
285: }
286: }
287:
288: for (i=7; i >= 0; i--)
289: if (regs_ever_live[i] && !call_used_regs[i])
290: fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]);
291:
292: if (fsize)
293: fprintf((stream), "\tadd $%d, sp\n", fsize);
294: }
295:
296: fprintf (stream, "\trts pc\n");
297: fprintf (stream, "\t;/* end of epilogue*/\n\n\n");
298: }
299:
300: /* Return the best assembler insn template
301: for moving operands[1] into operands[0] as a fullword. */
302: static char *
303: singlemove_string (operands)
304: rtx *operands;
305: {
306: if (operands[1] != const0_rtx)
307: return "mov %1,%0";
308:
309: return "clr %0";
310: }
311:
312:
313: /* Output assembler code to perform a doubleword move insn
314: with operands OPERANDS. */
315:
316: char *
317: output_move_double (operands)
318: rtx *operands;
319: {
320: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
321: rtx latehalf[2];
322: rtx addreg0 = 0, addreg1 = 0;
323:
324: /* First classify both operands. */
325:
326: if (REG_P (operands[0]))
327: optype0 = REGOP;
328: else if (offsettable_memref_p (operands[0]))
329: optype0 = OFFSOP;
330: else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
331: optype0 = POPOP;
332: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
333: optype0 = PUSHOP;
334: else if (GET_CODE (operands[0]) == MEM)
335: optype0 = MEMOP;
336: else
337: optype0 = RNDOP;
338:
339: if (REG_P (operands[1]))
340: optype1 = REGOP;
341: else if (CONSTANT_P (operands[1]))
342: #if 0
343: || GET_CODE (operands[1]) == CONST_DOUBLE)
344: #endif
345: optype1 = CNSTOP;
346: else if (offsettable_memref_p (operands[1]))
347: optype1 = OFFSOP;
348: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
349: optype1 = POPOP;
350: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
351: optype1 = PUSHOP;
352: else if (GET_CODE (operands[1]) == MEM)
353: optype1 = MEMOP;
354: else
355: optype1 = RNDOP;
356:
357: /* Check for the cases that the operand constraints are not
358: supposed to allow to happen. Abort if we get one,
359: because generating code for these cases is painful. */
360:
361: if (optype0 == RNDOP || optype1 == RNDOP)
362: abort ();
363:
364: /* If one operand is decrementing and one is incrementing
365: decrement the former register explicitly
366: and change that operand into ordinary indexing. */
367:
368: if (optype0 == PUSHOP && optype1 == POPOP)
369: {
370: operands[0] = XEXP (XEXP (operands[0], 0), 0);
371: output_asm_insn ("sub $4,%0", operands);
372: operands[0] = gen_rtx (MEM, SImode, operands[0]);
373: optype0 = OFFSOP;
374: }
375: if (optype0 == POPOP && optype1 == PUSHOP)
376: {
377: operands[1] = XEXP (XEXP (operands[1], 0), 0);
378: output_asm_insn ("sub $4,%1", operands);
379: operands[1] = gen_rtx (MEM, SImode, operands[1]);
380: optype1 = OFFSOP;
381: }
382:
383: /* If an operand is an unoffsettable memory ref, find a register
384: we can increment temporarily to make it refer to the second word. */
385:
386: if (optype0 == MEMOP)
387: addreg0 = find_addr_reg (XEXP (operands[0], 0));
388:
389: if (optype1 == MEMOP)
390: addreg1 = find_addr_reg (XEXP (operands[1], 0));
391:
392: /* Ok, we can do one word at a time.
393: Normally we do the low-numbered word first,
394: but if either operand is autodecrementing then we
395: do the high-numbered word first.
396:
397: In either case, set up in LATEHALF the operands to use
398: for the high-numbered word and in some cases alter the
399: operands in OPERANDS to be suitable for the low-numbered word. */
400:
401: if (optype0 == REGOP)
402: latehalf[0] = gen_rtx (REG, HImode, REGNO (operands[0]) + 1);
403: else if (optype0 == OFFSOP)
404: latehalf[0] = adj_offsettable_operand (operands[0], 2);
405: else
406: latehalf[0] = operands[0];
407:
408: if (optype1 == REGOP)
409: latehalf[1] = gen_rtx (REG, HImode, REGNO (operands[1]) + 1);
410: else if (optype1 == OFFSOP)
411: latehalf[1] = adj_offsettable_operand (operands[1], 2);
412: else if (optype1 == CNSTOP)
413: {
414: if (CONSTANT_P (operands[1]))
415: {
416: /* now the mess begins, high word is in lower word???
417:
418: that's what ashc makes me think, but I don't remember :-( */
419: latehalf[1] = gen_rtx(CONST_INT, VOIDmode,
420: INTVAL(operands[1])>>16);
421: operands[1] = gen_rtx(CONST_INT, VOIDmode,
422: INTVAL(operands[1])&0xff);
423: }
424: else if (GET_CODE (operands[1]) == CONST_DOUBLE)
425: {
426: /* immediate 32 bit values not allowed */
427: abort();
428: }
429: }
430: else
431: latehalf[1] = operands[1];
432:
433: /* If insn is effectively movd N(sp),-(sp) then we will do the
434: high word first. We should use the adjusted operand 1 (which is N+4(sp))
435: for the low word as well, to compensate for the first decrement of sp. */
436: if (optype0 == PUSHOP
437: && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
438: && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
439: operands[1] = latehalf[1];
440:
441: /* If one or both operands autodecrementing,
442: do the two words, high-numbered first. */
443:
444: /* Likewise, the first move would clobber the source of the second one,
445: do them in the other order. This happens only for registers;
446: such overlap can't happen in memory unless the user explicitly
447: sets it up, and that is an undefined circumstance. */
448:
449: if (optype0 == PUSHOP || optype1 == PUSHOP
450: || (optype0 == REGOP && optype1 == REGOP
451: && REGNO (operands[0]) == REGNO (latehalf[1])))
452: {
453: /* Make any unoffsettable addresses point at high-numbered word. */
454: if (addreg0)
455: output_asm_insn ("add $2,%0", &addreg0);
456: if (addreg1)
457: output_asm_insn ("add $2,%0", &addreg1);
458:
459: /* Do that word. */
460: output_asm_insn (singlemove_string (latehalf), latehalf);
461:
462: /* Undo the adds we just did. */
463: if (addreg0)
464: output_asm_insn ("sub $2,%0", &addreg0);
465: if (addreg1)
466: output_asm_insn ("sub $2,%0", &addreg1);
467:
468: /* Do low-numbered word. */
469: return singlemove_string (operands);
470: }
471:
472: /* Normal case: do the two words, low-numbered first. */
473:
474: output_asm_insn (singlemove_string (operands), operands);
475:
476: /* Make any unoffsettable addresses point at high-numbered word. */
477: if (addreg0)
478: output_asm_insn ("add $2,%0", &addreg0);
479: if (addreg1)
480: output_asm_insn ("add $2,%0", &addreg1);
481:
482: /* Do that word. */
483: output_asm_insn (singlemove_string (latehalf), latehalf);
484:
485: /* Undo the adds we just did. */
486: if (addreg0)
487: output_asm_insn ("sub $2,%0", &addreg0);
488: if (addreg1)
489: output_asm_insn ("sub $2,%0", &addreg1);
490:
491: return "";
492: }
493: /* Output assembler code to perform a quadword move insn
494: with operands OPERANDS. */
495:
496: char *
497: output_move_quad (operands)
498: rtx *operands;
499: {
500: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
501: rtx latehalf[2];
502: rtx addreg0 = 0, addreg1 = 0;
503:
504: output_asm_insn(";; movdi/df: %1 -> %0", operands);
505:
506: if (REG_P (operands[0]))
507: optype0 = REGOP;
508: else if (offsettable_memref_p (operands[0]))
509: optype0 = OFFSOP;
510: else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
511: optype0 = POPOP;
512: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
513: optype0 = PUSHOP;
514: else if (GET_CODE (operands[0]) == MEM)
515: optype0 = MEMOP;
516: else
517: optype0 = RNDOP;
518:
519: if (REG_P (operands[1]))
520: optype1 = REGOP;
521: else if (CONSTANT_P (operands[1])
522: || GET_CODE (operands[1]) == CONST_DOUBLE)
523: optype1 = CNSTOP;
524: else if (offsettable_memref_p (operands[1]))
525: optype1 = OFFSOP;
526: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
527: optype1 = POPOP;
528: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
529: optype1 = PUSHOP;
530: else if (GET_CODE (operands[1]) == MEM)
531: optype1 = MEMOP;
532: else
533: optype1 = RNDOP;
534:
535: /* Check for the cases that the operand constraints are not
536: supposed to allow to happen. Abort if we get one,
537: because generating code for these cases is painful. */
538:
539: if (optype0 == RNDOP || optype1 == RNDOP)
540: abort ();
541:
542: /* check if we move a CPU reg to an FPU reg, or vice versa! */
543: if (optype0 == REGOP && optype1 == REGOP)
544: /* bogus - 64 bit cannot reside in CPU! */
545: if (CPU_REG_P(REGNO(operands[0]))
546: || CPU_REG_P (REGNO(operands[1])))
547: abort();
548:
549: if (optype0 == REGOP || optype1 == REGOP)
550: {
551: /* check for use of clrd????
552: if you ever allow ac4 and ac5 (now we require secondary load)
553: you must check whether
554: you want to load into them or store from them -
555: then dump ac0 into $help$ movce ac4/5 to ac0, do the
556: store from ac0, and restore ac0 - if you can find
557: an unused ac[0-3], use that and you save a store and a load!*/
558:
559: if (FPU_REG_P(REGNO(operands[0])))
560: {
561: if (GET_CODE(operands[1]) == CONST_DOUBLE)
562: {
563: union { double d; int i[2]; } u;
564: u.i[0] = CONST_DOUBLE_LOW (operands[1]);
565: u.i[1] = CONST_DOUBLE_HIGH (operands[1]);
566:
567: if (u.d == 0.0)
568: return "clrd %0";
569: }
570:
571: return "ldd %1, %0";
572: }
573:
574: if (FPU_REG_P(REGNO(operands[1])))
575: return "std %1, %0";
576: }
577:
578: /* If one operand is decrementing and one is incrementing
579: decrement the former register explicitly
580: and change that operand into ordinary indexing. */
581:
582: if (optype0 == PUSHOP && optype1 == POPOP)
583: {
584: operands[0] = XEXP (XEXP (operands[0], 0), 0);
585: output_asm_insn ("sub $8,%0", operands);
586: operands[0] = gen_rtx (MEM, DImode, operands[0]);
587: optype0 = OFFSOP;
588: }
589: if (optype0 == POPOP && optype1 == PUSHOP)
590: {
591: operands[1] = XEXP (XEXP (operands[1], 0), 0);
592: output_asm_insn ("sub $8,%1", operands);
593: operands[1] = gen_rtx (MEM, SImode, operands[1]);
594: optype1 = OFFSOP;
595: }
596:
597: /* If an operand is an unoffsettable memory ref, find a register
598: we can increment temporarily to make it refer to the second word. */
599:
600: if (optype0 == MEMOP)
601: addreg0 = find_addr_reg (XEXP (operands[0], 0));
602:
603: if (optype1 == MEMOP)
604: addreg1 = find_addr_reg (XEXP (operands[1], 0));
605:
606: /* Ok, we can do one word at a time.
607: Normally we do the low-numbered word first,
608: but if either operand is autodecrementing then we
609: do the high-numbered word first.
610:
611: In either case, set up in LATEHALF the operands to use
612: for the high-numbered word and in some cases alter the
613: operands in OPERANDS to be suitable for the low-numbered word. */
614:
615: if (optype0 == REGOP)
616: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
617: else if (optype0 == OFFSOP)
618: latehalf[0] = adj_offsettable_operand (operands[0], 4);
619: else
620: latehalf[0] = operands[0];
621:
622: if (optype1 == REGOP)
623: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
624: else if (optype1 == OFFSOP)
625: latehalf[1] = adj_offsettable_operand (operands[1], 4);
626: else if (optype1 == CNSTOP)
627: {
628: if (GET_CODE (operands[1]) == CONST_DOUBLE)
629: {
630: /* floats only. not yet supported!
631:
632: -- compute it into PDP float format, - internally,
633: just use IEEE and ignore possible problems ;-)
634:
635: we might get away with it !!!! */
636:
637: abort();
638:
639: #ifndef HOST_WORDS_BIG_ENDIAN
640: latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
641: CONST_DOUBLE_LOW (operands[1]));
642: operands[1] = gen_rtx (CONST_INT, VOIDmode,
643: CONST_DOUBLE_HIGH (operands[1]));
644: #else /* HOST_WORDS_BIG_ENDIAN */
645: latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
646: CONST_DOUBLE_HIGH (operands[1]));
647: operands[1] = gen_rtx (CONST_INT, VOIDmode,
648: CONST_DOUBLE_LOW (operands[1]));
649: #endif /* HOST_WORDS_BIG_ENDIAN */
650: }
651: }
652: else
653: latehalf[1] = operands[1];
654:
655: /* If insn is effectively movd N(sp),-(sp) then we will do the
656: high word first. We should use the adjusted operand 1 (which is N+4(sp))
657: for the low word as well, to compensate for the first decrement of sp. */
658: if (optype0 == PUSHOP
659: && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
660: && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
661: operands[1] = latehalf[1];
662:
663: /* If one or both operands autodecrementing,
664: do the two words, high-numbered first. */
665:
666: /* Likewise, the first move would clobber the source of the second one,
667: do them in the other order. This happens only for registers;
668: such overlap can't happen in memory unless the user explicitly
669: sets it up, and that is an undefined circumstance. */
670:
671: if (optype0 == PUSHOP || optype1 == PUSHOP
672: || (optype0 == REGOP && optype1 == REGOP
673: && REGNO (operands[0]) == REGNO (latehalf[1])))
674: {
675: /* Make any unoffsettable addresses point at high-numbered word. */
676: if (addreg0)
677: output_asm_insn ("add $4,%0", &addreg0);
678: if (addreg1)
679: output_asm_insn ("add $4,%0", &addreg1);
680:
681: /* Do that word. */
682: output_asm_insn(output_move_double(latehalf), latehalf);
683:
684: /* Undo the adds we just did. */
685: if (addreg0)
686: output_asm_insn ("sub $4,%0", &addreg0);
687: if (addreg1)
688: output_asm_insn ("sub $4,%0", &addreg1);
689:
690: /* Do low-numbered word. */
691: return output_move_double (operands);
692: }
693:
694: /* Normal case: do the two words, low-numbered first. */
695:
696: output_asm_insn (output_move_double (operands), operands);
697:
698: /* Make any unoffsettable addresses point at high-numbered word. */
699: if (addreg0)
700: output_asm_insn ("add $4,%0", &addreg0);
701: if (addreg1)
702: output_asm_insn ("add $4,%0", &addreg1);
703:
704: /* Do that word. */
705: output_asm_insn (output_move_double (latehalf), latehalf);
706:
707: /* Undo the adds we just did. */
708: if (addreg0)
709: output_asm_insn ("sub $4,%0", &addreg0);
710: if (addreg1)
711: output_asm_insn ("sub $4,%0", &addreg1);
712:
713: return "";
714: }
715:
716:
717: /* Return a REG that occurs in ADDR with coefficient 1.
718: ADDR can be effectively incremented by incrementing REG. */
719:
720: static rtx
721: find_addr_reg (addr)
722: rtx addr;
723: {
724: while (GET_CODE (addr) == PLUS)
725: {
726: if (GET_CODE (XEXP (addr, 0)) == REG)
727: addr = XEXP (addr, 0);
728: if (GET_CODE (XEXP (addr, 1)) == REG)
729: addr = XEXP (addr, 1);
730: if (CONSTANT_P (XEXP (addr, 0)))
731: addr = XEXP (addr, 1);
732: if (CONSTANT_P (XEXP (addr, 1)))
733: addr = XEXP (addr, 0);
734: }
735: if (GET_CODE (addr) == REG)
736: return addr;
737: return 0;
738: }
739:
740: /* Output an ascii string. */
741: output_ascii (file, p, size)
742: FILE *file;
743: char *p;
744: int size;
745: {
746: int i;
747:
748: fprintf (file, "\t.byte \"");
749:
750: for (i = 0; i < size; i++)
751: {
752: register int c = p[i];
753: if (c == '\"' || c == '\\')
754: putc ('\\', file);
755: if (c >= ' ' && c < 0177)
756: putc (c, file);
757: else
758: {
759: fprintf (file, "\\%03o", c);
760: /* After an octal-escape, if a digit follows,
761: terminate one string constant and start another.
762: The Vax assembler fails to stop reading the escape
763: after three digits, so this is the only way we
764: can get it to parse the data properly. */
765: if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9')
766: fprintf (file, "\"\n\tstring \"");
767: }
768: }
769: fprintf (file, "\"\n");
770: }
771:
772:
773: /* --- stole from out-vax, needs changes */
774:
775: print_operand_address (file, addr)
776: FILE *file;
777: register rtx addr;
778: {
779: register rtx reg1, reg2, breg, ireg;
780: rtx offset;
781:
782: retry:
783:
784: switch (GET_CODE (addr))
785: {
786: case MEM:
787: fprintf (file, "@");
788: addr = XEXP (addr, 0);
789: goto retry;
790:
791: case REG:
792: fprintf (file, "(%s)", reg_names[REGNO (addr)]);
793: break;
794:
795: case PRE_DEC:
796: fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]);
797: break;
798:
799: case POST_INC:
800: fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]);
801: break;
802:
803: case PLUS:
804: reg1 = 0; reg2 = 0;
805: ireg = 0; breg = 0;
806: offset = 0;
807: if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
808: || GET_CODE (XEXP (addr, 0)) == MEM)
809: {
810: offset = XEXP (addr, 0);
811: addr = XEXP (addr, 1);
812: }
813: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
814: || GET_CODE (XEXP (addr, 1)) == MEM)
815: {
816: offset = XEXP (addr, 1);
817: addr = XEXP (addr, 0);
818: }
819: if (GET_CODE (addr) != PLUS)
820: ;
821: else if (GET_CODE (XEXP (addr, 0)) == MULT)
822: {
823: reg1 = XEXP (addr, 0);
824: addr = XEXP (addr, 1);
825: }
826: else if (GET_CODE (XEXP (addr, 1)) == MULT)
827: {
828: reg1 = XEXP (addr, 1);
829: addr = XEXP (addr, 0);
830: }
831: else if (GET_CODE (XEXP (addr, 0)) == REG)
832: {
833: reg1 = XEXP (addr, 0);
834: addr = XEXP (addr, 1);
835: }
836: else if (GET_CODE (XEXP (addr, 1)) == REG)
837: {
838: reg1 = XEXP (addr, 1);
839: addr = XEXP (addr, 0);
840: }
841: if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
842: {
843: if (reg1 == 0)
844: reg1 = addr;
845: else
846: reg2 = addr;
847: addr = 0;
848: }
849: if (offset != 0)
850: {
851: if (addr != 0) abort ();
852: addr = offset;
853: }
854: if (reg1 != 0 && GET_CODE (reg1) == MULT)
855: {
856: breg = reg2;
857: ireg = reg1;
858: }
859: else if (reg2 != 0 && GET_CODE (reg2) == MULT)
860: {
861: breg = reg1;
862: ireg = reg2;
863: }
864: else if (reg2 != 0 || GET_CODE (addr) == MEM)
865: {
866: breg = reg2;
867: ireg = reg1;
868: }
869: else
870: {
871: breg = reg1;
872: ireg = reg2;
873: }
874: if (addr != 0)
875: output_address (addr);
876: if (breg != 0)
877: {
878: if (GET_CODE (breg) != REG)
879: abort ();
880: fprintf (file, "(%s)", reg_names[REGNO (breg)]);
881: }
882: if (ireg != 0)
883: {
884: if (GET_CODE (ireg) == MULT)
885: ireg = XEXP (ireg, 0);
886: if (GET_CODE (ireg) != REG)
887: abort ();
888: abort();
889: fprintf (file, "[%s]", reg_names[REGNO (ireg)]);
890: }
891: break;
892:
893: default:
894: output_addr_const (file, addr);
895: }
896: }
897:
898: /* register move costs, indexed by regs */
899:
900: static int move_costs[N_REG_CLASSES][N_REG_CLASSES] =
901: {
902: /* NO MUL GEN LFPU NLFPU FPU ALL */
903:
904: /* NO */ { 0, 0, 0, 0, 0, 0, 0},
905: /* MUL */ { 0, 2, 2, 10, 22, 22, 22},
906: /* GEN */ { 0, 2, 2, 10, 22, 22, 22},
907: /* LFPU */ { 0, 10, 10, 2, 2, 2, 10},
908: /* NLFPU */ { 0, 22, 22, 2, 2, 2, 22},
909: /* FPU */ { 0, 22, 22, 2, 2, 2, 22},
910: /* ALL */ { 0, 22, 22, 10, 22, 22, 22}
911: } ;
912:
913:
914: /* -- note that some moves are tremendously expensive,
915: because they require lots of tricks! do we have to
916: charge the costs incurred by secondary reload class
917: -- as we do here with 22 -- or not ? */
918:
919: int
920: register_move_cost(c1, c2)
921: enum reg_class c1, c2;
922: {
923: return move_costs[(int)c1][(int)c2];
924: }
925:
926: char *
927: output_jump(pos, neg, length)
928: int length;
929: char *pos, *neg;
930: {
931: static int x = 0;
932:
933: static char buf[1000];
934:
935: #if 0
936: /* currently we don't need this, because the tstdf and cmpdf
937: copy the condition code immediately, and other float operations are not
938: yet recognized as changing the FCC - if so, then the length-cost of all
939: jump insns increases by one, because we have to potentially copy the
940: FCC! */
941: if (cc_status.flags & CC_IN_FPU)
942: output_asm_insn("cfcc", NULL);
943: #endif
944:
945: switch (length)
946: {
947: case 1:
948:
949: strcpy(buf, pos);
950: strcat(buf, " %l0");
951:
952: return buf;
953:
954: case 3:
955:
956: sprintf(buf, "%s JMP_%d\n\tjmp %%l0\nJMP_%d:", neg, x, x);
957:
958: x++;
959:
960: return buf;
961:
962: default:
963:
964: abort();
965: }
966:
967: }
968:
969: void
970: notice_update_cc_on_set(exp, insn)
971: rtx exp;
972: rtx insn;
973: {
974: if (GET_CODE (SET_DEST (exp)) == CC0)
975: {
976: cc_status.flags = 0;
977: cc_status.value1 = SET_DEST (exp);
978: cc_status.value2 = SET_SRC (exp);
979:
980: /*
981: if (GET_MODE(SET_SRC(exp)) == DFmode)
982: cc_status.flags |= CC_IN_FPU;
983: */
984: }
985: else if ((GET_CODE (SET_DEST (exp)) == REG
986: || GET_CODE (SET_DEST (exp)) == MEM)
987: && GET_CODE (SET_SRC (exp)) != PC
988: && (GET_MODE (SET_DEST(exp)) == HImode
989: || GET_MODE (SET_DEST(exp)) == QImode)
990: && (GET_CODE (SET_SRC(exp)) == PLUS
991: || GET_CODE (SET_SRC(exp)) == MINUS
992: || GET_CODE (SET_SRC(exp)) == AND
993: || GET_CODE (SET_SRC(exp)) == IOR
994: || GET_CODE (SET_SRC(exp)) == XOR
995: || GET_CODE (SET_SRC(exp)) == NOT
996: || GET_CODE (SET_SRC(exp)) == NEG
997: || GET_CODE (SET_SRC(exp)) == REG
998: || GET_CODE (SET_SRC(exp)) == MEM))
999: {
1000: cc_status.flags = 0;
1001: cc_status.value1 = SET_SRC (exp);
1002: cc_status.value2 = SET_DEST (exp);
1003:
1004: if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
1005: && cc_status.value2
1006: && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
1007: cc_status.value2 = 0;
1008: if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM
1009: && cc_status.value2
1010: && GET_CODE (cc_status.value2) == MEM)
1011: cc_status.value2 = 0;
1012: }
1013: else if (GET_CODE (SET_SRC (exp)) == CALL)
1014: {
1015: CC_STATUS_INIT;
1016: }
1017: else if (GET_CODE (SET_DEST (exp)) == REG)
1018: /* what's this ? */
1019: {
1020: if ((cc_status.value1
1021: && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)))
1022: cc_status.value1 = 0;
1023: if ((cc_status.value2
1024: && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)))
1025: cc_status.value2 = 0;
1026: }
1027: else if (SET_DEST(exp) == pc_rtx)
1028: {
1029: /* jump */
1030: }
1031: else /* if (GET_CODE (SET_DEST (exp)) == MEM) */
1032: {
1033: /* the last else is a bit paranoic, but since nearly all instructions
1034: play with condition codes, it's reasonable! */
1035:
1036: CC_STATUS_INIT; /* paranoia*/
1037: }
1038: }
1039:
1040:
1041: int simple_memory_operand(op, mode)
1042: rtx op;
1043: enum machine_mode mode;
1044: {
1045: rtx addr, plus0, plus1;
1046: int offset = 0;
1047:
1048: /* Eliminate non-memory operations */
1049: if (GET_CODE (op) != MEM)
1050: return FALSE;
1051:
1052: #if 0
1053: /* dword operations really put out 2 instructions, so eliminate them. */
1054: if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
1055: return FALSE;
1056: #endif
1057:
1058: /* Decode the address now. */
1059:
1060: indirection:
1061:
1062: addr = XEXP (op, 0);
1063:
1064: switch (GET_CODE (addr))
1065: {
1066: case REG:
1067: /* (R0) - no extra cost */
1068: return 1;
1069:
1070: case PRE_DEC:
1071: case POST_INC:
1072: /* -(R0), (R0)+ - cheap! */
1073: return 0;
1074:
1075: case MEM:
1076: /* cheap - is encoded in addressing mode info!
1077:
1078: -- except for @(R0), which has to be @0(R0) !!! */
1079:
1080: if (GET_CODE (XEXP (addr, 0)) == REG)
1081: return 0;
1082:
1083: op=addr;
1084: goto indirection;
1085:
1086: case CONST_INT:
1087: case LABEL_REF:
1088: case CONST:
1089: case SYMBOL_REF:
1090: /* @#address - extra cost */
1091: return 0;
1092:
1093: case PLUS:
1094: /* X(R0) - extra cost */
1095: return 0;
1096: }
1097:
1098: return FALSE;
1099: }
1100:
1101:
1102: /*
1103: * output a block move:
1104: *
1105: * operands[0] ... to
1106: * operands[1] ... from
1107: * operands[2] ... length
1108: * operands[3] ... alignment
1109: * operands[4] ... scratch register
1110: */
1111:
1112:
1113: char *
1114: output_block_move(operands)
1115: rtx *operands;
1116: {
1117: static int count = 0;
1118: char buf[200];
1119:
1120: if (GET_CODE(operands[2]) == CONST_INT
1121: && TARGET_TIME)
1122: {
1123: if (INTVAL(operands[2]) < 16
1124: && INTVAL(operands[3]) == 1)
1125: {
1126: register int i;
1127:
1128: for (i = 1; i <= INTVAL(operands[2]); i++)
1129: output_asm_insn("movb (%1)+, (%0)+", operands);
1130:
1131: return "";
1132: }
1133: else if (INTVAL(operands[2]) < 32)
1134: {
1135: register int i;
1136:
1137: for (i = 1; i <= INTVAL(operands[2])/2; i++)
1138: output_asm_insn("mov (%1)+, (%0)+", operands);
1139:
1140: /* may I assume that moved quantity is
1141: multiple of alignment ???
1142:
1143: I HOPE SO !
1144: */
1145:
1146: return "";
1147: }
1148:
1149:
1150: /* can do other clever things, maybe... */
1151: }
1152:
1153: if (CONSTANT_P(operands[2]) )
1154: {
1155: /* just move count to scratch */
1156: output_asm_insn("mov %2, %4", operands);
1157: }
1158: else
1159: {
1160: /* just clobber the register */
1161: operands[4] = operands[2];
1162: }
1163:
1164:
1165: /* switch over alignment */
1166: switch (INTVAL(operands[3]))
1167: {
1168: case 1:
1169:
1170: /*
1171: x:
1172: movb (%1)+, (%0)+
1173:
1174: if (TARGET_45)
1175: sob %4,x
1176: else
1177: dec %4
1178: bgt x
1179:
1180: */
1181:
1182: sprintf(buf, "\nmovestrhi%d:", count);
1183: output_asm_insn(buf, NULL);
1184:
1185: output_asm_insn("movb (%1)+, (%0)+", operands);
1186:
1187: if (TARGET_45)
1188: {
1189: sprintf(buf, "sob %%4, movestrhi%d", count);
1190: output_asm_insn(buf, operands);
1191: }
1192: else
1193: {
1194: output_asm_insn("dec %4", operands);
1195:
1196: sprintf(buf, "bgt movestrhi%d", count);
1197: output_asm_insn(buf, NULL);
1198: }
1199:
1200: count ++;
1201: break;
1202:
1203: case 2:
1204:
1205: /*
1206: asr %4
1207:
1208: x:
1209:
1210: mov (%1)+, (%0)+
1211:
1212: if (TARGET_45)
1213: sob %4, x
1214: else
1215: dec %4
1216: bgt x
1217: */
1218:
1219: generate_compact_code:
1220:
1221: output_asm_insn("asr %4", operands);
1222:
1223: sprintf(buf, "\nmovestrhi%d:", count);
1224: output_asm_insn(buf, NULL);
1225:
1226: output_asm_insn("mov (%1)+, (%0)+", operands);
1227:
1228: if (TARGET_45)
1229: {
1230: sprintf(buf, "sob %%4, movestrhi%d", count);
1231: output_asm_insn(buf, operands);
1232: }
1233: else
1234: {
1235: output_asm_insn("dec %4", operands);
1236:
1237: sprintf(buf, "bgt movestrhi%d", count);
1238: output_asm_insn(buf, NULL);
1239: }
1240:
1241: count ++;
1242: break;
1243:
1244: case 4:
1245:
1246: /*
1247:
1248: asr %4
1249: asr %4
1250:
1251: x:
1252:
1253: mov (%1)+, (%0)+
1254: mov (%1)+, (%0)+
1255:
1256: if (TARGET_45)
1257: sob %4, x
1258: else
1259: dec %4
1260: bgt x
1261: */
1262:
1263: if (TARGET_SPACE)
1264: goto generate_compact_code;
1265:
1266: output_asm_insn("asr %4", operands);
1267: output_asm_insn("asr %4", operands);
1268:
1269: sprintf(buf, "\nmovestrhi%d:", count);
1270: output_asm_insn(buf, NULL);
1271:
1272: output_asm_insn("mov (%1)+, (%0)+", operands);
1273: output_asm_insn("mov (%1)+, (%0)+", operands);
1274:
1275: if (TARGET_45)
1276: {
1277: sprintf(buf, "sob %%4, movestrhi%d", count);
1278: output_asm_insn(buf, operands);
1279: }
1280: else
1281: {
1282: output_asm_insn("dec %4", operands);
1283:
1284: sprintf(buf, "bgt movestrhi%d", count);
1285: output_asm_insn(buf, NULL);
1286: }
1287:
1288: count ++;
1289: break;
1290:
1291: default:
1292:
1293: /*
1294:
1295: asr %4
1296: asr %4
1297: asr %4
1298:
1299: x:
1300:
1301: mov (%1)+, (%0)+
1302: mov (%1)+, (%0)+
1303: mov (%1)+, (%0)+
1304: mov (%1)+, (%0)+
1305:
1306: if (TARGET_45)
1307: sob %4, x
1308: else
1309: dec %4
1310: bgt x
1311: */
1312:
1313:
1314: if (TARGET_SPACE)
1315: goto generate_compact_code;
1316:
1317: output_asm_insn("asr %4", operands);
1318: output_asm_insn("asr %4", operands);
1319: output_asm_insn("asr %4", operands);
1320:
1321: sprintf(buf, "\nmovestrhi%d:", count);
1322: output_asm_insn(buf, NULL);
1323:
1324: output_asm_insn("mov (%1)+, (%0)+", operands);
1325: output_asm_insn("mov (%1)+, (%0)+", operands);
1326: output_asm_insn("mov (%1)+, (%0)+", operands);
1327: output_asm_insn("mov (%1)+, (%0)+", operands);
1328:
1329: if (TARGET_45)
1330: {
1331: sprintf(buf, "sob %%4, movestrhi%d", count);
1332: output_asm_insn(buf, operands);
1333: }
1334: else
1335: {
1336: output_asm_insn("dec %4", operands);
1337:
1338: sprintf(buf, "bgt movestrhi%d", count);
1339: output_asm_insn(buf, NULL);
1340: }
1341:
1342: count ++;
1343: break;
1344:
1345: ;
1346:
1347: }
1348:
1349: return "";
1350: }
1351:
1352: /* for future use */
1353: int
1354: comparison_operator_index(op)
1355: rtx op;
1356: {
1357: switch (GET_CODE(op))
1358: {
1359: case NE:
1360: return 0;
1361:
1362: case EQ:
1363: return 1;
1364:
1365: case GE:
1366: return 2;
1367:
1368: case GT:
1369: return 3;
1370:
1371: case LE:
1372: return 4;
1373:
1374: case LT:
1375: return 5;
1376:
1377: case GEU:
1378: return 6;
1379:
1380: case GTU:
1381: return 7;
1382:
1383: case LEU:
1384: return 8;
1385:
1386: case LTU:
1387: return 9;
1388:
1389: default:
1390: return -1;
1391: }
1392: }
1393:
1394: /* tests whether the rtx is a comparison operator */
1395: int
1396: comp_operator (op, mode)
1397: rtx op;
1398: enum machine_mode mode;
1399: {
1400: return comparison_operator_index(op) >= 0;
1401: }
1402:
1403:
1404: int
1405: legitimate_address_p (mode, address)
1406: enum machine_mode mode;
1407: rtx address;
1408: {
1409: /* #define REG_OK_STRICT */
1410: GO_IF_LEGITIMATE_ADDRESS(mode, address, win);
1411:
1412: return 0;
1413:
1414: win:
1415: return 1;
1416:
1417: /* #undef REG_OK_STRICT */
1418: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.