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