OSDN Git Service

6fec44418f48c439f37eb5a24f4ea08ca0b87a6d
[pf3gnuchains/gcc-fork.git] / gcc / config / tahoe / tahoe.c
1 /* Subroutines for insn-output.c for Tahoe.
2    Copyright (C) 1989, 1991 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 2, 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
22 #include "config.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 "output.h"
31 #include "insn-attr.h"
32
33 /*
34  * File: output-tahoe.c
35  *
36  * Original port made at the University of Buffalo by Devon Bowen,
37  * Dale Wiles and Kevin Zachmann.
38  *
39  * Changes for HCX by Piet van Oostrum,
40  * University of Utrecht, The Netherlands (piet@cs.ruu.nl)
41  *
42  * Speed tweaks by Michael Tiemann (tiemann@lurch.stanford.edu).
43  *
44  * Mail bugs reports or fixes to:       gcc@cs.buffalo.edu
45  */
46
47
48 /* On tahoe, you have to go to memory to convert a register
49    from sub-word to word.  */
50
51 rtx tahoe_reg_conversion_loc;
52
53 int
54 extendable_operand (op, mode)
55      rtx op;
56      enum machine_mode mode;
57 {
58   if ((GET_CODE (op) == REG
59        || (GET_CODE (op) == SUBREG
60            && GET_CODE (SUBREG_REG (op)) == REG))
61       && tahoe_reg_conversion_loc == 0)
62     tahoe_reg_conversion_loc = assign_stack_local (SImode, GET_MODE_SIZE (SImode));
63   return general_operand (op, mode);
64 }
65
66 /* most of the print_operand_address function was taken from the vax    */
67 /* since the modes are basically the same. I had to add a special case, */
68 /* though, for symbol references with offsets.                          */
69
70 #include <stdio.h>
71
72 print_operand_address (file, addr)
73      FILE *file;
74      register rtx addr;
75 {
76   register rtx reg1, reg2, breg, ireg;
77   rtx offset;
78   static char *reg_name[] = REGISTER_NAMES;
79
80  retry:
81   switch (GET_CODE (addr))
82     {
83     case MEM:
84       fprintf (file, "*");
85       addr = XEXP (addr, 0);
86       goto retry;
87
88     case REG:
89       fprintf (file, "(%s)", reg_name [REGNO (addr)]);
90       break;
91
92     case PRE_DEC:
93       fprintf (file, "-(%s)", reg_name [REGNO (XEXP (addr, 0))]);
94       break;
95
96     case POST_INC:
97       fprintf (file, "(%s)+", reg_name [REGNO (XEXP (addr, 0))]);
98       break;
99
100     case PLUS:
101       reg1 = 0; reg2 = 0;
102       ireg = 0; breg = 0;
103       offset = 0;
104
105       if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
106           && GET_CODE (XEXP (addr, 1)) == CONST_INT)
107         output_addr_const (file, addr);
108
109       if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
110           && GET_CODE (XEXP (addr, 0)) == CONST_INT)
111         output_addr_const (file, addr);
112
113       if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
114           || GET_CODE (XEXP (addr, 0)) == MEM)
115         {
116           offset = XEXP (addr, 0);
117           addr = XEXP (addr, 1);
118         }
119       else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
120                || GET_CODE (XEXP (addr, 1)) == MEM)
121         {
122           offset = XEXP (addr, 1);
123           addr = XEXP (addr, 0);
124         }
125       if (GET_CODE (addr) != PLUS)
126         ;
127       else if (GET_CODE (XEXP (addr, 0)) == MULT)
128         {
129           reg1 = XEXP (addr, 0);
130           addr = XEXP (addr, 1);
131         }
132       else if (GET_CODE (XEXP (addr, 1)) == MULT)
133         {
134           reg1 = XEXP (addr, 1);
135           addr = XEXP (addr, 0);
136         }
137       else if (GET_CODE (XEXP (addr, 0)) == REG)
138         {
139           reg1 = XEXP (addr, 0);
140           addr = XEXP (addr, 1);
141         }
142       else if (GET_CODE (XEXP (addr, 1)) == REG)
143         {
144           reg1 = XEXP (addr, 1);
145           addr = XEXP (addr, 0);
146         }
147       if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
148         {
149           if (reg1 == 0)
150             reg1 = addr;
151           else
152             reg2 = addr;
153           addr = 0;
154         }
155       if (offset != 0)
156         {
157           if (addr != 0) abort ();
158           addr = offset;
159         }
160       if (reg1 != 0 && GET_CODE (reg1) == MULT)
161         {
162           breg = reg2;
163           ireg = reg1;
164         }
165       else if (reg2 != 0 && GET_CODE (reg2) == MULT)
166         {
167           breg = reg1;
168           ireg = reg2;
169         }
170       else if (reg2 != 0 || GET_CODE (addr) == MEM)
171         {
172           breg = reg2;
173           ireg = reg1;
174         }
175       else
176         {
177           breg = reg1;
178           ireg = reg2;
179         }
180       if (addr != 0)
181         output_address (offset);
182       if (breg != 0)
183         {
184           if (GET_CODE (breg) != REG)
185             abort ();
186           fprintf (file, "(%s)", reg_name[REGNO (breg)]);
187         }
188       if (ireg != 0)
189         {
190           if (GET_CODE (ireg) == MULT)
191             ireg = XEXP (ireg, 0);
192           if (GET_CODE (ireg) != REG)
193             abort ();
194           fprintf (file, "[%s]", reg_name[REGNO (ireg)]);
195         }
196       break;
197
198     default:
199       output_addr_const (file, addr);
200     }
201 }
202
203 /* Do a quick check and find out what the best way to do the */
204 /* mini-move is. Could be a push or a move.....              */
205
206 static char *
207 singlemove_string (operands)
208      rtx *operands;
209 {
210   if (operands[1] == const0_rtx)
211       return "clrl %0";
212   if (push_operand (operands[0], SImode))
213     return "pushl %1";
214   return "movl %1,%0";
215 }
216
217 /* given the rtx for an address, return true if the given */
218 /* register number is used in the address somewhere.      */
219
220 regisused(addr,regnum)
221 rtx addr;
222 int regnum;
223 {
224         if (GET_CODE(addr) == REG)
225                 if (REGNO(addr) == regnum)
226                         return (1);
227                 else
228                         return (0);
229
230         if (GET_CODE(addr) == MEM)
231                 return regisused(XEXP(addr,0),regnum);
232
233         if ((GET_CODE(addr) == MULT) || (GET_CODE(addr) == PLUS))
234                 return ((regisused(XEXP(addr,0),regnum)) ||
235                                         (regisused(XEXP(addr,1),regnum)));
236
237         return 0;
238 }
239
240
241 /* Given some rtx, traverse it and return the register used in a */
242 /* index. If no index is found, return 0.                        */
243
244 rtx
245 index_reg(addr)
246 rtx addr;
247 {
248         rtx temp;
249
250         if (GET_CODE(addr) == MEM)
251                 return index_reg(XEXP(addr,0));
252
253         if (GET_CODE(addr) == MULT)
254                 if (GET_CODE(XEXP(addr,0)) == REG)
255                         return XEXP(addr,0);
256                 else
257                         return XEXP(addr,1);
258
259         if (GET_CODE(addr) == PLUS)
260                 if (temp = index_reg(XEXP(addr,0)))
261                         return temp;
262                 else
263                         return index_reg(XEXP(addr,1));
264
265         return 0;
266 }
267
268
269 /* simulate the move double by generating two movl's. You have */
270 /* to be careful about mixing modes here.                      */
271
272 char *
273 output_move_double (operands)
274      rtx *operands;
275 {
276   enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, INDOP, CNSTOP, RNDOP }
277     optype0, optype1;
278   rtx latehalf[2];
279   rtx shftreg0 = 0, shftreg1 = 0;
280   rtx temp0 = 0, temp1 = 0;
281   rtx addreg0 = 0, addreg1 = 0;
282   int dohighfirst = 0;
283
284   /* First classify both operands. */
285
286   if (REG_P (operands[0]))
287     optype0 = REGOP;
288   else if ((GET_CODE(operands[0])==MEM) && (shftreg0=index_reg(operands[0])))
289     optype0 = INDOP;
290   else if (offsettable_memref_p (operands[0]))
291     optype0 = OFFSOP;
292   else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) {
293     optype0 = PUSHOP;
294     dohighfirst++;
295   } else if (GET_CODE (operands[0]) == MEM)
296     optype0 = MEMOP;
297   else
298     optype0 = RNDOP;
299
300   if (REG_P (operands[1]))
301     optype1 = REGOP;
302   else if ((GET_CODE(operands[1])==MEM) && (shftreg1=index_reg(operands[1])))
303     optype1 = INDOP;
304   else if (offsettable_memref_p (operands[1]))
305     optype1 = OFFSOP;
306   else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
307     optype1 = POPOP; 
308   else if (GET_CODE (operands[1]) == MEM)
309     optype1 = MEMOP;
310   else if (CONSTANT_P (operands[1]))
311     optype1 = CNSTOP;
312   else
313     optype1 = RNDOP;
314
315   /* set up for the high byte move for operand zero */
316
317   switch (optype0) {
318
319         /* if it's a register, just use the next highest in the */
320         /* high address move.                                   */
321
322         case REGOP  : latehalf[0] = gen_rtx (REG,SImode,REGNO(operands[0])+1);
323                       break;
324
325         /* for an offsettable address, use the gcc function to  */
326         /* modify the operand to get an offset of 4 higher for  */
327         /* the second move.                                     */
328
329         case OFFSOP : latehalf[0] = adj_offsettable_operand (operands[0], 4);
330                       break;
331
332         /* if the operand is MEMOP type, it must be a pointer   */
333         /* to a pointer. So just remember to increase the mem   */
334         /* location and use the same operand.                   */
335
336         case MEMOP  : latehalf[0] = operands[0];
337                       addreg0 = XEXP(operands[0],0);
338                       break;
339
340         /* if we're dealing with a push instruction, just leave */
341         /* the operand alone since it auto-increments.          */
342
343         case PUSHOP : latehalf[0] = operands[0];
344                       break;
345
346         /* YUCK! Indexed addressing!! If the address is considered   */
347         /* offsettable, go use the offset in the high part. Otherwise */
348         /* find what exactly is being added to the multiplication. If */
349         /* it's a mem reference, increment that with the high part   */
350         /* being unchanged to cause the shift. If it's a reg, do the */
351         /* same. If you can't identify it, abort. Remember that the  */
352         /* shift register was already set during identification.     */
353
354         case INDOP  : if (offsettable_memref_p(operands[0])) {
355                            latehalf[0] = adj_offsettable_operand(operands[0],4);
356                            break;
357                       }
358
359                       latehalf[0] = operands[0];
360
361                       temp0 = XEXP(XEXP(operands[0],0),0);
362                       if (GET_CODE(temp0) == MULT) {
363                            temp1 = temp0;
364                            temp0 = XEXP(XEXP(operands[0],0),1);
365                       } else {
366                            temp1 = XEXP(XEXP(operands[0],0),1);
367                            if (GET_CODE(temp1) != MULT)
368                                 abort();
369                       }
370
371                       if (GET_CODE(temp0) == MEM)
372                            addreg0 = temp0;
373                       else if (GET_CODE(temp0) == REG)
374                            addreg0 = temp0;
375                       else
376                            abort();
377
378                       break;
379
380         /* if we don't know the operand type, print a friendly  */
381         /* little error message...   8-)                        */
382
383         case RNDOP  :
384         default     : abort();
385   }
386
387   /* do the same setup for operand one */
388
389   switch (optype1) {
390
391         case REGOP  : latehalf[1] = gen_rtx(REG,SImode,REGNO(operands[1])+1);
392                       break;
393
394         case OFFSOP : latehalf[1] = adj_offsettable_operand (operands[1], 4);
395                       break;
396
397         case MEMOP  : latehalf[1] = operands[1];
398                       addreg1 = XEXP(operands[1],0);
399                       break;
400
401         case POPOP  : latehalf[1] = operands[1];
402                       break;
403
404         case INDOP  : if (offsettable_memref_p(operands[1])) {
405                            latehalf[1] = adj_offsettable_operand(operands[1],4);
406                            break;
407                       }
408
409                       latehalf[1] = operands[1];
410
411                       temp0 = XEXP(XEXP(operands[1],0),0);
412                       if (GET_CODE(temp0) == MULT) {
413                            temp1 = temp0;
414                            temp0 = XEXP(XEXP(operands[1],0),1);
415                       } else {
416                            temp1 = XEXP(XEXP(operands[1],0),1);
417                            if (GET_CODE(temp1) != MULT)
418                                 abort();
419                       }
420
421                       if (GET_CODE(temp0) == MEM)
422                            addreg1 = temp0;
423                       else if (GET_CODE(temp0) == REG)
424                            addreg1 = temp0;
425                       else
426                            abort();
427
428                       break;
429
430         case CNSTOP :
431           if (GET_CODE (operands[1]) == CONST_DOUBLE)
432             split_double (operands[1], &operands[1], &latehalf[1]);
433           else if (CONSTANT_P (operands[1]))
434             latehalf[1] = const0_rtx;
435           else abort ();
436           break;
437
438         case RNDOP  :
439         default     : abort();
440   }
441
442
443   /* double the register used for shifting in both of the operands */
444   /* but make sure the same register isn't doubled twice!          */
445
446   if (shftreg0 && shftreg1 && (rtx_equal_p(shftreg0,shftreg1)))
447         output_asm_insn("addl2 %0,%0", &shftreg0);
448   else {
449         if (shftreg0)
450                 output_asm_insn("addl2 %0,%0", &shftreg0);
451         if (shftreg1)
452                 output_asm_insn("addl2 %0,%0", &shftreg1);
453   }
454
455   /* if the destination is a register and that register is needed in  */
456   /* the source addressing mode, swap the order of the moves since we */
457   /* don't want this destroyed til last. If both regs are used, not   */
458   /* much we can do, so abort. If these becomes a problem, maybe we   */
459   /* can do it on the stack?                                          */
460
461   if (GET_CODE(operands[0])==REG && regisused(operands[1],REGNO(operands[0])))
462         if (regisused(latehalf[1],REGNO(latehalf[0])))
463                 8;
464         else
465                 dohighfirst++;
466
467   /* if we're pushing, do the high address part first. */
468
469   if (dohighfirst) {
470
471         if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
472                 output_asm_insn("addl2 $4,%0", &addreg0);
473         else {
474                 if (addreg0)
475                         output_asm_insn("addl2 $4,%0", &addreg0);
476                 if (addreg1)
477                         output_asm_insn("addl2 $4,%0", &addreg1);
478         }
479
480         output_asm_insn(singlemove_string(latehalf), latehalf);
481
482         if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
483                 output_asm_insn("subl2 $4,%0", &addreg0);
484         else {
485                 if (addreg0)
486                         output_asm_insn("subl2 $4,%0", &addreg0);
487                 if (addreg1)
488                         output_asm_insn("subl2 $4,%0", &addreg1);
489         }
490
491         return singlemove_string(operands);
492   }
493
494   output_asm_insn(singlemove_string(operands), operands);
495
496   if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
497         output_asm_insn("addl2 $4,%0", &addreg0);
498   else {
499         if (addreg0)
500                 output_asm_insn("addl2 $4,%0", &addreg0);
501         if (addreg1)
502                 output_asm_insn("addl2 $4,%0", &addreg1);
503   }
504
505   output_asm_insn(singlemove_string(latehalf), latehalf);
506
507   if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
508         output_asm_insn("subl2 $4,%0", &addreg0);
509   else {
510         if (addreg0)
511                 output_asm_insn("subl2 $4,%0", &addreg0);
512         if (addreg1)
513                 output_asm_insn("subl2 $4,%0", &addreg1);
514   }
515
516   if (shftreg0 && shftreg1 && (rtx_equal_p(shftreg0,shftreg1)))
517         output_asm_insn("shar $1,%0,%0", &shftreg0);
518   else {
519         if (shftreg0)
520                 output_asm_insn("shar $1,%0,%0", &shftreg0);
521         if (shftreg1)
522                 output_asm_insn("shar $1,%0,%0", &shftreg1);
523   }
524
525   return "";
526 }
527
528
529 /* This checks if a zero_extended cmp[bw] can be replaced by a sign_extended
530    cmp[bw]. This can be done if the operand is a constant that fits in a
531    byte/word or a memory operand. Besides that the next instruction must be an
532    unsigned compare. Some of these tests are done by the machine description */
533
534 int
535 tahoe_cmp_check (insn, op, max)
536 rtx insn, op; int max;
537 {
538     if (GET_CODE (op) == CONST_INT
539         && ( INTVAL (op) < 0 || INTVAL (op) > max ))
540         return 0;
541     {
542         register rtx next = NEXT_INSN (insn);
543
544         if ((GET_CODE (next) == JUMP_INSN
545            || GET_CODE (next) == INSN
546            || GET_CODE (next) == CALL_INSN))
547             {
548                 next = PATTERN (next);
549                 if (GET_CODE (next) == SET
550                     && SET_DEST (next) == pc_rtx
551                     && GET_CODE (SET_SRC (next)) == IF_THEN_ELSE)
552                     switch (GET_CODE (XEXP (SET_SRC (next), 0)))
553                         {
554                         case EQ:
555                         case NE:
556                         case LTU:
557                         case GTU:
558                         case LEU:
559                         case GEU:
560                             return 1;
561                         }
562             }
563     }
564     return 0;
565 }