|
|
1.1 root 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
1.1.1.2 ! root 18: the Free Software Foundation, 59 Temple Place - Suite 330,
! 19: Boston, MA 02111-1307, USA. */
1.1 root 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 ([email protected])
41: *
42: * Speed tweaks by Michael Tiemann ([email protected]).
43: *
44: * Mail bugs reports or fixes to: [email protected]
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.