OSDN Git Service

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