]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/tahoe/tahoe.c
53f77d64be2ab88ce2c6d07e543b647a28631ab1
[thirdparty/gcc.git] / gcc / config / tahoe / tahoe.c
1 /* Subroutines for insn-output.c for Tahoe.
2 Copyright (C) 1989, 1991, 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 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 <stdio.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 "function.h"
32 #include "output.h"
33 #include "insn-attr.h"
34
35 /*
36 * File: output-tahoe.c
37 *
38 * Original port made at the University of Buffalo by Devon Bowen,
39 * Dale Wiles and Kevin Zachmann.
40 *
41 * Changes for HCX by Piet van Oostrum,
42 * University of Utrecht, The Netherlands (piet@cs.ruu.nl)
43 *
44 * Speed tweaks by Michael Tiemann (tiemann@lurch.stanford.edu).
45 *
46 * Mail bugs reports or fixes to: gcc@cs.buffalo.edu
47 */
48
49
50 /* On tahoe, you have to go to memory to convert a register
51 from sub-word to word. */
52
53 rtx tahoe_reg_conversion_loc;
54
55 int
56 extensible_operand (op, mode)
57 rtx op;
58 enum machine_mode mode;
59 {
60 if ((GET_CODE (op) == REG
61 || (GET_CODE (op) == SUBREG
62 && GET_CODE (SUBREG_REG (op)) == REG))
63 && tahoe_reg_conversion_loc == 0)
64 tahoe_reg_conversion_loc = assign_stack_local (SImode, GET_MODE_SIZE (SImode));
65 return general_operand (op, mode);
66 }
67
68 /* most of the print_operand_address function was taken from the vax */
69 /* since the modes are basically the same. I had to add a special case, */
70 /* though, for symbol references with offsets. */
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 }