]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/convex/convex.c
1a588e034c565678141f88904b98c037d680c9c3
[thirdparty/gcc.git] / gcc / config / convex / convex.c
1 /* Subroutines for insn-output.c for Convex.
2 Copyright (C) 1988, 1993, 1994, 1997 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, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include "tree.h"
24 #include "rtl.h"
25 #include "regs.h"
26 #include "hard-reg-set.h"
27 #include "real.h"
28 #include "insn-config.h"
29 #include "conditions.h"
30 #include "insn-flags.h"
31 #include "insn-attr.h"
32 #include "output.h"
33 #include "function.h"
34 #include "expr.h"
35
36 /* Tables used in convex.h */
37
38 char regno_ok_for_index_p_base[1 + LAST_VIRTUAL_REGISTER + 1];
39 enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
40 enum reg_class reg_class_from_letter[256];
41
42 /* Target cpu index. */
43
44 int target_cpu;
45
46 /* Boolean to keep track of whether the current section is .text or not.
47 Used by .align handler in convex.h. */
48
49 int current_section_is_text;
50
51 /* Communication between output_compare and output_condjump. */
52
53 static rtx cmp_operand0, cmp_operand1;
54 static char cmp_modech;
55
56 /* Forwards */
57
58 static rtx frame_argblock;
59 static int frame_argblock_size;
60 static rtx convert_arg_pushes ();
61 static void expand_movstr_call ();
62
63 /* Here from OVERRIDE_OPTIONS at startup. Initialize constant tables. */
64
65 init_convex ()
66 {
67 int regno;
68
69 /* Set A and S reg classes. */
70 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
71 if (A_REGNO_P (regno))
72 {
73 regno_ok_for_index_p[regno] = 1;
74 regno_reg_class[regno] = INDEX_REGS;
75 }
76 else
77 {
78 regno_ok_for_index_p[regno] = 0;
79 regno_reg_class[regno] = S_REGS;
80 }
81
82 /* Can't index off the stack pointer, register 0. */
83 regno_ok_for_index_p[STACK_POINTER_REGNUM] = 0;
84 regno_reg_class[STACK_POINTER_REGNUM] = SP_REGS;
85
86 /* Can't index off aliases of the stack pointer. */
87 regno_ok_for_index_p[VIRTUAL_INCOMING_ARGS_REGNUM] = 1;
88 regno_ok_for_index_p[VIRTUAL_STACK_VARS_REGNUM] = 1;
89 regno_ok_for_index_p[VIRTUAL_STACK_DYNAMIC_REGNUM] = 0;
90 regno_ok_for_index_p[VIRTUAL_OUTGOING_ARGS_REGNUM] = 0;
91
92 /* Can't index off hard reg -1 == pseudos not assigned */
93 regno_ok_for_index_p[-1] = 0;
94
95 /* Set reg class letters */
96 reg_class_from_letter['a'] = A_REGS;
97 reg_class_from_letter['A'] = INDEX_REGS;
98 reg_class_from_letter['d'] = S_REGS;
99
100 /* Turn off floating point exception enables in the psw. */
101 psw_disable_float ();
102 }
103
104 psw_disable_float ()
105 {
106 #if __convex__ && __GNUC__
107 register int *p;
108 asm ("mov fp,%0" : "=a" (p));
109 while (p)
110 {
111 p[1] &= ~0x1000c400;
112 p = (int *) p[2];
113 }
114 #endif
115 }
116 \f
117 /* Here to output code for a compare insn. Output nothing, just
118 record the operands and their mode. */
119
120 char *
121 output_cmp (operand0, operand1, modech)
122 rtx operand0, operand1;
123 char modech;
124 {
125 cmp_operand0 = operand0;
126 cmp_operand1 = operand1;
127 cmp_modech = modech;
128 return "";
129 }
130
131 /* Output code for a conditional jump. The preceding instruction
132 is necessarily a compare. Output two instructions, for example
133 eq.w a1,a2
134 jbra.t L5
135 for
136 (cmpsi a1 a2)
137 (beq L5)
138 */
139
140 char *
141 output_condjump (label, cond, jbr_sense)
142 rtx label;
143 char *cond;
144 char jbr_sense;
145 {
146 rtx operands[3];
147 char cmp_op[4];
148 char buf[80];
149 char jbr_regch;
150
151 strcpy (cmp_op, cond);
152
153 /* [BL] mean the value is being compared against immediate 0.
154 Use neg.x, which produces the same carry that eq.x #0 would if it
155 existed. In this case operands[1] is a scratch register, not a
156 compare operand. */
157
158 if (cmp_modech == 'B' || cmp_modech == 'L')
159 {
160 cmp_modech = cmp_modech - 'A' + 'a';
161 strcpy (cmp_op, "neg");
162 }
163
164 /* [WH] mean the value being compared resulted from "add.[wh] #-1,rk"
165 when rk was nonnegative -- we can omit equality compares against -1
166 or inequality compares against 0. */
167
168 else if (cmp_modech == 'W' || cmp_modech == 'H')
169 {
170 if (! strcmp (cmp_op, "eq") && cmp_operand1 == constm1_rtx)
171 jbr_sense ^= 't' ^ 'f';
172 else if (! strcmp (cmp_op, "lt") && cmp_operand1 == const0_rtx)
173 ;
174 else
175 cmp_modech = cmp_modech - 'A' + 'a';
176 }
177
178 /* Constant must be first; swap operands if necessary.
179 If lt, le, ltu, leu are swapped, change to le, lt, leu, ltu
180 and reverse the sense of the jump. */
181
182 if (! REG_P (cmp_operand1))
183 {
184 operands[0] = cmp_operand1;
185 operands[1] = cmp_operand0;
186 if (cmp_op[0] == 'l')
187 {
188 cmp_op[1] ^= 'e' ^ 't';
189 jbr_sense ^= 't' ^ 'f';
190 }
191 }
192 else
193 {
194 operands[0] = cmp_operand0;
195 operands[1] = cmp_operand1;
196 }
197
198 operands[2] = label;
199
200 if (S_REG_P (operands[1]))
201 jbr_regch = 's';
202 else if (A_REG_P (operands[1]))
203 jbr_regch = 'a';
204 else
205 abort ();
206
207 if (cmp_modech == 'W' || cmp_modech == 'H')
208 sprintf (buf, "jbr%c.%c %%l2", jbr_regch, jbr_sense);
209 else
210 sprintf (buf, "%s.%c %%0,%%1\n\tjbr%c.%c %%l2",
211 cmp_op, cmp_modech, jbr_regch, jbr_sense);
212 output_asm_insn (buf, operands);
213 return "";
214 }
215
216 /* Return 1 if OP is valid for cmpsf.
217 In IEEE mode, +/- zero compares are not handled by
218 the immediate versions of eq.s and on some machines, lt.s, and le.s.
219 So disallow 0.0 as the immediate operand of xx.s compares in IEEE mode. */
220
221 int
222 nonmemory_cmpsf_operand (op, mode)
223 rtx op;
224 enum machine_mode mode;
225 {
226 #if _IEEE_FLOAT_
227 if (op == CONST0_RTX (SFmode))
228 return 0;
229 #endif
230
231 return nonmemory_operand (op, mode);
232 }
233 \f
234 /* Convex /bin/as does not like unary minus in some contexts.
235 Simplify CONST addresses to remove it. */
236
237 rtx
238 simplify_for_convex (x)
239 rtx x;
240 {
241 switch (GET_CODE (x))
242 {
243 case MINUS:
244 if (GET_CODE (XEXP (x, 1)) == CONST_INT
245 && INTVAL (XEXP (x, 1)) < 0)
246 {
247 PUT_CODE (x, PLUS);
248 XEXP (x, 1) = GEN_INT (- INTVAL (XEXP (x, 1)));
249 }
250 break;
251
252 case CONST:
253 return simplify_for_convex (XEXP (x, 0));
254 }
255
256 return x;
257 }
258 \f
259 /* Routines to separate CONST_DOUBLEs into component parts. */
260
261 int
262 const_double_high_int (x)
263 rtx x;
264 {
265 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
266 return CONST_DOUBLE_LOW (x);
267 else
268 return CONST_DOUBLE_HIGH (x);
269 }
270
271 int
272 const_double_low_int (x)
273 rtx x;
274 {
275 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
276 return CONST_DOUBLE_HIGH (x);
277 else
278 return CONST_DOUBLE_LOW (x);
279 }
280 \f
281 /* Inline block copy. */
282
283 void
284 expand_movstr (operands)
285 rtx *operands;
286 {
287 rtx dest = operands[0];
288 rtx src = operands[1];
289 int align = INTVAL (operands[3]);
290 int nregs, maxsize;
291 unsigned len;
292 enum machine_mode mode;
293 rtx reg, load, store, prev_store, prev_store_2;
294 int size;
295
296 /* Decide how many regs to use, depending on load latency, and what
297 size pieces to move, depending on whether machine does unaligned
298 loads and stores efficiently. */
299
300 if (TARGET_C1)
301 {
302 /* ld.l latency is 4, no alignment problems. */
303 nregs = 3, maxsize = 8;
304 }
305 else if (TARGET_C2)
306 {
307 /* loads are latency 2 if we avoid ld.l not at least word aligned. */
308 if (align >= 4)
309 nregs = 2, maxsize = 8;
310 else
311 nregs = 2, maxsize = 4;
312 }
313 else if (TARGET_C34)
314 {
315 /* latency is 4 if aligned, horrible if not. */
316 nregs = 3, maxsize = align;
317 }
318 else if (TARGET_C38)
319 {
320 /* latency is 2 if at least word aligned, 3 or 4 if unaligned. */
321 if (align >= 4)
322 nregs = 2, maxsize = 8;
323 else
324 nregs = 3, maxsize = 8;
325 }
326 else
327 abort ();
328
329 /* Caller is not necessarily prepared for us to fail in this
330 expansion. So fall back by generating memcpy call here. */
331
332 if (GET_CODE (operands[2]) != CONST_INT
333 || (len = INTVAL (operands[2])) > (unsigned) 32 * maxsize)
334 {
335 expand_movstr_call (operands);
336 return;
337 }
338
339 reg = 0;
340 prev_store = prev_store_2 = 0;
341
342 while (len > 0)
343 {
344 if (len >= 8 && maxsize >= 8)
345 mode = DImode;
346 else if (len >= 4 && maxsize >= 4)
347 mode = SImode;
348 else if (len >= 2 && maxsize >= 2)
349 mode = HImode;
350 else
351 mode = QImode;
352
353 /* If no temp pseudo to reuse, or not the right mode, make one */
354 if (! reg || GET_MODE (reg) != mode)
355 reg = gen_reg_rtx (mode);
356
357 /* Get src and dest in the right mode */
358 if (GET_MODE (src) != mode)
359 src = change_address (src, mode, 0),
360 dest = change_address (dest, mode, 0);
361
362 /* Make load and store patterns for this piece */
363 load = gen_rtx (SET, VOIDmode, reg, src);
364 store = gen_rtx (SET, VOIDmode, dest, reg);
365
366 /* Emit the load and the store from last time.
367 When we emit a store, we can reuse its temp reg. */
368 emit_insn (load);
369 if (prev_store)
370 {
371 reg = SET_SRC (prev_store);
372 emit_insn (prev_store);
373 }
374 else
375 reg = 0;
376
377 /* Queue up the store, for next time or the time after that. */
378 if (nregs == 2)
379 prev_store = store;
380 else
381 prev_store = prev_store_2, prev_store_2 = store;
382
383 /* Advance to next piece. */
384 size = GET_MODE_SIZE (mode);
385 src = adj_offsettable_operand (src, size);
386 dest = adj_offsettable_operand (dest, size);
387 len -= size;
388 }
389
390 /* Finally, emit the last stores. */
391 if (prev_store)
392 emit_insn (prev_store);
393 if (prev_store_2)
394 emit_insn (prev_store_2);
395 }
396
397 static void
398 expand_movstr_call (operands)
399 rtx *operands;
400 {
401 emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
402 VOIDmode, 3,
403 XEXP (operands[0], 0), Pmode,
404 XEXP (operands[1], 0), Pmode,
405 convert_to_mode (TYPE_MODE (sizetype), operands[2],
406 TREE_UNSIGNED (sizetype)),
407 TYPE_MODE (sizetype));
408 }
409 \f
410 #if _IEEE_FLOAT_
411 #define MAX_FLOAT 3.4028234663852886e+38
412 #define MIN_FLOAT 1.1754943508222875e-38
413 #else
414 #define MAX_FLOAT 1.7014117331926443e+38
415 #define MIN_FLOAT 2.9387358770557188e-39
416 #endif
417
418 int
419 check_float_value (mode, dp, overflow)
420 enum machine_mode mode;
421 REAL_VALUE_TYPE *dp;
422 int overflow;
423 {
424 REAL_VALUE_TYPE d = *dp;
425
426 if (overflow)
427 {
428 *dp = MAX_FLOAT;
429 return 1;
430 }
431
432 if (mode == SFmode)
433 {
434 if (d > MAX_FLOAT)
435 {
436 *dp = MAX_FLOAT;
437 return 1;
438 }
439 else if (d < -MAX_FLOAT)
440 {
441 *dp = -MAX_FLOAT;
442 return 1;
443 }
444 else if ((d > 0 && d < MIN_FLOAT) || (d < 0 && d > -MIN_FLOAT))
445 {
446 *dp = 0.0;
447 return 1;
448 }
449 }
450
451 return 0;
452 }
453 \f
454 /* Output the label at the start of a function.
455 Precede it with the number of formal args so debuggers will have
456 some idea of how many args to print. */
457
458 void
459 asm_declare_function_name (file, name, decl)
460 FILE *file;
461 char *name;
462 tree decl;
463 {
464 tree parms;
465 int nargs = list_length (DECL_ARGUMENTS (decl));
466
467 char *p, c;
468 extern char *version_string;
469 static char vers[4];
470 int i;
471
472 p = version_string;
473 for (i = 0; i < 3; ) {
474 c = *p;
475 if (c - '0' < (unsigned) 10)
476 vers[i++] = c;
477 if (c == 0 || c == ' ')
478 vers[i++] = '0';
479 else
480 p++;
481 }
482 fprintf (file, "\tds.b \"g%s\"\n", vers);
483
484 if (nargs < 100)
485 fprintf (file, "\tds.b \"+%02d\\0\"\n", nargs);
486 else
487 fprintf (file, "\tds.b \"+00\\0\"\n");
488
489 ASM_OUTPUT_LABEL (file, name);
490 }
491 \f
492 /* Print an instruction operand X on file FILE.
493 CODE is the code from the %-spec that requested printing this operand;
494 if `%z3' was used to print operand 3, then CODE is 'z'. */
495 /* Convex codes:
496 %u prints a CONST_DOUBLE's high word
497 %v prints a CONST_DOUBLE's low word
498 %z prints a CONST_INT shift count as a multiply operand -- viz. 1 << n.
499 */
500
501 print_operand (file, x, code)
502 FILE *file;
503 rtx x;
504 char code;
505 {
506 long u[2];
507 REAL_VALUE_TYPE d;
508
509 switch (GET_CODE (x))
510 {
511 case REG:
512 fprintf (file, "%s", reg_names[REGNO (x)]);
513 break;
514
515 case MEM:
516 output_address (XEXP (x, 0));
517 break;
518
519 case CONST_DOUBLE:
520 REAL_VALUE_FROM_CONST_DOUBLE (d, x);
521 switch (GET_MODE (x)) {
522 case DFmode:
523 #if 0 /* doesn't work, produces dfloats */
524 REAL_VALUE_TO_TARGET_DOUBLE (d, u);
525 #else
526 {
527 union { double d; int i[2]; } t;
528 t.d = d;
529 u[0] = t.i[0];
530 u[1] = t.i[1];
531 }
532 #endif
533 if (code == 'u')
534 fprintf (file, "#%#x", u[0]);
535 else if (code == 'v')
536 fprintf (file, "#%#x", u[1]);
537 else
538 outfloat (file, d, "%.17e", "#", "");
539 break;
540 case SFmode:
541 outfloat (file, d, "%.9e", "#", "");
542 break;
543 default:
544 if (code == 'u')
545 fprintf (file, "#%d", CONST_DOUBLE_HIGH (x));
546 else
547 fprintf (file, "#%d", CONST_DOUBLE_LOW (x));
548 }
549 break;
550
551 default:
552 if (code == 'z')
553 {
554 if (GET_CODE (x) != CONST_INT)
555 abort ();
556 fprintf (file, "#%d", 1 << INTVAL (x));
557 }
558 else
559 {
560 putc ('#', file);
561 output_addr_const (file, x);
562 }
563 }
564 }
565
566 /* Print a memory operand whose address is X, on file FILE. */
567
568 print_operand_address (file, addr)
569 FILE *file;
570 rtx addr;
571 {
572 rtx index = 0;
573 rtx offset = 0;
574
575 if (GET_CODE (addr) == MEM)
576 {
577 fprintf (file, "@");
578 addr = XEXP (addr, 0);
579 }
580
581 switch (GET_CODE (addr))
582 {
583 case REG:
584 index = addr;
585 break;
586
587 case PLUS:
588 index = XEXP (addr, 0);
589 if (REG_P (index))
590 offset = XEXP (addr, 1);
591 else
592 {
593 offset = XEXP (addr, 0);
594 index = XEXP (addr, 1);
595 if (! REG_P (index))
596 abort ();
597 }
598 break;
599
600 default:
601 offset = addr;
602 break;
603 }
604
605 if (offset)
606 output_addr_const (file, offset);
607
608 if (index)
609 fprintf (file, "(%s)", reg_names[REGNO (index)]);
610 }
611
612 /* Output a float to FILE, value VALUE, format FMT, preceded by PFX
613 and followed by SFX. */
614
615 outfloat (file, value, fmt, pfx, sfx)
616 FILE *file;
617 REAL_VALUE_TYPE value;
618 char *fmt, *pfx, *sfx;
619 {
620 char buf[64];
621 fputs (pfx, file);
622 REAL_VALUE_TO_DECIMAL (value, fmt, buf);
623 fputs (buf, file);
624 fputs (sfx, file);
625 }
626 \f
627 /* Here during RTL generation of return. If we are at the final return
628 in a function, go through the function and replace pushes with stores
629 into a frame arg block. This is similar to what ACCUMULATE_OUTGOING_ARGS
630 does, but we must index off the frame pointer, not the stack pointer,
631 and the calling sequence does not require the arg block to be at the
632 top of the stack. */
633
634 replace_arg_pushes ()
635 {
636 /* Doesn't work yet. */
637 }
638
639 /* Output the insns needed to do a call. operands[] are
640 0 - MEM, the place to call
641 1 - CONST_INT, the number of bytes in the arg list
642 2 - CONST_INT, the number of arguments
643 3 - CONST_INT, the number of bytes to pop
644 4 - address of the arg list.
645 */
646
647 char *
648 output_call (insn, operands)
649 rtx insn, *operands;
650 {
651 if (operands[4] == stack_pointer_rtx)
652 output_asm_insn ("mov sp,ap", operands);
653 else
654 abort ();
655
656 if (TARGET_ARGCOUNT)
657 output_asm_insn ("pshea %a2", operands);
658
659 output_asm_insn ("calls %0", operands);
660
661 output_asm_insn ("ld.w 12(fp),ap", operands);
662
663 if (operands[4] == stack_pointer_rtx && operands[3] != const0_rtx)
664 output_asm_insn ("add.w %3,sp", operands);
665
666 return "";
667 }
668
669
670 /* Here after reloading, before the second scheduling pass. */
671
672 emit_ap_optimizations ()
673 {
674 /* Removed for now. */
675 }
676