|
|
1.1 root 1: /* Subroutines for assembler code output on the NS32000.
2: Copyright (C) 1988 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 1, 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: /* Some output-actions in ns32k.md need these. */
21: #include <stdio.h>
22: extern FILE *asm_out_file;
23:
24: #define FP_REG_P(X) (GET_CODE (X) == REG && REGNO (X) > 7 && REGNO (X) < 16)
25:
26: /* Generate the rtx that comes from an address expression in the md file */
27: /* The expression to be build is BASE[INDEX:SCALE]. To recognize this,
28: scale must be converted from an exponent (from ASHIFT) to a
29: muliplier (for MULT). */
30: rtx
31: gen_indexed_expr (base, index, scale)
32: rtx base, index, scale;
33: {
34: rtx addr;
35:
36: /* This generates an illegal addressing mode, if BASE is
37: fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */
38: if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT)
39: base = gen_rtx (MEM, SImode, base);
40: addr = gen_rtx (MULT, SImode, index,
41: gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (scale)));
42: addr = gen_rtx (PLUS, SImode, base, addr);
43: return addr;
44: }
45:
46: /* Return 1 if OP is a valid constant int. These can be modeless
47: (void mode), so we do not mess with their modes.
48:
49: The main use of this function is as a predicate in match_operand
50: expressions in the machine description. */
51:
52: int
53: const_int (op, mode)
54: register rtx op;
55: enum machine_mode mode;
56: {
57: return (GET_CODE (op) == CONST_INT);
58: }
59:
60: /* Return 1 if OP is a valid operand of mode MODE. This
61: predicate rejects operands which do not have a mode
62: (such as CONST_INT which are VOIDmode). */
63: int
64: reg_or_mem_operand (op, mode)
65: register rtx op;
66: enum machine_mode mode;
67: {
68: return (GET_MODE (op) == mode
69: && (GET_CODE (op) == REG
70: || GET_CODE (op) == SUBREG
71: || GET_CODE (op) == MEM));
72: }
73:
74: /* Return the best assembler insn template
75: for moving operands[1] into operands[0] as a fullword. */
76:
77: static char *
78: singlemove_string (operands)
79: rtx *operands;
80: {
81: if (GET_CODE (operands[1]) == CONST_INT
82: && INTVAL (operands[1]) <= 7
83: && INTVAL (operands[1]) >= -8)
84: return "movqd %1,%0";
85: return "movd %1,%0";
86: }
87:
88: char *
89: output_move_double (operands)
90: rtx *operands;
91: {
92: enum anon1 { REGOP, OFFSOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
93: rtx latehalf[2];
94:
95: /* First classify both operands. */
96:
97: if (REG_P (operands[0]))
98: optype0 = REGOP;
99: else if (offsettable_memref_p (operands[0]))
100: optype0 = OFFSOP;
101: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
102: optype0 = POPOP;
103: else
104: optype0 = RNDOP;
105:
106: if (REG_P (operands[1]))
107: optype1 = REGOP;
108: else if (CONSTANT_ADDRESS_P (operands[1])
109: || GET_CODE (operands[1]) == CONST_DOUBLE)
110: optype1 = CNSTOP;
111: else if (offsettable_memref_p (operands[1]))
112: optype1 = OFFSOP;
113: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
114: optype1 = POPOP;
115: else
116: optype1 = RNDOP;
117:
118: /* Check for the cases that the operand constraints are not
119: supposed to allow to happen. Abort if we get one,
120: because generating code for these cases is painful. */
121:
122: if (optype0 == RNDOP || optype1 == RNDOP)
123: abort ();
124:
125: /* Ok, we can do one word at a time.
126: Normally we do the low-numbered word first,
127: but if either operand is autodecrementing then we
128: do the high-numbered word first.
129:
130: In either case, set up in LATEHALF the operands to use
131: for the high-numbered word and in some cases alter the
132: operands in OPERANDS to be suitable for the low-numbered word. */
133:
134: if (optype0 == REGOP)
135: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
136: else if (optype0 == OFFSOP)
137: latehalf[0] = adj_offsettable_operand (operands[0], 4);
138: else
139: latehalf[0] = operands[0];
140:
141: if (optype1 == REGOP)
142: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
143: else if (optype1 == OFFSOP)
144: latehalf[1] = adj_offsettable_operand (operands[1], 4);
145: else if (optype1 == CNSTOP)
146: {
147: if (CONSTANT_ADDRESS_P (operands[1]))
148: latehalf[1] = const0_rtx;
149: else if (GET_CODE (operands[1]) == CONST_DOUBLE)
150: {
151: latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
152: CONST_DOUBLE_HIGH (operands[1]));
153: operands[1] = gen_rtx (CONST_INT, VOIDmode,
154: CONST_DOUBLE_LOW (operands[1]));
155: }
156: }
157: else
158: latehalf[1] = operands[1];
159:
160: /* If one or both operands autodecrementing,
161: do the two words, high-numbered first. */
162:
163: if (optype0 == POPOP || optype1 == POPOP)
164: {
165: output_asm_insn (singlemove_string (latehalf), latehalf);
166: return singlemove_string (operands);
167: }
168:
169: /* Not autodecrementing. Do the two words, low-numbered first. */
170:
171: output_asm_insn (singlemove_string (operands), operands);
172:
173: operands[0] = latehalf[0];
174: operands[1] = latehalf[1];
175: return singlemove_string (operands);
176: }
177:
178: int
179: check_reg (oper, reg)
180: rtx oper;
181: int reg;
182: {
183: register int i;
184:
185: if (oper == 0)
186: return 0;
187: switch (GET_CODE(oper))
188: {
189: case REG:
190: return (REGNO(oper) == reg) ? 1 : 0;
191: case MEM:
192: return check_reg(XEXP(oper, 0), reg);
193: case PLUS:
194: case MULT:
195: return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg);
196: }
197: return 0;
198: }
199:
200: /* PRINT_OPERAND_ADDRESS is defined to call this function,
201: which is easier to debug than putting all the code in
202: a macro definition in tm-ns32k.h . */
203:
204: /* Nonzero if we have printed a base register.
205: If zero, on some systems, it means `(sb)' must be printed. */
206: int paren_base_reg_printed = 0;
207:
208: print_operand_address (file, addr)
209: register FILE *file;
210: register rtx addr;
211: {
212: register rtx reg1, reg2, breg, ireg;
213: rtx offset;
214: static char scales[] = { 'b', 'w', 'd', 0, 'q', };
215:
216: retry:
217: switch (GET_CODE (addr))
218: {
219: case MEM:
220: addr = XEXP (addr, 0);
221: if (GET_CODE (addr) == REG)
222: if (REGNO (addr) == STACK_POINTER_REGNUM)
223: { fprintf (file, "tos"); break; }
224: else
225: { fprintf (file, "%s", reg_names[REGNO (addr)]); break; }
226: else if (CONSTANT_P (addr))
227: { output_addr_const (file, addr); break; }
228: else if (GET_CODE (addr) == MULT)
229: { fprintf (file, "@0"); ireg = addr; goto print_index; }
230: else if (GET_CODE (addr) == MEM)
231: {
232: addr = XEXP (addr, 0);
233: if (GET_CODE (addr) == PLUS)
234: {
235: offset = XEXP (addr, 1);
236: addr = XEXP (addr, 0);
237: }
238: else
239: {
240: offset = const0_rtx;
241: }
242: output_addr_const (file, offset);
243: fprintf (file, "(%s)", reg_names[REGNO (addr)]);
244: break;
245: }
246:
247: if (GET_CODE (addr) != PLUS)
248: abort ();
249:
250: goto retry;
251:
252: case REG:
253: if (REGNO (addr) == STACK_POINTER_REGNUM)
254: fprintf (file, "tos");
255: else
256: fprintf (file, "0(%s)", reg_names[REGNO (addr)]);
257: break;
258:
259: case PRE_DEC:
260: case POST_INC:
261: fprintf (file, "tos");
262: break;
263:
264: case MULT:
265: fprintf (file, "@0");
266: ireg = addr; /* [rX:Y] */
267: goto print_index;
268: break;
269:
270: case PLUS:
271: reg1 = 0; reg2 = 0;
272: ireg = 0; breg = 0;
273: offset = const0_rtx;
274: if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
275: {
276: offset = XEXP (addr, 0);
277: addr = XEXP (addr, 1);
278: }
279: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
280: {
281: offset = XEXP (addr, 1);
282: addr = XEXP (addr, 0);
283: }
284: if (GET_CODE (addr) != PLUS) ;
285: else if (GET_CODE (XEXP (addr, 0)) == MULT)
286: {
287: reg1 = XEXP (addr, 0);
288: addr = XEXP (addr, 1);
289: }
290: else if (GET_CODE (XEXP (addr, 1)) == MULT)
291: {
292: reg1 = XEXP (addr, 1);
293: addr = XEXP (addr, 0);
294: }
295: /* The case for memory is somewhat tricky: to get
296: a MEM here, the only RTX formats that could
297: get here are either (modulo commutativity)
298: (PLUS (PLUS (REG *MEM)) CONST) -or-
299: (PLUS (PLUS (CONST REG/MULT)) *MEM)
300: We take advantage of that knowledge here. */
301: else if (GET_CODE (XEXP (addr, 0)) == MEM
302: || GET_CODE (XEXP (addr, 1)) == MEM)
303: {
304: rtx temp;
305:
306: if (GET_CODE (XEXP (addr, 0)) == MEM)
307: {
308: temp = XEXP (addr, 1);
309: addr = XEXP (addr, 0);
310: }
311: else
312: {
313: temp = XEXP (addr, 0);
314: addr = XEXP (addr, 1);
315: }
316:
317: if (GET_CODE (temp) == REG)
318: {
319: reg1 = temp;
320: }
321: else
322: {
323: if (GET_CODE (temp) != PLUS)
324: abort ();
325:
326: if (GET_CODE (XEXP (temp, 0)) == MULT)
327: {
328: reg1 = XEXP (temp, 0);
329: offset = XEXP (temp, 1);
330: }
331: if (GET_CODE (XEXP (temp, 1)) == MULT)
332: {
333: reg1 = XEXP (temp, 1);
334: offset = XEXP (temp, 0);
335: }
336: else
337: abort ();
338: }
339: }
340: else if (GET_CODE (XEXP (addr, 0)) == REG
341: || GET_CODE (XEXP (addr, 1)) == REG)
342: {
343: rtx temp;
344:
345: if (GET_CODE (XEXP (addr, 0)) == REG)
346: {
347: temp = XEXP (addr, 0);
348: addr = XEXP (addr, 1);
349: }
350: else
351: {
352: temp = XEXP (addr, 1);
353: addr = XEXP (addr, 0);
354: }
355:
356: if (GET_CODE (addr) == REG)
357: {
358: if (REGNO (temp) >= FRAME_POINTER_REGNUM)
359: { reg1 = addr; addr = temp; }
360: else
361: { reg1 = temp; }
362: }
363: else if (CONSTANT_P (addr))
364: {
365: if (GET_CODE (offset) == CONST_INT
366: && INTVAL (offset))
367: offset = plus_constant (addr, INTVAL (offset));
368: addr = temp;
369: }
370: else if (GET_CODE (addr) != PLUS)
371: abort ();
372: else
373: {
374: if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
375: {
376: offset = XEXP (addr, 0);
377: addr = XEXP (addr, 1);
378: }
379: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
380: {
381: offset = XEXP (addr, 1);
382: addr = XEXP (addr, 0);
383: }
384: else abort ();
385:
386: if (GET_CODE (addr) == REG)
387: {
388: if (REGNO (temp) >= FRAME_POINTER_REGNUM)
389: { reg1 = addr; addr = temp; }
390: else
391: { reg1 = temp; }
392: }
393: else
394: reg1 = temp;
395: }
396: }
397:
398: if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
399: { if (reg1 == 0) reg1 = addr; else reg2 = addr; addr = 0; }
400: if (addr != 0)
401: {
402: if (CONSTANT_P (addr) && reg1)
403: {
404: /* OFFSET comes second, to prevent outputting
405: operands of the form INT+SYMBOL+INT.
406: The Genix assembler dies on them. */
407: output_addr_const (file, addr);
408: if (offset != const0_rtx)
409: {
410: putc ('+', file);
411: output_addr_const (file, offset);
412: }
413: ireg = reg1;
414: goto print_index;
415: }
416: else if (GET_CODE (addr) != MEM)
417: abort ();
418:
419: output_addr_const (file, offset);
420: #ifndef SEQUENT_ADDRESS_BUG
421: putc ('(', file);
422: paren_base_reg_printed = 0;
423: output_address (addr);
424: #ifdef SEQUENT_BASE_REGS
425: if (!paren_base_reg_printed)
426: fprintf (file, "(sb)");
427: #endif
428: putc (')', file);
429: #else /* SEQUENT_ADDRESS_BUG */
430: if ((GET_CODE (offset) == SYMBOL_REF
431: || GET_CODE (offset) == CONST)
432: && GET_CODE (addr) == REG)
433: {
434: if (reg1) abort ();
435: fprintf (file, "[%s:b]", reg_names[REGNO (addr)]);
436: }
437: else
438: {
439: putc ('(', file);
440: paren_base_reg_printed = 0;
441: output_address (addr);
442: #ifdef SEQUENT_BASE_REGS
443: if (!paren_base_reg_printed)
444: fprintf (file, "(sb)");
445: #endif
446: putc (')', file);
447: }
448: #endif /* SEQUENT_ADDRESS_BUG */
449: ireg = reg1;
450: goto print_index;
451: }
452: else addr = offset;
453: if (reg1 && GET_CODE (reg1) == MULT)
454: { breg = reg2; ireg = reg1; }
455: else if (reg2 && GET_CODE (reg2) == MULT)
456: { breg = reg1; ireg = reg2; }
457: else if (reg2 || GET_CODE (addr) == MEM)
458: { breg = reg2; ireg = reg1; }
459: else
460: { breg = reg1; ireg = reg2; }
461: if (ireg != 0 && breg == 0 && GET_CODE (addr) == LABEL_REF)
462: {
463: int scale;
464: if (GET_CODE (ireg) == MULT)
465: {
466: scale = INTVAL (XEXP (ireg, 1)) >> 1;
467: ireg = XEXP (ireg, 0);
468: }
469: else scale = 0;
470: output_asm_label (addr);
471: fprintf (file, "[%s:%c]",
472: reg_names[REGNO (ireg)], scales[scale]);
473: break;
474: }
475: if (ireg && breg && offset == const0_rtx)
476: fprintf (file, "0(%s)", reg_names[REGNO (breg)]);
477: else
478: {
479: if (addr != 0)
480: {
481: if (ireg != 0 && breg == 0
482: && GET_CODE (offset) == CONST_INT) putc('@', file);
483: output_addr_const (file, offset);
484: }
485: if (breg != 0)
486: {
487: if (GET_CODE (breg) != REG) abort ();
488: #ifndef SEQUENT_ADDRESS_BUG
489: fprintf (file, "(%s)", reg_names[REGNO (breg)]);
490: paren_base_reg_printed = -1;
491: #else
492: if (GET_CODE (offset) == SYMBOL_REF || GET_CODE (offset) == CONST)
493: {
494: if (ireg) abort ();
495: fprintf (file, "[%s:b]", reg_names[REGNO (breg)]);
496: }
497: else
498: {
499: fprintf (file, "(%s)", reg_names[REGNO (breg)]);
500: paren_base_reg_printed = -1;
501: }
502: #endif
503: }
504: }
505: print_index:
506: if (ireg != 0)
507: {
508: int scale;
509: if (GET_CODE (ireg) == MULT)
510: {
511: scale = INTVAL (XEXP (ireg, 1)) >> 1;
512: ireg = XEXP (ireg, 0);
513: }
514: else scale = 0;
515: if (GET_CODE (ireg) != REG) abort ();
516: fprintf (file, "[%s:%c]",
517: reg_names[REGNO (ireg)],
518: scales[scale]);
519: }
520: break;
521: default:
522: output_addr_const (file, addr);
523: }
524: }
525:
526: /* National 32032 shifting is so bad that we can get
527: better performance in many common cases by using other
528: techniques. */
529: char *
530: output_shift_insn (operands)
531: rtx *operands;
532: {
533: if (GET_CODE (operands[2]) == CONST_INT
534: && INTVAL (operands[2]) > 0
535: && INTVAL (operands[2]) <= 3)
536: if (GET_CODE (operands[0]) == REG)
537: {
538: if (GET_CODE (operands[1]) == REG)
539: {
540: if (REGNO (operands[0]) == REGNO (operands[1]))
541: {
542: if (operands[2] == const1_rtx)
543: return "addd %0,%0";
544: else if (INTVAL (operands[2]) == 2)
545: return "addd %0,%0\n\taddd %0,%0";
546: }
547: if (operands[2] == const1_rtx)
548: return "movd %1,%0\n\taddd %0,%0";
549:
550: operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
551: return "addr %a1,%0";
552: }
553: if (operands[2] == const1_rtx)
554: return "movd %1,%0\n\taddd %0,%0";
555: }
556: else if (GET_CODE (operands[1]) == REG)
557: {
558: operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
559: return "addr %a1,%0";
560: }
561: else if (INTVAL (operands[2]) == 1
562: && GET_CODE (operands[1]) == MEM
563: && rtx_equal_p (operands [0], operands[1]))
564: {
565: rtx temp = XEXP (operands[1], 0);
566:
567: if (GET_CODE (temp) == REG
568: || (GET_CODE (temp) == PLUS
569: && GET_CODE (XEXP (temp, 0)) == REG
570: && GET_CODE (XEXP (temp, 1)) == CONST_INT))
571: return "addd %0,%0";
572: }
573: else return "ashd %2,%0";
574: return "ashd %2,%0";
575: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.