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