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