|
|
1.1 root 1: /* Subroutines for insn-output.c for Motorola 88000.
2: Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
3: Contributed by Michael Tiemann ([email protected])
4: Enhanced by Michael Meissner ([email protected])
5: Currently supported by Tom Wood ([email protected])
6:
7: This file is part of GNU CC.
8:
9: GNU CC is free software; you can redistribute it and/or modify
10: it under the terms of the GNU General Public License as published by
11: the Free Software Foundation; either version 2, or (at your option)
12: any later version.
13:
14: GNU CC is distributed in the hope that it will be useful,
15: but WITHOUT ANY WARRANTY; without even the implied warranty of
16: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: GNU General Public License for more details.
18:
19: You should have received a copy of the GNU General Public License
20: along with GNU CC; see the file COPYING. If not, write to
21: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22:
23: #include <stdio.h>
24: #include <sys/types.h>
25: #include <time.h>
26: #include <ctype.h>
27:
28: #include "config.h"
29: #include "rtl.h"
30: #include "regs.h"
31: #include "hard-reg-set.h"
32: #include "real.h"
33: #include "insn-config.h"
34: #include "conditions.h"
35: #include "insn-flags.h"
36: #include "output.h"
37: #include "insn-attr.h"
38: #include "tree.h"
39: #include "c-tree.h"
40: #include "expr.h"
41: #include "hard-reg-set.h"
42: #include "flags.h"
43:
44: extern char *version_string;
45: extern time_t time ();
46: extern char *ctime ();
47: extern int flag_traditional;
48: extern FILE *asm_out_file;
49:
1.1.1.2 ! root 50: static char out_sccs_id[] = "@(#)m88k.c 2.0.3.4 19 Mar 1992 11:11:58";
1.1 root 51: static char tm_sccs_id [] = TM_SCCS_ID;
52:
53: char *m88k_pound_sign = ""; /* Either # for SVR4 or empty for SVR3 */
54: char *m88k_short_data;
55:
56: int m88k_gp_threshold;
57: int m88k_prologue_done = 0; /* Ln directives can now be emitted */
58: int m88k_function_number = 0; /* Counter unique to each function */
59: int m88k_fp_offset = 0; /* offset of frame pointer if used */
60: int m88k_stack_size = 0; /* size of allocated stack (including frame) */
61: int m88k_case_index;
62:
63: rtx m88k_compare_reg; /* cmp output pseudo register */
64: rtx m88k_compare_op0; /* cmpsi operand 0 */
65: rtx m88k_compare_op1; /* cmpsi operand 1 */
1.1.1.2 ! root 66:
! 67: enum attr_cpu m88k_cpu; /* target cpu */
1.1 root 68:
69: /* Determine what instructions are needed to manufacture the integer VALUE
70: in the given MODE. */
71:
72: enum m88k_instruction
73: classify_integer (mode, value)
74: enum machine_mode mode;
75: register int value;
76: {
77: register int mask;
78:
79: if (value == 0)
80: return m88k_zero;
81: else if (SMALL_INTVAL (value))
82: return m88k_or;
83: else if (SMALL_INTVAL (-value))
84: return m88k_subu;
85: else if (mode == HImode)
86: return m88k_or_lo16;
87: else if (mode == QImode)
88: return m88k_or_lo8;
89: else if ((value & 0xffff) == 0)
90: return m88k_oru_hi16;
91: else if (integer_ok_for_set (value))
92: return m88k_set;
93: else
94: return m88k_oru_or;
95: }
96:
97: int
98: integer_ok_for_set (value)
99: register unsigned value;
100: {
101: /* All the "one" bits must be contiguous. If so, MASK + 1 will be
102: a power of two or zero. */
103: register unsigned mask = (value | (value - 1));
104: return (value && POWER_OF_2_or_0 (mask + 1));
105: }
106:
107: char *
108: output_load_const_int (mode, operands)
109: enum machine_mode mode;
110: rtx *operands;
111: {
112: static char *patterns[] =
113: { "or %0,%#r0,0",
114: "or %0,%#r0,%1",
115: "subu %0,%#r0,%n1",
116: "or %0,%#r0,%h1",
117: "or %0,%#r0,%q1",
118: "set %0,%#r0,%s1",
119: "or.u %0,%#r0,%X1",
120: "or.u %0,%#r0,%X1\n\tor %0,%0,%x1",
121: };
122:
123: if (! REG_P (operands[0])
124: || GET_CODE (operands[1]) != CONST_INT)
125: abort ();
126: return patterns[classify_integer (mode, INTVAL (operands[1]))];
127: }
128:
129: /* These next two routines assume that floating point numbers are represented
130: in a manner which is consistent between host and target machines. */
131:
132: char *
133: output_load_const_float (operands)
134: rtx *operands;
135: {
136: /* These can return 0 under some circumstances when cross-compiling. */
137: operands[0] = operand_subword (operands[0], 0, 0, SFmode);
138: operands[1] = operand_subword (operands[1], 0, 0, SFmode);
139:
140: return output_load_const_int (SImode, operands);
141: }
142:
143: char *
144: output_load_const_double (operands)
145: rtx *operands;
146: {
147: rtx latehalf[2];
148:
149: /* These can return zero on some cross-compilers, but there's nothing
150: we can do about it. */
151: latehalf[0] = operand_subword (operands[0], 1, 0, DFmode);
152: latehalf[1] = operand_subword (operands[1], 1, 0, DFmode);
153:
154: operands[0] = operand_subword (operands[0], 0, 0, DFmode);
155: operands[1] = operand_subword (operands[1], 0, 0, DFmode);
156:
157: output_asm_insn (output_load_const_int (SImode, operands), operands);
158:
159: operands[0] = latehalf[0];
160: operands[1] = latehalf[1];
161:
162: return output_load_const_int (SImode, operands);
163: }
164:
165: char *
166: output_load_const_dimode (operands)
167: rtx *operands;
168: {
169: rtx latehalf[2];
170:
171: latehalf[0] = operand_subword (operands[0], 1, 0, DImode);
172: latehalf[1] = operand_subword (operands[1], 1, 0, DImode);
173:
174: operands[0] = operand_subword (operands[0], 0, 0, DImode);
175: operands[1] = operand_subword (operands[1], 0, 0, DImode);
176:
177: output_asm_insn (output_load_const_int (SImode, operands), operands);
178:
179: operands[0] = latehalf[0];
180: operands[1] = latehalf[1];
181:
182: return output_load_const_int (SImode, operands);
183: }
184:
185: /* Emit insns to move operands[1] into operands[0].
186:
187: Return 1 if we have written out everything that needs to be done to
188: do the move. Otherwise, return 0 and the caller will emit the move
189: normally. */
190:
191: int
192: emit_move_sequence (operands, mode)
193: rtx *operands;
194: enum machine_mode mode;
195: {
196: register rtx operand0 = operands[0];
197: register rtx operand1 = operands[1];
198:
199: /* Handle most common case first: storing into a register. */
200: if (register_operand (operand0, mode))
201: {
202: if (register_operand (operand1, mode)
203: || (GET_CODE (operand1) == CONST_INT && SMALL_INT (operand1))
204: || GET_CODE (operand1) == HIGH
205: /* Only `general_operands' can come here, so MEM is ok. */
206: || GET_CODE (operand1) == MEM)
207: {
208: /* Run this case quickly. */
209: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1));
210: return 1;
211: }
212: }
213: else if (GET_CODE (operand0) == MEM)
214: {
215: if (register_operand (operand1, mode) || operand1 == const0_rtx)
216: {
217: /* Run this case quickly. */
218: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1));
219: return 1;
220: }
1.1.1.2 ! root 221: if (! reload_in_progress && ! reload_completed)
1.1 root 222: {
223: operands[0] = validize_mem (operand0);
224: operands[1] = operand1 = force_reg (mode, operand1);
225: }
226: }
227:
228: /* Simplify the source if we need to. */
229: if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode))
230: {
231: if (GET_CODE (operand1) != CONST_INT
232: && GET_CODE (operand1) != CONST_DOUBLE)
233: {
1.1.1.2 ! root 234: rtx temp = ((reload_in_progress || reload_completed)
! 235: ? operand0 : gen_reg_rtx (Pmode));
1.1 root 236: operands[1] = legitimize_address (flag_pic
237: && symbolic_address_p (operand1),
238: operand1, temp);
239: if (mode != SImode)
240: operands[1] = gen_rtx (SUBREG, mode, operands[1], 0);
241: }
242: }
243:
244: /* Now have insn-emit do whatever it normally does. */
245: return 0;
246: }
247:
248: /* Return a legitimate reference for ORIG (either an address or a MEM) using
249: the register REG. If PIC and the address is already position-independent,
250: use ORIG. */
251:
252: struct rtx_def *
253: legitimize_address (pic, orig, reg)
254: int pic;
255: rtx orig;
256: rtx reg;
257: {
258: rtx addr = (GET_CODE (orig) == MEM ? XEXP (orig, 0) : orig);
259: rtx new = orig;
260: rtx temp;
261:
262: if (pic)
263: {
264: if (GET_CODE (addr) == SYMBOL_REF
265: || GET_CODE (addr) == LABEL_REF)
266: {
267: if (reg == 0) abort ();
268:
269: if (flag_pic == 2)
270: {
271: emit_insn (gen_rtx (SET, VOIDmode,
272: reg, gen_rtx (HIGH, SImode, addr)));
273: emit_insn (gen_rtx (SET, VOIDmode,
274: reg, gen_rtx (LO_SUM, SImode, reg, addr)));
275: addr = reg;
276: }
277: new = gen_rtx (MEM, Pmode,
278: gen_rtx (PLUS, SImode,
279: pic_offset_table_rtx, addr));
280: current_function_uses_pic_offset_table = 1;
281: RTX_UNCHANGING_P (new) = 1;
282: {
283: rtx insn = emit_move_insn (reg, new);
284: /* Put a REG_EQUAL note on this insn, so that it can be optimized
285: by loop. */
286: REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, orig,
287: REG_NOTES (insn));
288: }
289: new = reg;
290: }
291: else if (GET_CODE (addr) == CONST)
292: {
293: rtx base, offset;
294:
295: if (GET_CODE (XEXP (addr, 0)) == PLUS
296: && XEXP (XEXP (addr, 0), 0) == pic_offset_table_rtx)
297: return orig;
298:
299: if (reg == 0)
300: abort ();
301:
302: if (GET_CODE (XEXP (addr, 0)) != PLUS) abort ();
303:
304: base = legitimize_address (1, XEXP (XEXP (addr, 0), 0), reg);
305: addr = legitimize_address (1, XEXP (XEXP (addr, 0), 1),
306: base == reg ? 0 : reg);
307:
308: if (GET_CODE (addr) == CONST_INT)
309: new = plus_constant_for_output (base, INTVAL (addr));
310: else
311: new = gen_rtx (PLUS, SImode, base, addr);
312: /* Should we set special REG_NOTEs here? */
313: }
314: }
315: else if (! SHORT_ADDRESS_P (addr, temp))
316: {
317: emit_insn (gen_rtx (SET, VOIDmode,
318: reg, gen_rtx (HIGH, SImode, addr)));
319: new = gen_rtx (LO_SUM, SImode, reg, addr);
320: }
321:
322: if (new != orig
323: && GET_CODE (orig) == MEM)
324: {
325: new = gen_rtx (MEM, GET_MODE (orig), new);
326: RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (orig);
327: MEM_VOLATILE_P (new) = MEM_VOLATILE_P (orig);
328: MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (orig);
329: }
330: return new;
331: }
332:
333: /* Support functions for code to emit a block move. There are four methods
334: used to perform the block move:
335: + call memcpy
336: + call the looping library function, e.g. __movstrSI64n8
337: + call a non-looping library function, e.g. __movstrHI15x11
338: + produce an inline sequence of ld/st instructions
339:
340: The parameters below describe the library functions produced by
341: movstr-m88k.sh. */
342:
343: #define MOVSTR_LOOP 64 /* __movstrSI64n68 .. __movstrSI64n8 */
344: #define MOVSTR_QI 16 /* __movstrQI16x16 .. __movstrQI16x2 */
345: #define MOVSTR_HI 48 /* __movstrHI48x48 .. __movstrHI48x4 */
346: #define MOVSTR_SI 96 /* __movstrSI96x96 .. __movstrSI96x8 */
347: #define MOVSTR_ODD_SI 48 /* __movstrSI47x47 .. __movstrSI47x11,
348: __movstrSI46x46 .. __movstrSI46x10,
349: __movstrSI45x45 .. __movstrSI45x9 */
350: #define MOVSTR_ODD_HI 16 /* __movstrHI15x15 .. __movstrHI15x5 */
351:
352: /* Break even points where memcpy will do just as well. */
353: #define MOVSTR_QI_LIMIT 13
354: #define MOVSTR_HI_LIMIT 38
355: #define MOVSTR_SI_LIMIT MOVSTR_SI
356:
357: static enum machine_mode mode_from_bytes[] =
358: {VOIDmode, QImode, HImode, VOIDmode, SImode};
359: static int max_from_bytes[] = {0, MOVSTR_QI, MOVSTR_HI, 0, MOVSTR_SI};
360: static int all_from_bytes[] = {0, MOVSTR_QI, MOVSTR_ODD_HI, 0, MOVSTR_ODD_SI};
361: static int best_from_bytes[] =
362: {0, MOVSTR_QI_LIMIT, MOVSTR_HI_LIMIT, 0, MOVSTR_SI_LIMIT};
363:
364: static void block_move_loop ();
365: static void block_move_no_loop ();
366: static void block_move_sequence ();
367:
368: /* Emit code to perform a block move. Choose the best method.
369:
370: OPERANDS[0] is the destination.
371: OPERANDS[1] is the source.
372: OPERANDS[2] is the size.
373: OPERANDS[3] is the alignment safe to use. */
374:
375: void
376: expand_block_move (dest_mem, src_mem, operands)
377: rtx dest_mem;
378: rtx src_mem;
379: rtx *operands;
380: {
381: int align = INTVAL (operands[3]);
382: int constp = (GET_CODE (operands[2]) == CONST_INT);
383: int bytes = (constp ? INTVAL (operands[2]) : 0);
384:
385: if (constp && bytes <= 0)
386: return;
387:
388: /* Determine machine mode to do move with. */
389: if (align > 4)
390: align = 4;
391: else if (align <= 0 || align == 3)
392: abort (); /* block move invalid alignment. */
393:
394: if (constp && bytes <= 3 * align)
395: block_move_sequence (operands[0], dest_mem, operands[1], src_mem,
396: bytes, align, 0);
397:
398: else if (constp && bytes <= best_from_bytes[align])
399: block_move_no_loop (operands[0], dest_mem, operands[1], src_mem,
400: bytes, align);
401:
402: else if (constp && align == 4)
403: block_move_loop (operands[0], dest_mem, operands[1], src_mem,
404: bytes, align);
405:
406: else
407: {
408: #ifdef TARGET_MEM_FUNCTIONS
409: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
410: VOIDmode, 3,
411: operands[0], Pmode,
412: operands[1], Pmode,
413: operands[2], SImode);
414: #else
415: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0,
416: VOIDmode, 3,
417: operands[1], Pmode,
418: operands[0], Pmode,
419: operands[2], SImode);
420: #endif
421: }
422: }
423:
424: /* Emit code to perform a block move by calling a looping movstr library
425: function. SIZE and ALIGN are known constants. DEST and SRC are
426: registers. */
427:
428: static void
429: block_move_loop (dest, dest_mem, src, src_mem, size, align)
430: rtx dest, dest_mem;
431: rtx src, src_mem;
432: int size;
433: int align;
434: {
435: enum machine_mode mode;
436: int count;
437: int units;
438: int remainder;
439: rtx offset_rtx;
440: rtx value_rtx;
441: char entry[30];
442: tree entry_name;
443:
444: /* Determine machine mode to do move with. */
445: if (align != 4)
446: abort ();
447:
448: /* Determine the structure of the loop. */
449: count = size / MOVSTR_LOOP;
450: units = (size - count * MOVSTR_LOOP) / align;
451:
452: if (units < 2)
453: {
454: count--;
455: units += MOVSTR_LOOP / align;
456: }
457:
458: if (count <= 0)
459: {
460: block_move_no_loop (dest, dest_mem, src, src_mem, size, align);
461: return;
462: }
463:
464: remainder = size - count * MOVSTR_LOOP - units * align;
465:
466: mode = mode_from_bytes[align];
467: sprintf (entry, "__movstr%s%dn%d",
468: GET_MODE_NAME (mode), MOVSTR_LOOP, units * align);
469: entry_name = get_identifier (entry);
470:
471: offset_rtx = gen_rtx (CONST_INT, VOIDmode,
472: MOVSTR_LOOP + (1 - units) * align);
473:
474: value_rtx = gen_rtx (MEM, mode,
475: gen_rtx (PLUS, Pmode,
476: gen_rtx (REG, Pmode, 3),
477: offset_rtx));
478: RTX_UNCHANGING_P (value_rtx) = RTX_UNCHANGING_P (src_mem);
479: MEM_VOLATILE_P (value_rtx) = MEM_VOLATILE_P (src_mem);
480: MEM_IN_STRUCT_P (value_rtx) = MEM_IN_STRUCT_P (src_mem);
481:
482: emit_insn (gen_call_block_move_loop
483: (gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)),
484: dest, src, offset_rtx, value_rtx,
485: gen_rtx (REG, GET_MODE (value_rtx), ((units & 1) ? 4 : 5)),
486: gen_rtx (CONST_INT, VOIDmode, count)));
487:
488: if (remainder)
489: block_move_sequence (gen_rtx (REG, Pmode, 2), dest_mem,
490: gen_rtx (REG, Pmode, 3), src_mem,
491: remainder, align, MOVSTR_LOOP + align);
492: }
493:
494: /* Emit code to perform a block move by calling a non-looping library
495: function. SIZE and ALIGN are known constants. DEST and SRC are
496: registers. OFFSET is the known starting point for the output pattern. */
497:
498: static void
499: block_move_no_loop (dest, dest_mem, src, src_mem, size, align)
500: rtx dest, dest_mem;
501: rtx src, src_mem;
502: int size;
503: int align;
504: {
505: enum machine_mode mode = mode_from_bytes[align];
506: int units = size / align;
507: int remainder = size - units * align;
508: int most;
509: int evenp;
510: rtx offset_rtx;
511: rtx value_rtx;
512: char entry[30];
513: tree entry_name;
514:
515: if (remainder && size <= all_from_bytes[align])
516: {
517: most = all_from_bytes[align] - (align - remainder);
518: remainder = 0;
519: }
520: else
521: {
522: most = max_from_bytes[align];
523: }
524:
525: sprintf (entry, "__movstr%s%dx%d",
526: GET_MODE_NAME (mode), most, size - remainder);
527: entry_name = get_identifier (entry);
528:
529: offset_rtx = gen_rtx (CONST_INT, VOIDmode, most - (size - remainder));
530:
531: value_rtx = gen_rtx (MEM, mode,
532: gen_rtx (PLUS, Pmode,
533: gen_rtx (REG, Pmode, 3),
534: offset_rtx));
535: RTX_UNCHANGING_P (value_rtx) = RTX_UNCHANGING_P (src_mem);
536: MEM_VOLATILE_P (value_rtx) = MEM_VOLATILE_P (src_mem);
537: MEM_IN_STRUCT_P (value_rtx) = MEM_IN_STRUCT_P (src_mem);
538:
539: evenp = ((most - (size - remainder)) / align) & 1;
540:
541: emit_insn (gen_call_block_move
542: (gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)),
543: dest, src, offset_rtx, value_rtx,
544: gen_rtx (REG, GET_MODE (value_rtx), (evenp ? 4 : 5))));
545:
546: if (remainder)
547: block_move_sequence (gen_rtx (REG, Pmode, 2), dest_mem,
548: gen_rtx (REG, Pmode, 3), src_mem,
549: remainder, align, most);
550: }
551:
552: /* Emit code to perform a block move with an offset sequence of ld/st
553: instructions (..., ld 0, st 1, ld 1, st 0, ...). SIZE and ALIGN are
554: known constants. DEST and SRC are registers. OFFSET is the known
555: starting point for the output pattern. */
556:
557: static void
558: block_move_sequence (dest, dest_mem, src, src_mem, size, align, offset)
559: rtx dest, dest_mem;
560: rtx src, src_mem;
561: int size;
562: int align;
563: int offset;
564: {
565: rtx temp[2];
566: enum machine_mode mode[2];
567: int amount[2];
568: int active[2];
569: int phase = 0;
570: int next;
571: int offset_ld = offset;
572: int offset_st = offset;
573:
574: active[0] = active[1] = FALSE;
575:
576: /* Establish parameters for the first load and for the second load if
577: it is known to be the same mode as the first. */
578: amount[0] = amount[1] = align;
579: mode[0] = mode_from_bytes[align];
580: temp[0] = gen_reg_rtx (mode[0]);
581: if (size >= 2 * align)
582: {
583: mode[1] = mode[0];
584: temp[1] = gen_reg_rtx (mode[1]);
585: }
586:
587: do
588: {
589: rtx srcp, dstp;
590: next = phase;
591: phase = !phase;
592:
593: if (size > 0)
594: {
595: /* Change modes as the sequence tails off. */
596: if (size < amount[next])
597: {
598: amount[next] = (size >= 2 ? 2 : 1);
599: mode[next] = mode_from_bytes[amount[next]];
600: temp[next] = gen_reg_rtx (mode[next]);
601: }
602: size -= amount[next];
603: srcp = gen_rtx (MEM, mode[next],
604: gen_rtx (PLUS, Pmode, src,
605: gen_rtx (CONST_INT, SImode, offset_ld)));
606: RTX_UNCHANGING_P (srcp) = RTX_UNCHANGING_P (src_mem);
607: MEM_VOLATILE_P (srcp) = MEM_VOLATILE_P (src_mem);
608: MEM_IN_STRUCT_P (srcp) = MEM_IN_STRUCT_P (src_mem);
609: emit_move_insn (temp[next], srcp);
610: offset_ld += amount[next];
611: active[next] = TRUE;
612: }
613:
614: if (active[phase])
615: {
616: active[phase] = FALSE;
617: dstp = gen_rtx (MEM, mode[phase],
618: gen_rtx (PLUS, Pmode, dest,
619: gen_rtx (CONST_INT, SImode, offset_st)));
620: RTX_UNCHANGING_P (dstp) = RTX_UNCHANGING_P (dest_mem);
621: MEM_VOLATILE_P (dstp) = MEM_VOLATILE_P (dest_mem);
622: MEM_IN_STRUCT_P (dstp) = MEM_IN_STRUCT_P (dest_mem);
623: emit_move_insn (dstp, temp[phase]);
624: offset_st += amount[phase];
625: }
626: }
627: while (active[next]);
628: }
629:
630: /* Emit the code to do an AND operation. */
631:
632: char *
633: output_and (operands)
634: rtx operands[];
635: {
636: unsigned int value;
637:
638: if (REG_P (operands[2]))
639: return "and %0,%1,%2";
640:
641: value = INTVAL (operands[2]);
642: if (SMALL_INTVAL (value))
643: return "mask %0,%1,%2";
644: else if ((value & 0xffff0000) == 0xffff0000)
645: return "and %0,%1,%x2";
646: else if ((value & 0xffff) == 0xffff)
647: return "and.u %0,%1,%X2";
648: else if ((value & 0xffff) == 0)
649: return "mask.u %0,%1,%X2";
650: else if (integer_ok_for_set (~value))
651: return "clr %0,%1,%S2";
652: else
653: return "and.u %0,%1,%X2\n\tand %0,%0,%x2";
654: }
655:
656: /* Emit the code to do an inclusive OR operation. */
657:
658: char *
659: output_ior (operands)
660: rtx operands[];
661: {
662: unsigned int value;
663:
664: if (REG_P (operands[2]))
665: return "or %0,%1,%2";
666:
667: value = INTVAL (operands[2]);
668: if (SMALL_INTVAL (value))
669: return "or %0,%1,%2";
670: else if ((value & 0xffff) == 0)
671: return "or.u %0,%1,%X2";
672: else if (integer_ok_for_set (value))
673: return "set %0,%1,%s2";
674: else
675: return "or.u %0,%1,%X2\n\tor %0,%0,%x2";
676: }
677:
678: /* Emit the instructions for doing an XOR. */
679:
680: char *
681: output_xor (operands)
682: rtx operands[];
683: {
684: unsigned int value;
685:
686: if (REG_P (operands[2]))
687: return "xor %0,%1,%2";
688:
689: value = INTVAL (operands[2]);
690: if (SMALL_INTVAL (value))
691: return "xor %0,%1,%2";
692: else if ((value & 0xffff) == 0)
693: return "xor.u %0,%1,%X2";
694: else
695: return "xor.u %0,%1,%X2\n\txor %0,%0,%x2";
696: }
697:
698: /* Output a call. Normally this is just bsr or jsr, but this also deals with
699: accomplishing a branch after the call by incrementing r1. This requires
1.1.1.2 ! root 700: that various assembler bugs be accommodated. The 4.30 DG/UX assembler
1.1 root 701: requires that forward references not occur when computing the difference of
702: two labels. The [version?] Motorola assembler computes a word difference.
703: No doubt there's more to come!
704:
705: It would seem the same idea could be used to tail call, but in this case,
706: the epilogue will be non-null. */
707:
708: static rtx sb_name = 0;
709: static rtx sb_high = 0;
710: static rtx sb_low = 0;
711:
712: char *
713: output_call (operands, addr)
714: rtx operands[];
715: rtx addr;
716: {
717: operands[0] = addr;
718: if (final_sequence)
719: {
720: rtx jump;
721:
722: /* This can be generalized, but there is currently no need. */
723: if (XVECLEN (final_sequence, 0) != 2)
724: abort ();
725:
726: jump = XVECEXP (final_sequence, 0, 1);
727: if (GET_CODE (jump) == JUMP_INSN)
728: {
729: rtx low, high;
730: char *last;
731: rtx dest = XEXP (SET_SRC (PATTERN (jump)), 0);
732: int delta = 4 * (insn_addresses[INSN_UID (dest)]
733: - insn_addresses[INSN_UID (jump)]);
734: #if (MONITOR_GCC & 0x2) /* How often do long branches happen? */
735: if ((unsigned) (delta + 0x8000) >= 0x10000)
736: warning ("Internal gcc monitor: short-branch(%x)", delta);
737: #endif
738:
739: /* Delete the jump. */
740: PUT_CODE (jump, NOTE);
741: NOTE_LINE_NUMBER (jump) = NOTE_INSN_DELETED;
742: NOTE_SOURCE_FILE (jump) = 0;
743:
744: /* If we loose, we must use the non-delay form. This is unlikely
745: to ever happen. If it becomes a problem, claim that a call
746: has two delay slots and only the second can be filled with
747: a jump. */
748: #ifdef AS_BUG_IMMEDIATE_LABEL /* The assembler restricts immediate values. */
749: if (! ADD_INTVAL (delta * 2))
750: #else
751: if (! ADD_INTVAL (delta))
752: #endif
753: {
754: operands[1] = dest;
755: return (REG_P (addr)
756: ? "jsr %0\n\tbr %l1"
757: : (flag_pic
758: ? "bsr %0#plt\n\tbr %l1"
759: : "bsr %0\n\tbr %l1"));
760: }
761:
762: /* Output the short branch form. */
763: output_asm_insn ((REG_P (addr)
764: ? "jsr.n %0"
765: : (flag_pic ? "bsr.n %0#plt" : "bsr.n %0")),
766: operands);
767:
768: operands[0] = gen_label_rtx ();
769: operands[1] = gen_label_rtx ();
770: if (delta < 0)
771: {
772: low = dest;
773: high = operands[1];
774: last = "subu %#r1,%#r1,%l0\n%l1:";
775: }
776: else
777: {
778: low = operands[1];
779: high = dest;
780: last = "addu %#r1,%#r1,%l0\n%l1:";
781: }
782:
783: /* Record the values to be computed later as "def name,high-low". */
784: sb_name = gen_rtx (EXPR_LIST, VOIDmode, operands[0], sb_name);
785: sb_high = gen_rtx (EXPR_LIST, VOIDmode, high, sb_high);
786: sb_low = gen_rtx (EXPR_LIST, VOIDmode, low, sb_low);
787:
788: return last;
789: }
790: }
791: return (REG_P (addr)
792: ? "jsr%. %0"
793: : (flag_pic ? "bsr%. %0#plt" : "bsr%. %0"));
794: }
795:
796: static void
797: output_short_branch_defs (stream)
798: FILE *stream;
799: {
800: char name[256], high[256], low[256];
801:
802: for (; sb_name && sb_high && sb_low;
803: sb_name = XEXP (sb_name, 1),
804: sb_high = XEXP (sb_high, 1),
805: sb_low = XEXP (sb_low, 1))
806: {
807: ASM_GENERATE_INTERNAL_LABEL
808: (name, "L", CODE_LABEL_NUMBER (XEXP (sb_name, 0)));
809: ASM_GENERATE_INTERNAL_LABEL
810: (high, "L", CODE_LABEL_NUMBER (XEXP (sb_high, 0)));
811: ASM_GENERATE_INTERNAL_LABEL
812: (low, "L", CODE_LABEL_NUMBER (XEXP (sb_low, 0)));
813: /* This will change as the assembler requirements become known. */
1.1.1.2 ! root 814: fprintf (stream, "\t%s\t %s,%s-%s\n",
1.1 root 815: DEF_ASM_OP, &name[1], &high[1], &low[1]);
816: }
817: if (sb_name || sb_high || sb_low)
818: abort ();
819: }
820:
821: /* Report errors on floating point, if we are given NaN's, or such. Leave
822: the number as is, though, since we output the number in hex, and the
1.1.1.2 ! root 823: assembler won't choke on it. */
1.1 root 824:
825: void
826: check_float_value (mode, value)
827: enum machine_mode mode;
828: REAL_VALUE_TYPE value;
829: {
830: union {
831: REAL_VALUE_TYPE d;
832: struct {
833: unsigned sign : 1;
834: unsigned exponent : 11;
835: unsigned mantissa1 : 20;
836: unsigned mantissa2;
837: } s;
838: } u;
839:
840: if (mode == DFmode)
841: {
842: u.d = value;
843: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0)
844: {
845: if (u.s.exponent == 0x7ff) /* Not a Number */
846: warning ("floating point number is not valid for IEEE double precision");
847: else if (u.s.exponent == 0)
848: warning ("denormalized double precision floating point number");
849: }
850: }
851: else if (mode == SFmode)
852: {
853: u.d = REAL_VALUE_TRUNCATE (mode, value);
854: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0)
855: {
856: if (u.s.exponent == 0x7ff) /* Not a Number */
857: warning ("floating point number is not valid for IEEE double precision");
858: else if (u.s.exponent == 0)
859: warning ("denormalized single precision floating point number");
860: }
861: else if (u.s.exponent == 0x7ff) /* Infinity */
862: warning ("floating point number exceeds range of `float'");
863: }
864: }
865:
866: /* Return true if the operand is a power of two and is a floating
867: point type (to optimize division by power of two into multiplication). */
868:
869: int
870: real_power_of_2_operand (op, mode)
871: rtx op;
872: enum machine_mode mode;
873: {
874: union {
875: REAL_VALUE_TYPE d;
876: int i[sizeof (REAL_VALUE_TYPE) / sizeof (int)];
877: struct { /* IEEE double precision format */
878: unsigned sign : 1;
879: unsigned exponent : 11;
880: unsigned mantissa1 : 20;
881: unsigned mantissa2;
882: } s;
883: struct { /* IEEE double format to quick check */
884: unsigned sign : 1; /* if it fits in a float */
885: unsigned exponent1 : 4;
886: unsigned exponent2 : 7;
887: unsigned mantissa1 : 20;
888: unsigned mantissa2;
889: } s2;
890: } u;
891:
892: if (GET_MODE (op) != DFmode && GET_MODE (op) != SFmode)
893: return 0;
894:
895: if (GET_CODE (op) != CONST_DOUBLE)
896: return 0;
897:
898: u.i[0] = CONST_DOUBLE_LOW (op);
899: u.i[1] = CONST_DOUBLE_HIGH (op);
900:
901: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0 /* not a power of two */
902: || u.s.exponent == 0 /* constant 0.0 */
903: || u.s.exponent == 0x7ff /* NAN */
904: || (u.s2.exponent1 != 0x8 && u.s2.exponent1 != 0x7))
905: return 0; /* const won't fit in float */
906:
907: return 1;
908: }
909:
910: /* Make OP legitimate for mode MODE. Currently this only deals with DFmode
911: operands, putting them in registers and making CONST_DOUBLE values
912: SFmode where possible. */
913:
914: struct rtx_def *
915: legitimize_operand (op, mode)
916: rtx op;
917: enum machine_mode mode;
918: {
919: rtx temp;
920: union {
921: union real_extract r;
922: struct { /* IEEE double precision format */
923: unsigned sign : 1;
924: unsigned exponent : 11;
925: unsigned mantissa1 : 20;
926: unsigned mantissa2;
927: } d;
928: struct { /* IEEE double format to quick check */
929: unsigned sign : 1; /* if it fits in a float */
930: unsigned exponent1 : 4;
931: unsigned exponent2 : 7;
932: unsigned mantissa1 : 20;
933: unsigned mantissa2;
934: } s;
935: } u;
936:
937: if (GET_CODE (op) == REG || mode != DFmode)
938: return op;
939:
940: if (GET_CODE (op) == CONST_DOUBLE)
941: {
942: bcopy (&CONST_DOUBLE_LOW (op), &u.r, sizeof u);
943: if (u.d.exponent != 0x7ff /* NaN */
944: && u.d.mantissa2 == 0 /* Mantissa fits */
945: && (u.s.exponent1 == 0x8 || u.s.exponent1 == 0x7) /* Exponent fits */
946: && (temp = simplify_unary_operation (FLOAT_TRUNCATE, SFmode,
947: op, mode)) != 0)
948: return gen_rtx (FLOAT_EXTEND, mode, force_reg (SFmode, temp));
949: }
950: else if (register_operand (op, mode))
951: return op;
952:
953: return force_reg (mode, op);
954: }
955:
956: /* Return true if OP is a suitable input for a move insn. */
957:
958: int
959: move_operand (op, mode)
960: rtx op;
961: enum machine_mode mode;
962: {
963: if (register_operand (op, mode))
964: return 1;
965: if (GET_CODE (op) == CONST_INT)
966: return (classify_integer (mode, INTVAL (op)) < m88k_oru_hi16);
967: if (GET_MODE (op) != mode)
968: return 0;
969: if (GET_CODE (op) == SUBREG)
970: op = SUBREG_REG (op);
971: if (GET_CODE (op) != MEM)
972: return 0;
973:
974: op = XEXP (op, 0);
975: if (GET_CODE (op) == LO_SUM)
976: return (REG_P (XEXP (op, 0))
977: && symbolic_address_p (XEXP (op, 1)));
978: return memory_address_p (mode, op);
979: }
980:
981: /* Return true if OP is suitable for a call insn. */
982:
983: int
984: call_address_operand (op, mode)
985: rtx op;
986: enum machine_mode mode;
987: {
988: return (REG_P (op) || symbolic_address_p (op));
989: }
990:
991: /* Returns true if OP is either a symbol reference or a sum of a symbol
992: reference and a constant. */
993:
994: int
995: symbolic_address_p (op)
996: register rtx op;
997: {
998: switch (GET_CODE (op))
999: {
1000: case SYMBOL_REF:
1001: case LABEL_REF:
1002: return 1;
1003:
1004: case CONST:
1005: op = XEXP (op, 0);
1006: return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
1007: || GET_CODE (XEXP (op, 0)) == LABEL_REF)
1008: && GET_CODE (XEXP (op, 1)) == CONST_INT);
1009:
1010: default:
1011: return 0;
1012: }
1013: }
1014:
1015: /* Return true if OP is a register or const0_rtx. */
1016:
1017: int
1018: reg_or_0_operand (op, mode)
1019: rtx op;
1020: enum machine_mode mode;
1021: {
1022: return (op == const0_rtx || register_operand (op, mode));
1023: }
1024:
1025: /* Nonzero if OP is a valid second operand for an arithmetic insn. */
1026:
1027: int
1028: arith_operand (op, mode)
1029: rtx op;
1030: enum machine_mode mode;
1031: {
1032: return (register_operand (op, mode)
1033: || (GET_CODE (op) == CONST_INT && SMALL_INT (op)));
1034: }
1035:
1036: /* Return true if OP is a register or 5 bit integer. */
1037:
1038: int
1039: arith5_operand (op, mode)
1040: rtx op;
1041: enum machine_mode mode;
1042: {
1043: return (register_operand (op, mode)
1044: || (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 32));
1045: }
1046:
1047: int
1048: arith32_operand (op, mode)
1049: rtx op;
1050: enum machine_mode mode;
1051: {
1052: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
1053: }
1054:
1055: int
1056: arith64_operand (op, mode)
1057: rtx op;
1058: enum machine_mode mode;
1059: {
1060: return (register_operand (op, mode)
1061: || GET_CODE (op) == CONST_INT
1062: || (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DImode));
1063: }
1064:
1065: int
1066: int5_operand (op, mode)
1067: rtx op;
1068: enum machine_mode mode;
1069: {
1070: return (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 32);
1071: }
1072:
1073: int
1074: int32_operand (op, mode)
1075: rtx op;
1076: enum machine_mode mode;
1077: {
1078: return (GET_CODE (op) == CONST_INT);
1079: }
1080:
1081: /* Return true if OP is a register or a valid immediate operand for
1082: addu or subu. */
1083:
1084: int
1085: add_operand (op, mode)
1086: rtx op;
1087: enum machine_mode mode;
1088: {
1089: return (register_operand (op, mode)
1090: || (GET_CODE (op) == CONST_INT && ADD_INT (op)));
1091: }
1092:
1093: /* Nonzero if this is a bitmask filling the bottom bits, for optimizing and +
1094: shift left combinations into a single mak instruction. */
1095:
1096: int
1097: mak_mask_p (value)
1098: int value;
1099: {
1100: return (value && POWER_OF_2_or_0 (value + 1));
1101: }
1102:
1103: int
1104: reg_or_bbx_mask_operand (op, mode)
1105: rtx op;
1106: enum machine_mode mode;
1107: {
1108: int value;
1109: if (register_operand (op, mode))
1110: return 1;
1111: if (GET_CODE (op) != CONST_INT)
1112: return 0;
1113:
1114: value = INTVAL (op);
1115: if (POWER_OF_2 (value))
1116: return 1;
1117:
1118: return 0;
1119: }
1120:
1121: /* Return true if OP is valid to use in the context of a floating
1122: point operation. Special case 0.0, since we can use r0. */
1123:
1124: int
1125: real_or_0_operand (op, mode)
1126: rtx op;
1127: enum machine_mode mode;
1128: {
1129: if (mode != SFmode && mode != DFmode)
1130: return 0;
1131:
1132: return (register_operand (op, mode)
1133: || (GET_CODE (op) == CONST_DOUBLE
1134: && op == CONST0_RTX (mode)));
1135: }
1136:
1137: /* Return true if OP is a relational operator. */
1138:
1139: int
1140: relop (op, mode)
1141: rtx op;
1142: enum machine_mode mode;
1143: {
1144: switch (GET_CODE (op))
1145: {
1146: case EQ:
1147: case NE:
1148: case LT:
1149: case LE:
1150: case GE:
1151: case GT:
1152: case LTU:
1153: case LEU:
1154: case GEU:
1155: case GTU:
1156: return 1;
1157: default:
1158: return 0;
1159: }
1160: }
1161:
1162: /* Return true if OP is a relational operator, and is not an unsigned
1163: relational operator. */
1164:
1165: int
1166: relop_no_unsigned (op, mode)
1167: rtx op;
1168: enum machine_mode mode;
1169: {
1170: switch (GET_CODE (op))
1171: {
1172: case EQ:
1173: case NE:
1174: case LT:
1175: case LE:
1176: case GE:
1177: case GT:
1178: /* @@ What is this test doing? Why not use `mode'? */
1179: if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
1180: || GET_MODE (op) == DImode
1181: || GET_MODE_CLASS (GET_MODE (XEXP (op, 0))) == MODE_FLOAT
1182: || GET_MODE (XEXP (op, 0)) == DImode
1183: || GET_MODE_CLASS (GET_MODE (XEXP (op, 1))) == MODE_FLOAT
1184: || GET_MODE (XEXP (op, 1)) == DImode)
1185: return 0;
1186: return 1;
1187: default:
1188: return 0;
1189: }
1190: }
1191:
1192: /* Return true if the code of this rtx pattern is EQ or NE. */
1193:
1194: int
1195: equality_op (op, mode)
1196: rtx op;
1197: enum machine_mode mode;
1198: {
1199: return (GET_CODE (op) == EQ || GET_CODE (op) == NE);
1200: }
1201:
1202: /* Return true if the code of this rtx pattern is pc or label_ref. */
1203:
1204: int
1205: pc_or_label_ref (op, mode)
1206: rtx op;
1207: enum machine_mode mode;
1208: {
1209: return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF);
1210: }
1211:
1212: /* Output to FILE the start of the assembler file. */
1213:
1214: struct option
1215: {
1216: char *string;
1217: int *variable;
1218: int on_value;
1219: };
1220:
1221: static int
1222: output_option (file, sep, type, name, indent, pos, max)
1223: FILE *file;
1224: char *sep;
1225: char *type;
1226: char *name;
1227: char *indent;
1228: int pos;
1229: int max;
1230: {
1231: if (strlen (sep) + strlen (type) + strlen (name) + pos > max)
1232: {
1233: fprintf (file, indent);
1234: return fprintf (file, "%s%s", type, name);
1235: }
1236: return pos + fprintf (file, "%s%s%s", sep, type, name);
1237: }
1238:
1239: static struct { char *name; int value; } m_options[] = TARGET_SWITCHES;
1240:
1241: static void
1242: output_options (file, f_options, f_len, W_options, W_len,
1243: pos, max, sep, indent, term)
1244: FILE *file;
1245: struct option *f_options;
1246: struct option *W_options;
1247: int f_len, W_len;
1248: int pos;
1249: int max;
1250: char *indent;
1251: char *term;
1252: {
1253: register int j;
1254:
1255: if (optimize)
1256: pos = output_option (file, sep, "-O", "", indent, pos, max);
1257: if (write_symbols != NO_DEBUG)
1258: pos = output_option (file, sep, "-g", "", indent, pos, max);
1259: if (flag_traditional)
1260: pos = output_option (file, sep, "-traditional", "", indent, pos, max);
1261: if (profile_flag)
1262: pos = output_option (file, sep, "-p", "", indent, pos, max);
1263:
1264: for (j = 0; j < f_len; j++)
1265: if (*f_options[j].variable == f_options[j].on_value)
1266: pos = output_option (file, sep, "-f", f_options[j].string,
1267: indent, pos, max);
1268:
1269: for (j = 0; j < W_len; j++)
1270: if (*W_options[j].variable == W_options[j].on_value)
1271: pos = output_option (file, sep, "-W", W_options[j].string,
1272: indent, pos, max);
1273:
1274: for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++)
1275: if (m_options[j].name[0] != '\0'
1276: && m_options[j].value > 0
1277: && ((m_options[j].value & target_flags)
1278: == m_options[j].value))
1279: pos = output_option (file, sep, "-m", m_options[j].name,
1280: indent, pos, max);
1281:
1282: if (m88k_short_data)
1283: pos = output_option (file, sep, "-mshort-data-", m88k_short_data,
1284: indent, pos, max);
1285:
1286: fprintf (file, term);
1287: }
1288:
1289: void
1290: output_file_start (file, f_options, f_len, W_options, W_len)
1291: FILE *file;
1292: struct option *f_options;
1293: struct option *W_options;
1294: int f_len, W_len;
1295: {
1296: register int pos;
1297:
1298: ASM_FIRST_LINE (file);
1299: output_file_directive (file, main_input_filename);
1300: /* Switch to the data section so that the coffsem symbol and the
1301: gcc2_compiled. symbol aren't in the text section. */
1302: data_section ();
1303: ASM_COFFSEM (file);
1304:
1305: pos = fprintf (file, "\n; cc1 (%s) arguments:", VERSION_STRING);
1306: output_options (file, f_options, f_len, W_options, W_len,
1307: pos, 75, " ", "\n; ", "\n\n");
1308:
1309: if (TARGET_IDENTIFY_REVISION)
1310: {
1311: char indent[256];
1312:
1313: time_t now = time ((time_t *)0);
1.1.1.2 ! root 1314: sprintf (indent, "]\"\n\t%s\t \"@(#)%s [", IDENT_ASM_OP, main_input_filename);
1.1 root 1315: fprintf (file, indent+3);
1316: pos = fprintf (file, "gcc %s, %.24s,", VERSION_STRING, ctime (&now));
1317: output_options (file, f_options, f_len, W_options, W_len,
1318: pos, 150 - strlen (indent), " ", indent, "]\"\n\n");
1319: }
1320: }
1321:
1322: /* Output an ascii string. */
1323:
1324: void
1325: output_ascii (file, p, size)
1326: FILE *file;
1327: unsigned char *p;
1328: int size;
1329: {
1330: int i;
1331:
1332: register int num = 0;
1333:
1.1.1.2 ! root 1334: fprintf (file, "\t%s\t \"", ASCII_DATA_ASM_OP);
1.1 root 1335: for (i = 0; i < size; i++)
1336: {
1337: register int c = p[i];
1338:
1339: if (num > 48)
1340: {
1.1.1.2 ! root 1341: fprintf (file, "\"\n\t%s\t \"", ASCII_DATA_ASM_OP);
1.1 root 1342: num = 0;
1343: }
1344:
1345: if (c == '\"' || c == '\\')
1346: {
1347: putc ('\\', file);
1348: num++;
1349: }
1350:
1351: if (c >= ' ' && c < 0177)
1352: {
1353: putc (c, file);
1354: num++;
1355: }
1356: else
1357: {
1358: fprintf (file, "\\%03o", c);
1359: num += 4;
1360: /* After an octal-escape, if a digit follows,
1361: terminate one string constant and start another.
1362: The Vax assembler fails to stop reading the escape
1363: after three digits, so this is the only way we
1364: can get it to parse the data properly. */
1365: if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9')
1366: num = 32767; /* next pass will start a new string */
1367: }
1368: }
1369: fprintf (file, "\"\n");
1370: }
1371:
1372: /* Output a label (allows insn-output.c to be compiled without including
1373: m88k.c or needing to include stdio.h). */
1374:
1375: void
1376: output_label (label_number)
1377: int label_number;
1378: {
1379: ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", label_number);
1380: }
1381:
1382: /* Handle a pragma directive. HANDLE_PRAGMA conspires to parse the input
1383: following #pragma into tokens based on yylex. */
1384:
1385: void
1386: m88k_handle_pragma_token (string, token)
1387: char *string;
1388: tree token;
1389: {
1390: static enum pragma_state
1391: {
1392: ps_start,
1393: ps_done,
1394: ps_bad,
1395: ps_weak,
1396: ps_name,
1397: ps_equals,
1398: ps_value
1399: } state;
1400: static char *name;
1401: static char *value;
1402:
1403: if (HANDLE_PRAGMA_WEAK)
1404: {
1405: if (string == 0)
1406: {
1407: if (state == ps_name || state == ps_value)
1408: {
1.1.1.2 ! root 1409: fprintf (asm_out_file, "\t%s\t ", WEAK_ASM_OP);
1.1 root 1410: ASM_OUTPUT_LABELREF (asm_out_file, name);
1411: fputc ('\n', asm_out_file);
1412: if (state == ps_value)
1413: {
1.1.1.2 ! root 1414: fprintf (asm_out_file, "\t%s\t ", DEF_ASM_OP);
1.1 root 1415: ASM_OUTPUT_LABELREF (asm_out_file, name);
1416: fputc (',', asm_out_file);
1417: ASM_OUTPUT_LABELREF (asm_out_file, value);
1418: fputc ('\n', asm_out_file);
1419: }
1420: }
1421: else if (! (state == ps_done || state == ps_start))
1422: warning ("ignoring malformed #pragma weak symbol [=value]");
1423: state = ps_start;
1424: }
1425: else
1426: switch (state)
1427: {
1428: case ps_start:
1429: if (token
1430: && TREE_CODE (token) == IDENTIFIER_NODE
1431: && !strcmp (IDENTIFIER_POINTER (token), "weak"))
1432: state = ps_weak;
1433: else
1434: state = ps_done;
1435: break;
1436:
1437: case ps_weak:
1438: if (token
1439: && TREE_CODE (token) == IDENTIFIER_NODE)
1440: {
1441: name = IDENTIFIER_POINTER (token);
1442: state = ps_name;
1443: }
1444: else
1445: state = ps_bad;
1446: break;
1447:
1448: case ps_name:
1449: state = (strcmp (string, "=") ? ps_bad : ps_equals);
1450: break;
1451:
1452: case ps_equals:
1453: if (token
1454: && TREE_CODE (token) == IDENTIFIER_NODE)
1455: {
1456: value = IDENTIFIER_POINTER (token);
1457: state = ps_value;
1458: }
1459: else
1460: state = ps_bad;
1461: break;
1462:
1463: case ps_value:
1464: state = ps_bad;
1465: case ps_bad:
1466: case ps_done:
1467: break;
1468:
1469: default:
1470: abort ();
1471: }
1472: }
1473: }
1474:
1475: /* Generate the assembly code for function entry.
1476:
1477: The prologue is responsible for setting up the stack frame,
1478: initializing the frame pointer register, saving registers that must be
1479: saved, and allocating SIZE additional bytes of storage for the
1480: local variables. SIZE is an integer. FILE is a stdio
1481: stream to which the assembler code should be output.
1482:
1483: The label for the beginning of the function need not be output by this
1484: macro. That has already been done when the macro is run.
1485:
1486: To determine which registers to save, the macro can refer to the array
1487: `regs_ever_live': element R is nonzero if hard register
1488: R is used anywhere within the function. This implies the
1489: function prologue should save register R, but not if it is one
1490: of the call-used registers.
1491:
1492: On machines where functions may or may not have frame-pointers, the
1493: function entry code must vary accordingly; it must set up the frame
1494: pointer if one is wanted, and not otherwise. To determine whether a
1495: frame pointer is in wanted, the macro can refer to the variable
1496: `frame_pointer_needed'. The variable's value will be 1 at run
1497: time in a function that needs a frame pointer.
1498:
1499: On machines where an argument may be passed partly in registers and
1500: partly in memory, this macro must examine the variable
1501: `current_function_pretend_args_size', and allocate that many bytes
1502: of uninitialized space on the stack just underneath the first argument
1503: arriving on the stack. (This may not be at the very end of the stack,
1504: if the calling sequence has pushed anything else since pushing the stack
1505: arguments. But usually, on such machines, nothing else has been pushed
1506: yet, because the function prologue itself does all the pushing.)
1507:
1508: If `ACCUMULATE_OUTGOING_ARGS' is defined, the variable
1509: `current_function_outgoing_args_size' contains the size in bytes
1510: required for the outgoing arguments. This macro must add that
1511: amount of uninitialized space to very bottom of the stack.
1512:
1513: The stack frame we use looks like this:
1514:
1515: caller callee
1516: |==============================================|
1517: | caller's frame |
1518: |==============================================|
1519: | [caller's outgoing memory arguments] |
1520: |==============================================|
1521: | caller's outgoing argument area (32 bytes) |
1522: sp -> |==============================================| <- ap
1523: | [local variable space] |
1524: |----------------------------------------------|
1525: | [return address (r1)] |
1526: |----------------------------------------------|
1527: | [previous frame pointer (r30)] |
1528: |==============================================| <- fp
1529: | [preserved registers (r25..r14)] |
1530: |==============================================|
1531: | [dynamically allocated space (alloca)] |
1532: |==============================================|
1533: | [callee's outgoing memory arguments] |
1534: |==============================================|
1535: | [callee's outgoing argument area (32 bytes)] |
1536: |==============================================| <- sp
1537:
1538: Notes:
1539:
1540: r1 and r30 must be saved if debugging.
1541:
1542: fp (if present) is located two words down from the local
1543: variable space.
1544: */
1545:
1546: static void output_reg_adjust ();
1547: static void preserve_registers ();
1548: static void output_tdesc ();
1549:
1550: static int nregs;
1551: static char save_regs[FIRST_PSEUDO_REGISTER];
1552: static int frame_laid_out;
1553: static int frame_size;
1554: static int variable_args_p;
1555:
1556: extern char call_used_regs[];
1557: extern int current_function_pretend_args_size;
1558: extern int current_function_outgoing_args_size;
1559: extern int frame_pointer_needed;
1560:
1561: #define FIRST_OCS_PRESERVE_REGISTER 14
1562: #define LAST_OCS_PRESERVE_REGISTER 30
1563:
1564: #define STACK_UNIT_BOUNDARY (STACK_BOUNDARY / BITS_PER_UNIT)
1565: #define ROUND_CALL_BLOCK_SIZE(BYTES) \
1566: (((BYTES) + (STACK_UNIT_BOUNDARY - 1)) & ~(STACK_UNIT_BOUNDARY - 1))
1567:
1568: /* Establish the position of the FP relative to the SP. This is done
1569: either during FUNCTION_PROLOGUE or by INITIAL_ELIMINATION_OFFSET. */
1570:
1571: void
1572: m88k_layout_frame ()
1573: {
1574: int regno, sp_size;
1575:
1576: frame_laid_out++;
1577:
1578: bzero ((char *) &save_regs[0], sizeof (save_regs));
1579: sp_size = nregs = 0;
1580: frame_size = get_frame_size ();
1581:
1582: /* Since profiling requires a call, make sure r1 is saved. */
1583: if (profile_flag)
1584: save_regs[1] = 1;
1585:
1586: /* If we are producing debug information, store r1 and r30 where the
1587: debugger wants to find them (r30 at r30+0, r1 at r30+4). Space has
1588: already been reserved for r1/r30 in STARTING_FRAME_OFFSET. */
1589: if (write_symbols != NO_DEBUG && !TARGET_OCS_FRAME_POSITION)
1590: save_regs[1] = 1;
1591:
1592: /* If there is a call, alloca is used, __builtin_alloca is used, or
1593: a dynamic-sized object is defined, add the 8 additional words
1594: for the callee's argument area. The common denominator is that the
1595: FP is required. may_call_alloca only gets calls to alloca;
1596: current_function_calls_alloca gets alloca and __builtin_alloca. */
1597: if (regs_ever_live[1] || frame_pointer_needed)
1598: {
1599: save_regs[1] = 1;
1600: sp_size += REG_PARM_STACK_SPACE (0);
1601: }
1602:
1603: /* If we are producing PIC, save the addressing base register and r1. */
1604: if (flag_pic && current_function_uses_pic_offset_table)
1605: {
1606: save_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
1607: nregs++;
1608: }
1609:
1610: /* If a frame is requested, save the previous FP, and the return
1611: address (r1), so that a traceback can be done without using tdesc
1612: information. */
1613: if (frame_pointer_needed)
1614: save_regs[FRAME_POINTER_REGNUM] = save_regs[1] = 1;
1615:
1616: /* Figure out which normal register(s) needs to be saved. */
1617: for (regno = 2; regno < FRAME_POINTER_REGNUM; regno++)
1618: if (regs_ever_live[regno] && ! call_used_regs[regno])
1619: {
1620: save_regs[regno] = 1;
1621: nregs++;
1622: }
1623:
1624: /* Achieve greatest use of double memory ops. Either we end up saving
1625: r30 or we use that slot to align the regsters we do save. */
1626: if (nregs >= 2 && save_regs[1] && !save_regs[FRAME_POINTER_REGNUM])
1627: sp_size += 4;
1628:
1629: nregs += save_regs[1] + save_regs[FRAME_POINTER_REGNUM];
1630: sp_size += 4 * nregs;
1631: sp_size += current_function_outgoing_args_size;
1632:
1633: /* The first two saved registers are placed above the new frame pointer
1634: if any. In the only case this matters, they are r1 and r30. */
1635: if (frame_pointer_needed || sp_size)
1636: {
1637: m88k_fp_offset = ROUND_CALL_BLOCK_SIZE (sp_size - STARTING_FRAME_OFFSET);
1638: m88k_stack_size = m88k_fp_offset + STARTING_FRAME_OFFSET;
1639: }
1640: else
1641: {
1642: m88k_stack_size = m88k_fp_offset = 0;
1643: }
1644:
1645: /* First, combine m88k_stack_size and size. If m88k_stack_size is
1646: non-zero, align the frame size to 8 mod 16; otherwise align the
1647: frame size to 0 mod 16. (If stacks are 8 byte aligned, this ends
1648: up as a NOP. */
1649: {
1650: int need
1651: = ((m88k_stack_size ? STACK_UNIT_BOUNDARY - STARTING_FRAME_OFFSET : 0)
1652: - (frame_size % STACK_UNIT_BOUNDARY));
1653: if (need)
1654: {
1655: if (need < 0)
1656: need += STACK_UNIT_BOUNDARY;
1657: (void) assign_stack_local (BLKmode, need, BITS_PER_UNIT);
1658: frame_size = get_frame_size ();
1659: }
1660: m88k_stack_size
1661: = ROUND_CALL_BLOCK_SIZE (m88k_stack_size + frame_size
1662: + current_function_pretend_args_size);
1663: }
1664: }
1665:
1666: /* Return true if this function is known to have a null epilogue. */
1667:
1668: int
1669: null_epilogue ()
1670: {
1671: if (! reload_completed)
1672: return 0;
1673: if (! frame_laid_out)
1674: m88k_layout_frame ();
1675: return (! frame_pointer_needed
1676: && nregs == 0
1677: && m88k_stack_size == 0);
1678: }
1679:
1680: /* Determine the number of instructions needed for the function epilogue. */
1681:
1682: #define MAX_EPILOGUE_DELAY_INSNS 4
1683:
1684: static char epilogue_dead_regs[FIRST_PSEUDO_REGISTER];
1685:
1686: delay_slots_for_epilogue ()
1687: {
1688: register int insns = save_regs[1] + save_regs[FRAME_POINTER_REGNUM];
1689: register int regs = nregs - insns;
1690:
1691: if (regs > 3)
1692: insns += 1 + (regs & 1);
1693: else if (nregs == 4)
1694: /* This is a special cases of ld/ld/ld.d which has no start-up delay. */
1695: return 0;
1696:
1697: if (insns)
1698: {
1699: bzero ((char *) &epilogue_dead_regs[0], sizeof (epilogue_dead_regs));
1700: epilogue_dead_regs[1] = save_regs[1];
1701: epilogue_dead_regs[STACK_POINTER_REGNUM] = frame_pointer_needed;
1702: epilogue_dead_regs[TEMP_REGNUM] = ! ADD_INTVAL (m88k_fp_offset);
1703: }
1704:
1705: return insns;
1706: }
1707:
1708: /* Return 1 if X is safe to use as an epilogue insn. */
1709:
1710: int
1711: ok_for_epilogue_p (x)
1712: rtx x;
1713: {
1714: register char *fmt;
1715: register int i, j;
1716:
1717: switch (GET_CODE (x))
1718: {
1719: case REG:
1720: for (i = REGNO (x), j = i + HARD_REGNO_NREGS (i, GET_MODE (x));
1721: i < j;
1722: i++)
1723: if (epilogue_dead_regs[i])
1724: return 0;
1725:
1726: case CONST_INT:
1727: case CONST_DOUBLE:
1728: case CONST:
1729: case PC:
1730: case CC0:
1731: case LABEL_REF:
1732: case SYMBOL_REF:
1733: case CODE_LABEL:
1734: return 1;
1735: }
1736:
1737: fmt = GET_RTX_FORMAT (GET_CODE (x));
1738: for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
1739: {
1740: if (fmt[i] == 'e')
1741: {
1742: if (!ok_for_epilogue_p (XEXP (x, i)))
1743: return 0;
1744: }
1745: else if (fmt[i] == 'E')
1746: {
1747: for (j = XVECLEN (x, i) - 1; j >= 0; j--)
1748: if (!ok_for_epilogue_p (XVECEXP (x, i, j)))
1749: return 0;
1750: }
1751: }
1752: return 1;
1753: }
1754:
1755: int
1756: eligible_for_epilogue_delay (insn)
1757: rtx insn;
1758: {
1759: switch (get_attr_type (insn))
1760: {
1761: case TYPE_STORE:
1762: case TYPE_LOADA:
1763: case TYPE_ARITH:
1764: case TYPE_MARITH:
1765: case TYPE_MSTORE:
1766: return ok_for_epilogue_p (PATTERN (insn));
1767: default:
1768: return 0;
1769: }
1770: }
1771:
1772: /* Determine if the current function has any references to the arg pointer.
1773: This is done indirectly by examining the DECL_ARGUMENTS' DECL_RTL.
1774: It is OK to return TRUE if there are no references, but FALSE must be
1775: correct. */
1776:
1777: static int
1778: uses_arg_area_p ()
1779: {
1780: register tree parm;
1781:
1782: if (current_function_decl == 0
1783: || current_function_varargs
1784: || variable_args_p)
1785: return 1;
1786:
1787: for (parm = DECL_ARGUMENTS (current_function_decl);
1788: parm;
1789: parm = TREE_CHAIN (parm))
1790: {
1791: if (DECL_RTL (parm) == 0
1792: || GET_CODE (DECL_RTL (parm)) == MEM)
1793: return 1;
1794:
1795: if (DECL_INCOMING_RTL (parm) == 0
1796: || GET_CODE (DECL_INCOMING_RTL (parm)) == MEM)
1797: return 1;
1798: }
1799: return 0;
1800: }
1801:
1802: void
1803: m88k_output_prologue (stream, size)
1804: FILE *stream;
1805: int size;
1806: {
1807: int old_fp_offset = m88k_fp_offset;
1808: int old_stack_size = m88k_stack_size;
1809:
1810: m88k_layout_frame ();
1811: #if (MONITOR_GCC & 0x8) /* Watch for suspicious register elimination changes. */
1812: if (frame_laid_out > 1)
1813: {
1814: if (old_fp_offset != m88k_fp_offset)
1815: warning ("Internal gcc error: FP offset has changed by %d bytes",
1816: m88k_fp_offset - old_fp_offset);
1817: if (old_stack_size != m88k_stack_size)
1818: warning ("Internal gcc error: stack size has changed by %d bytes",
1819: m88k_stack_size - old_stack_size);
1820: }
1821: #endif
1822: frame_laid_out = 0;
1823:
1824: if (TARGET_OPTIMIZE_ARG_AREA
1825: && m88k_stack_size
1826: && ! uses_arg_area_p ())
1827: {
1828: /* The incoming argument area is used for stack space if it is not
1829: used (or if -mno-use-arg-area is given). */
1830: if ((m88k_stack_size -= REG_PARM_STACK_SPACE (0)) < 0)
1831: m88k_stack_size = 0;
1832: }
1833:
1834: if (m88k_stack_size)
1835: output_reg_adjust (stream, 31, 31, -m88k_stack_size, 0);
1836:
1837: if (nregs)
1838: preserve_registers (stream, m88k_fp_offset + 4, 1);
1839:
1840: if (frame_pointer_needed)
1841: output_reg_adjust (stream, 30, 31, m88k_fp_offset, 0);
1842:
1843: if (TARGET_OCS_DEBUG_INFO)
1844: PUT_OCS_FUNCTION_START (stream);
1845:
1846: if (flag_pic && save_regs[PIC_OFFSET_TABLE_REGNUM])
1847: {
1848: char label[256];
1849:
1850: if (! save_regs[1])
1851: fprintf (stream, "\tor\t %s,%s,0\n",
1852: reg_names[TEMP_REGNUM], reg_names[1]);
1853: ASM_GENERATE_INTERNAL_LABEL (label, "Lab", m88k_function_number);
1854: fprintf (stream, "\tbsr.n\t %s\n", &label[1]);
1855: fprintf (stream, "\tor.u\t %s,%s,%shi16(%s#abdiff)\n",
1856: reg_names[PIC_OFFSET_TABLE_REGNUM], reg_names[0],
1857: m88k_pound_sign, &label[1]);
1858: ASM_OUTPUT_INTERNAL_LABEL (stream, "Lab", m88k_function_number);
1859: fprintf (stream, "\tor\t %s,%s,%slo16(%s#abdiff)\n",
1860: reg_names[PIC_OFFSET_TABLE_REGNUM],
1861: reg_names[PIC_OFFSET_TABLE_REGNUM],
1862: m88k_pound_sign, &label[1]);
1863: fprintf (stream, "\taddu\t %s,%s,%s\n",
1864: reg_names[PIC_OFFSET_TABLE_REGNUM],
1865: reg_names[PIC_OFFSET_TABLE_REGNUM], reg_names[1]);
1866: if (! save_regs[1])
1867: fprintf (stream, "\tor\t %s,%s,0\n",
1868: reg_names[1], reg_names[TEMP_REGNUM]);
1869: }
1870:
1871: m88k_prologue_done = 1; /* it's ok now to put out ln directives */
1872: }
1873:
1874: /* This function generates the assembly code for function exit,
1875: on machines that need it. Args are same as for FUNCTION_PROLOGUE.
1876:
1877: The function epilogue should not depend on the current stack pointer!
1878: It should use the frame pointer only, if there is a frame pointer.
1879: This is mandatory because of alloca; we also take advantage of it to
1880: omit stack adjustments before returning. */
1881:
1882: void
1883: m88k_output_epilogue (stream, size)
1884: FILE *stream;
1885: int size;
1886: {
1887: rtx insn = get_last_insn ();
1.1.1.2 ! root 1888: #if (MONITOR_GCC & 0x4) /* What are interesting prologue/epilogue values? */
1.1 root 1889: fprintf (stream, "; size = %d, m88k_fp_offset = %d, m88k_stack_size = %d\n",
1890: size, m88k_fp_offset, m88k_stack_size);
1891: #endif
1892:
1893: output_short_branch_defs (stream);
1894:
1895: if (TARGET_OCS_DEBUG_INFO)
1896: PUT_OCS_FUNCTION_END (stream);
1897:
1898: /* If the last insn was a BARRIER, we don't have to write any code. */
1899: if (GET_CODE (insn) == NOTE)
1900: insn = prev_nonnote_insn (insn);
1901: if (insn && GET_CODE (insn) == BARRIER)
1902: {
1903: if (current_function_epilogue_delay_list)
1904: abort ();
1905: }
1906: else
1907: {
1908: if (frame_pointer_needed)
1909: output_reg_adjust (stream, 31, 30, -m88k_fp_offset, 0);
1910:
1911: if (nregs)
1912: preserve_registers (stream, m88k_fp_offset + 4, 0);
1913:
1914: output_reg_adjust (stream, 31, 31, m88k_stack_size, 1);
1915: }
1916:
1917: fprintf (stream, "\n");
1918:
1919: if (TARGET_OCS_DEBUG_INFO)
1920: output_tdesc (stream, m88k_fp_offset + 4);
1921:
1922: m88k_function_number++;
1923: m88k_prologue_done = 0; /* don't put out ln directives */
1924: variable_args_p = 0; /* has variable args */
1925: }
1926:
1927: /* Output code to STREAM to set DSTREG to SRCREG + AMOUNT. Issue
1928: a return instruction and use it's delay slot based on RETURN_P. */
1929:
1930: static void
1931: output_reg_adjust (stream, dstreg, srcreg, amount, return_p)
1932: FILE *stream;
1933: int dstreg, srcreg, amount, return_p;
1934: {
1935: char *opname;
1936: char incr[256];
1937:
1938: if (amount < 0)
1939: {
1940: opname = "subu";
1941: amount = -amount;
1942: }
1943: else
1944: opname = "addu";
1945:
1946: if (amount == 0 && dstreg == srcreg)
1947: {
1948: if (return_p)
1949: fprintf (stream, "\tjmp\t %s\n", reg_names[1]);
1950: return;
1951: }
1952: else if (SMALL_INTVAL (amount))
1953: sprintf (incr, "\t%s\t %s,%s,%d", opname,
1954: reg_names[dstreg], reg_names[srcreg], amount);
1955: else
1956: {
1957: rtx operands[2];
1958:
1959: operands[0] = gen_rtx (REG, SImode, TEMP_REGNUM);
1960: operands[1] = gen_rtx (CONST_INT, VOIDmode, amount);
1961: output_asm_insn (output_load_const_int (SImode, operands),
1962: operands);
1963: sprintf (incr, "\t%s\t %s,%s,%s", opname,
1964: reg_names[dstreg], reg_names[srcreg], reg_names[TEMP_REGNUM]);
1965: }
1966:
1967: if (!return_p)
1968: fprintf (stream, "%s\n", incr);
1969: else if (flag_delayed_branch)
1970: fprintf (stream, "\tjmp.n\t %s\n%s\n", reg_names[1], incr);
1971: else
1972: fprintf (stream, "%s\n\tjmp\t %s\n", incr, reg_names[1]);
1973: }
1974:
1975: /* Save/restore the preserve registers. base is the highest offset from
1976: r31 at which a register is stored. store_p is true if stores are to
1977: be done; otherwise loads. When loading, output the epilogue delay
1978: insns. */
1979:
1980: static void
1981: preserve_registers (stream, base, store_p)
1982: FILE *stream;
1983: int base;
1984: int store_p;
1985: {
1986: int regno, offset;
1987: char *fmt = (store_p ? "\tst%s\t %s,%s,%d\n" : "\tld%s\t %s,%s,%d\n");
1988: struct mem_op {
1989: int regno;
1990: int nregs;
1991: int offset;
1992: } mem_op[FIRST_PSEUDO_REGISTER];
1993: struct mem_op *mo_ptr = mem_op;
1994:
1995: /* The 88open OCS mandates that preserved registers be stored in
1996: increasing order. For compatibility with current practice,
1997: the order is r1, r30, then the preserve registers. */
1998:
1999: offset = base;
2000: if (save_regs[1])
2001: {
2002: /* An extra word is given in this case to make best use of double
2003: memory ops. */
2004: if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM])
2005: offset -= 4;
2006: fprintf (stream, fmt, "", reg_names[1], reg_names[31], offset);
2007: offset -= 4;
2008: base = offset;
2009: }
2010:
2011: /* Walk the registers to save recording all single memory operations. */
2012: for (regno = FRAME_POINTER_REGNUM; regno > 1; regno--)
2013: if (save_regs[regno])
2014: {
2015: if ((offset & 7) != 4 || (regno & 1) != 1 || !save_regs[regno-1])
2016: {
2017: mo_ptr->nregs = 1;
2018: mo_ptr->regno = regno;
2019: mo_ptr->offset = offset;
2020: mo_ptr++;
2021: offset -= 4;
2022: }
2023: else
2024: {
2025: regno--;
2026: offset -= 2*4;
2027: }
2028: }
2029:
2030: /* Walk the registers to save recording all double memory operations.
2031: This avoids a delay in the epilogue (ld.d/ld). */
2032: offset = base;
2033: for (regno = FRAME_POINTER_REGNUM; regno > 1; regno--)
2034: if (save_regs[regno])
2035: {
2036: if ((offset & 7) != 4 || (regno & 1) != 1 || !save_regs[regno-1])
2037: {
2038: offset -= 4;
2039: }
2040: else
2041: {
2042: mo_ptr->nregs = 2;
2043: mo_ptr->regno = regno-1;
2044: mo_ptr->offset = offset-4;
2045: mo_ptr++;
2046: regno--;
2047: offset -= 2*4;
2048: }
2049: }
2050: mo_ptr->regno = 0;
2051:
2052: /* Output the delay insns interleaved with the memory operations. */
2053: if (! store_p && current_function_epilogue_delay_list)
2054: {
2055: rtx delay_insns = current_function_epilogue_delay_list;
2056: rtx insn;
2057:
2058: /* The first delay insn goes after the restore of r1. */
2059: if (save_regs[1])
2060: {
2061: final_scan_insn (XEXP (delay_insns, 0), stream, 1, 0, 1);
2062: delay_insns = XEXP (delay_insns, 1);
2063: }
2064:
2065: while (delay_insns)
2066: {
2067: /* Find a memory operation that doesn't conflict with this insn. */
2068: for (mo_ptr = mem_op; mo_ptr->regno != 0; mo_ptr++)
2069: {
2070: if (mo_ptr->nregs)
2071: {
2072: rtx ok_insns = delay_insns;
2073: int i;
2074:
2075: for (i = 0; i < mo_ptr->nregs; i++)
2076: epilogue_dead_regs[mo_ptr->regno + i] = 1;
2077:
2078: while (ok_insns)
2079: {
2080: insn = XEXP (ok_insns, 0);
2081: ok_insns = XEXP (ok_insns, 1);
2082:
2083: if (! ok_for_epilogue_p (PATTERN (insn)))
2084: {
2085: for (i = 0; i < mo_ptr->nregs; i++)
2086: epilogue_dead_regs[mo_ptr->regno + i] = 0;
2087: insn = 0;
2088: break; /* foreach delay insn */
2089: }
2090: }
2091: if (insn)
2092: {
2093: fprintf (stream, fmt, mo_ptr->nregs > 1 ? ".d" : "",
2094: reg_names[mo_ptr->regno], reg_names[31],
2095: mo_ptr->offset);
2096: mo_ptr->nregs = 0;
2097: break; /* foreach memory operation */
2098: }
2099: }
2100: }
2101: final_scan_insn (XEXP (delay_insns, 0), stream, 1, 0, 1);
2102: delay_insns = XEXP (delay_insns, 1);
2103: }
2104: }
2105:
2106: /* Output the memory operations. */
2107: for (mo_ptr = mem_op; mo_ptr->regno; mo_ptr++)
2108: {
2109: if (mo_ptr->nregs)
2110: fprintf (stream, fmt, mo_ptr->nregs > 1 ? ".d" : "",
2111: reg_names[mo_ptr->regno], reg_names[31], mo_ptr->offset);
2112: }
2113: }
2114:
2115: /* Convert the address expression REG to a CFA offset. */
2116:
2117: int
2118: m88k_debugger_offset (reg, offset)
2119: register rtx reg;
2120: register int offset;
2121: {
2122: if (GET_CODE (reg) == PLUS)
2123: {
2124: offset = INTVAL (XEXP (reg, 1));
2125: reg = XEXP (reg, 0);
2126: }
2127:
2128: /* Put the offset in terms of the CFA (arg pointer). */
2129: if (reg == frame_pointer_rtx)
2130: offset += m88k_fp_offset - m88k_stack_size;
2131: else if (reg == stack_pointer_rtx)
2132: offset -= m88k_stack_size;
2133: else if (reg != arg_pointer_rtx)
2134: {
1.1.1.2 ! root 2135: #if (MONITOR_GCC & 0x10) /* Watch for suspicious symbolic locations. */
1.1 root 2136: if (! (GET_CODE (reg) == REG
2137: && REGNO (reg) >= FIRST_PSEUDO_REGISTER))
2138: warning ("Internal gcc error: Can't express symbolic location");
1.1.1.2 ! root 2139: #endif
1.1 root 2140: return 0;
2141: }
2142:
2143: return offset;
2144: }
2145:
2146: /* Output the 88open OCS proscribed text description information.
2147: The information is:
2148: 0 8: zero
2149: 0 22: info-byte-length (16 bytes)
2150: 0 2: info-alignment (word 2)
2151: 1 32: info-protocol (version 1)
2152: 2 32: starting-address (inclusive, not counting prologue)
2153: 3 32: ending-address (exclusive, not counting epilog)
2154: 4 8: info-variant (version 1)
2155: 4 17: register-save-mask (from register 14 to 30)
2156: 4 1: zero
2157: 4 1: return-address-info-discriminant
2158: 4 5: frame-address-register
2159: 5 32: frame-address-offset
2160: 6 32: return-address-info
2161: 7 32: register-save-offset */
2162:
2163: static void
2164: output_tdesc (file, offset)
2165: FILE *file;
2166: int offset;
2167: {
2168: int regno, i;
2169: long mask, return_address_info, register_save_offset;
2170: char buf[256];
2171:
2172: for (mask = 0, i = 0, regno = FIRST_OCS_PRESERVE_REGISTER;
2173: regno <= LAST_OCS_PRESERVE_REGISTER;
2174: regno++)
2175: {
2176: mask <<= 1;
2177: if (save_regs[regno])
2178: {
2179: mask |= 1;
2180: i++;
2181: }
2182: }
2183:
2184: if (save_regs[1])
2185: {
2186: if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM])
2187: offset -= 4;
2188: return_address_info = - m88k_stack_size + offset;
2189: register_save_offset = return_address_info - i*4;
2190: }
2191: else
2192: {
2193: return_address_info = 1;
2194: register_save_offset = - m88k_stack_size + offset + 4 - i*4;
2195: }
2196:
2197: tdesc_section ();
2198:
1.1.1.2 ! root 2199: fprintf (file, "\t%s\t %d", INT_ASM_OP, (16 << 2) | 2 /* 8:0,22:16,2:2 */);
1.1 root 2200: fprintf (file, ",%d", flag_pic ? 2 : 1);
2201:
2202: ASM_GENERATE_INTERNAL_LABEL (buf, OCS_START_PREFIX, m88k_function_number);
2203: fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : "");
2204: ASM_GENERATE_INTERNAL_LABEL (buf, OCS_END_PREFIX, m88k_function_number);
2205: fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : "");
2206:
2207: fprintf (file, ",0x%x", /* 8:1,17:0x%.3x,1:0,1:%d,5:%d */
2208: (1 << (17+1+1+5)) |
2209: (mask << (1+1+5)) |
2210: ((!!save_regs[1]) << 5) |
2211: ((frame_pointer_needed
2212: ? FRAME_POINTER_REGNUM
2213: : STACK_POINTER_REGNUM)));
2214:
2215: fprintf (file, ",0x%x", (m88k_stack_size
2216: - (frame_pointer_needed ? m88k_fp_offset : 0)));
2217: fprintf (file, ",0x%x", return_address_info);
2218: fprintf (file, ",0x%x\n", register_save_offset);
2219:
2220: text_section ();
2221: }
2222:
2223: /* Output assembler code to FILE to increment profiler label # LABELNO
2224: for profiling a function entry. NAME is the mcount function name
2225: (varies), SAVEP indicates whether the parameter registers need to
2226: be saved and restored. */
2227:
2228: void
2229: output_function_profiler (file, labelno, name, savep)
2230: FILE *file;
2231: int labelno;
2232: char *name;
2233: int savep;
2234: {
2235: char label[256];
2236: char dbi[256];
2237: char *temp = (savep ? reg_names[2] : reg_names[10]);
2238:
2239: if (savep)
2240: {
2241: fprintf (file, "\tsubu\t %s,%s,64\n", reg_names[31], reg_names[31]);
2242: fprintf (file, "\tst.d\t %s,%s,32\n", reg_names[2], reg_names[31]);
2243: fprintf (file, "\tst.d\t %s,%s,40\n", reg_names[4], reg_names[31]);
2244: fprintf (file, "\tst.d\t %s,%s,48\n", reg_names[6], reg_names[31]);
2245: fprintf (file, "\tst.d\t %s,%s,56\n", reg_names[8], reg_names[31]);
2246: }
2247:
2248: ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno);
2249: if (flag_pic == 2)
2250: {
2251: fprintf (file, "\tor.u\t %s,%s,%shi16(%s#got_rel)\n",
2252: temp, reg_names[0], m88k_pound_sign, &label[1]);
2253: fprintf (file, "\tor\t %s,%s,%slo16(%s#got_rel)\n",
2254: temp, temp, m88k_pound_sign, &label[1]);
2255: sprintf (dbi, "\tld\t %s,%s,%s\n", temp,
2256: reg_names[PIC_OFFSET_TABLE_REGNUM], temp);
2257: }
2258: else if (flag_pic)
2259: {
2260: sprintf (dbi, "\tld\t %s,%s,%s#got_rel\n", temp,
2261: reg_names[PIC_OFFSET_TABLE_REGNUM], &label[1]);
2262: }
2263: else
2264: {
2265: fprintf (file, "\tor.u\t %s,%s,%shi16(%s)\n",
2266: temp, reg_names[0], m88k_pound_sign, &label[1]);
2267: sprintf (dbi, "\tor\t %s,%s,%slo16(%s)\n",
2268: temp, temp, m88k_pound_sign, &label[1]);
2269: }
2270:
2271: if (flag_pic)
2272: fprintf (file, "\tbsr.n\t %s#plt\n", name);
2273: else
2274: fprintf (file, "\tbsr.n\t %s\n", name);
2275: fputs (dbi, file);
2276:
2277: if (savep)
2278: {
2279: fprintf (file, "\tld.d\t %s,%s,32\n", reg_names[2], reg_names[31]);
2280: fprintf (file, "\tld.d\t %s,%s,40\n", reg_names[4], reg_names[31]);
2281: fprintf (file, "\tld.d\t %s,%s,48\n", reg_names[6], reg_names[31]);
2282: fprintf (file, "\tld.d\t %s,%s,56\n", reg_names[8], reg_names[31]);
2283: fprintf (file, "\taddu\t %s,%s,64\n", reg_names[31], reg_names[31]);
2284: }
2285: }
2286:
2287: /* Output assembler code to FILE to initialize basic-block profiling for
2288: the current module. LABELNO is unique to each instance. */
2289:
2290: void
2291: output_function_block_profiler (file, labelno)
2292: FILE *file;
2293: int labelno;
2294: {
2295: char block[256];
2296: char label[256];
2297:
2298: ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0);
2299: ASM_GENERATE_INTERNAL_LABEL (label, "LPY", labelno);
2300:
2301: /* @@ Need to deal with PIC. I'm not sure what the requirements are on
2302: register usage, so I used r26/r27 to be safe. */
2303: fprintf (file, "\tor.u\t %s,%s,%shi16(%s)\n", reg_names[27], reg_names[0],
2304: m88k_pound_sign, &block[1]);
2305: fprintf (file, "\tld\t %s,%s,%slo16(%s)\n", reg_names[26], reg_names[27],
2306: m88k_pound_sign, &block[1]);
2307: fprintf (file, "\tbcnd\t %sne0,%s,%s\n",
2308: m88k_pound_sign, reg_names[26], &label[1]);
2309: fputs ("\tbsr.n\t ", file);
2310: ASM_OUTPUT_LABELREF (file, "__bb_init_func");
2311: putc ('\n', file);
2312: fprintf (file, "\tor\t %s,%s,%slo16(%s)\n", reg_names[2], reg_names[27],
2313: m88k_pound_sign, &block[1]);
2314: ASM_OUTPUT_INTERNAL_LABEL (file, "LPY", labelno);
2315: }
2316:
2317: /* Output assembler code to FILE to increment the count associated with
2318: the basic block number BLOCKNO. */
2319:
2320: void
2321: output_block_profiler (file, blockno)
2322: FILE *file;
2323: int blockno;
2324: {
2325: char block[256];
2326:
2327: ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0);
2328:
2329: /* @@ Need to deal with PIC. I'm not sure what the requirements are on
2330: register usage, so I used r26/r27 to be safe. */
2331: fprintf (file, "\tor.u\t %s,%s,%shi16(%s+%d)\n", reg_names[27], reg_names[0],
2332: m88k_pound_sign, &block[1], 4 * blockno);
2333: fprintf (file, "\tld\t %s,%s,%slo16(%s+%d)\n", reg_names[26], reg_names[27],
2334: m88k_pound_sign, &block[1], 4 * blockno);
2335: fprintf (file, "\taddu\t %s,%s,1\n", reg_names[26], reg_names[26]);
2336: fprintf (file, "\tst\t %s,%s,%slo16(%s+%d)\n", reg_names[26], reg_names[27],
2337: m88k_pound_sign, &block[1], 4 * blockno);
2338: }
2339:
2340: /* Determine whether a function argument is passed in a register, and
2341: which register.
2342:
2343: The arguments are CUM, which summarizes all the previous
2344: arguments; MODE, the machine mode of the argument; TYPE,
2345: the data type of the argument as a tree node or 0 if that is not known
2346: (which happens for C support library functions); and NAMED,
2347: which is 1 for an ordinary argument and 0 for nameless arguments that
2348: correspond to `...' in the called function's prototype.
2349:
2350: The value of the expression should either be a `reg' RTX for the
2351: hard register in which to pass the argument, or zero to pass the
2352: argument on the stack.
2353:
2354: On the m88000 the first eight words of args are normally in registers
2355: and the rest are pushed. Double precision floating point must be
2356: double word aligned (and if in a register, starting on an even
2357: register). Structures and unions which are not 4 byte, and word
2358: aligned are passed in memory rather than registers, even if they
2359: would fit completely in the registers under OCS rules.
2360:
2361: Note that FUNCTION_ARG and FUNCTION_INCOMING_ARG were different.
2362: For structures that are passed in memory, but could have been
2363: passed in registers, we first load the structure into the
2364: register, and then when the last argument is passed, we store
2365: the registers into the stack locations. This fixes some bugs
2366: where GCC did not expect to have register arguments, followed
2367: by stack arguments, followed by register arguments. */
2368:
2369: struct rtx_def *
2370: m88k_function_arg (args_so_far, mode, type, named)
2371: CUMULATIVE_ARGS args_so_far;
2372: enum machine_mode mode;
2373: tree type;
2374: int named;
2375: {
2376: int bytes, words;
2377:
2378: if (type != 0 /* undo putting struct in register */
2379: && (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE))
2380: mode = BLKmode;
2381:
2382: if (mode == BLKmode && TARGET_WARN_PASS_STRUCT)
2383: warning ("argument #%d is a structure", args_so_far + 1);
2384:
2385: if ((args_so_far & 1) != 0
2386: && (mode == DImode || mode == DFmode
2387: || (type != 0 && TYPE_ALIGN (type) > 32)))
2388: args_so_far++;
2389:
2390: #ifdef ESKIT
2391: if (no_reg_params)
2392: return (rtx) 0; /* don't put args in registers */
2393: #endif
2394:
2395: if (type == 0 && mode == BLKmode)
2396: abort (); /* m88k_function_arg argument `type' is NULL for BLKmode. */
2397:
2398: bytes = (mode != BLKmode) ? GET_MODE_SIZE (mode) : int_size_in_bytes (type);
2399: words = (bytes + 3) / 4;
2400:
2401: if (args_so_far + words > 8)
2402: return (rtx) 0; /* args have exhausted registers */
2403:
2404: else if (mode == BLKmode
2405: && (TYPE_ALIGN (type) != BITS_PER_WORD
2406: || bytes != UNITS_PER_WORD))
2407: return (rtx) 0;
2408:
2409: return gen_rtx (REG,
2410: ((mode == BLKmode) ? TYPE_MODE (type) : mode),
2411: 2 + args_so_far);
2412: }
2413:
2414: /* Do what is necessary for `va_start'. The argument is ignored;
2415: We look at the current function to determine if stdargs or varargs
2416: is used and fill in an initial va_list. A pointer to this constructor
2417: is returned. */
2418:
2419: struct rtx_def *
2420: m88k_builtin_saveregs (arglist)
2421: tree arglist;
2422: {
2423: rtx block, addr, argsize;
2424: tree fntype = TREE_TYPE (current_function_decl);
2425: int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
2426: && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
2427: != void_type_node)))
2428: ? -UNITS_PER_WORD : 0) + UNITS_PER_WORD - 1;
2429: int fixed;
2430: variable_args_p = 1;
2431:
2432: if (CONSTANT_P (current_function_arg_offset_rtx))
2433: {
2434: fixed = (XINT (current_function_arg_offset_rtx, 0)
2435: + argadj) / UNITS_PER_WORD;
2436: argsize = gen_rtx (CONST_INT, VOIDmode, fixed);
2437: }
2438: else
2439: {
2440: fixed = 0;
2441: argsize = plus_constant (current_function_arg_offset_rtx, argadj);
2442: argsize = expand_shift (RSHIFT_EXPR, Pmode, argsize,
2443: build_int_2 (2, 0), argsize, 0);
2444: }
2445:
2446: /* Allocate the va_list constructor */
1.1.1.2 ! root 2447: block = assign_stack_local (BLKmode, 3 * UNITS_PER_WORD, BITS_PER_WORD);
1.1 root 2448: RTX_UNCHANGING_P (block) = 1;
2449: RTX_UNCHANGING_P (XEXP (block, 0)) = 1;
2450:
2451: /* Store the argsize as the __va_arg member. */
2452: emit_move_insn (change_address (block, SImode, XEXP (block, 0)),
2453: argsize);
2454:
2455: /* Store the arg pointer in the __va_stk member. */
2456: emit_move_insn (change_address (block, Pmode,
2457: plus_constant (XEXP (block, 0),
2458: UNITS_PER_WORD)),
2459: copy_to_reg (virtual_incoming_args_rtx));
2460:
2461: /* Allocate the register space, and store it as the __va_reg member. */
2462: addr = assign_stack_local (BLKmode, 8 * UNITS_PER_WORD, -1);
2463: MEM_IN_STRUCT_P (addr) = 1;
2464: RTX_UNCHANGING_P (addr) = 1;
2465: RTX_UNCHANGING_P (XEXP (addr, 0)) = 1;
2466: emit_move_insn (change_address (block, Pmode,
2467: plus_constant (XEXP (block, 0),
2468: 2 * UNITS_PER_WORD)),
2469: copy_to_reg (XEXP (addr, 0)));
2470:
2471: /* Now store the incoming registers and return the address of the
2472: va_list constructor. */
2473: if (fixed < 8)
2474: move_block_from_reg
2475: (2 + fixed,
2476: change_address (addr, Pmode,
2477: plus_constant (XEXP (addr, 0),
2478: fixed * UNITS_PER_WORD)),
2479: 8 - fixed);
2480:
2481: return copy_to_reg (XEXP (block, 0));
2482: }
2483:
2484: /* If cmpsi has not been generated, emit code to do the test. Return the
2485: expression describing the test of operator OP. */
2486:
2487: rtx
2488: emit_test (op, mode)
2489: enum rtx_code op;
2490: enum machine_mode mode;
2491: {
2492: if (m88k_compare_reg == 0)
2493: emit_insn (gen_test (m88k_compare_op0, m88k_compare_op1));
2494: return (gen_rtx (op, mode, m88k_compare_reg, const0_rtx));
2495: }
2496:
2497: /* Determine how to best perform cmpsi/bxx, where cmpsi has a constant
2498: operand. All tests with zero (albeit swapped) and all equality tests
2499: with a constant are done with bcnd. The remaining cases are swapped
2500: as needed. */
2501:
2502: void
2503: emit_bcnd (op, label)
2504: enum rtx_code op;
2505: rtx label;
2506: {
2507: if (m88k_compare_op1 == const0_rtx)
2508: emit_jump_insn (optimize
2509: ? gen_bxx (emit_test (op, VOIDmode), label)
2510: : gen_bcnd (gen_rtx (op, VOIDmode,
2511: m88k_compare_op0, const0_rtx),
2512: label));
2513: else if (m88k_compare_op0 == const0_rtx)
2514: emit_jump_insn (optimize
2515: ? gen_bxx (emit_test (op, VOIDmode), label)
2516: : gen_bcnd (gen_rtx (swap_condition (op), VOIDmode,
2517: m88k_compare_op1, const0_rtx),
2518: label));
2519: else if (op != EQ && op != NE)
2520: emit_jump_insn (gen_bxx (emit_test (op, VOIDmode), label));
2521: else
2522: {
2523: rtx zero = gen_reg_rtx (SImode);
2524: rtx reg, constant;
2525: int value;
2526:
2527: if (GET_CODE (m88k_compare_op1) == CONST_INT)
2528: {
2529: reg = force_reg (SImode, m88k_compare_op0);
2530: constant = m88k_compare_op1;
2531: }
2532: else
2533: {
2534: reg = force_reg (SImode, m88k_compare_op1);
2535: constant = m88k_compare_op0;
2536: }
2537: value = INTVAL (constant);
2538:
2539: /* Perform an arithmetic computation to make the compared-to value
2540: zero, but avoid loosing if the bcnd is later changed into sxx. */
2541: if (SMALL_INTVAL (value))
2542: emit_jump_insn (gen_bxx (emit_test (op, VOIDmode), label));
2543: else
2544: {
2545: if (SMALL_INTVAL (-value))
2546: emit_insn (gen_addsi3 (zero, reg,
2547: gen_rtx (CONST_INT, VOIDmode, -value)));
2548: else
2549: emit_insn (gen_xorsi3 (zero, reg, constant));
2550:
2551: emit_jump_insn (gen_bcnd (gen_rtx (op, VOIDmode,
2552: zero, const0_rtx),
2553: label));
2554: }
2555: }
2556: }
2557:
2558: /* Print an operand. Recognize special options, documented below. */
2559:
2560: void
2561: print_operand (file, x, code)
2562: FILE *file;
2563: rtx x;
2564: char code;
2565: {
2566: enum rtx_code xc = (x ? GET_CODE (x) : UNKNOWN);
2567: register int value = (xc == CONST_INT ? INTVAL (x) : 0);
2568: static int sequencep;
2569: static int reversep;
2570:
2571: if (sequencep)
2572: {
2573: if (code < 'B' || code > 'E')
2574: output_operand_lossage ("%R not followed by %B/C/D/E");
2575: if (reversep)
2576: xc = reverse_condition (xc);
2577: sequencep = 0;
2578: }
2579:
2580: switch (code)
2581: {
2582: case '*': /* addressing base register for PIC */
2583: fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file); return;
2584:
2585: case '#': /* SVR4 pound-sign syntax character (empty if SVR3) */
2586: fputs (m88k_pound_sign, file); return;
2587:
2588: case 'X': /* print the upper 16 bits... */
2589: value >>= 16;
2590: case 'x': /* print the lower 16 bits of the integer constant in hex */
2591: if (xc != CONST_INT)
2592: output_operand_lossage ("invalid %x/X value");
2593: fprintf (file, "0x%x", value & 0xffff); return;
2594:
2595: case 'H': /* print the low 16 bits of the negated integer constant */
2596: if (xc != CONST_INT)
2597: output_operand_lossage ("invalid %H value");
2598: value = -value;
2599: case 'h': /* print the register or low 16 bits of the integer constant */
2600: if (xc == REG)
2601: goto reg;
2602: if (xc != CONST_INT)
2603: output_operand_lossage ("invalid %h value");
2604: fprintf (file, "%d", value & 0xffff);
2605: return;
2606:
2607: case 'Q': /* print the low 8 bits of the negated integer constant */
2608: if (xc != CONST_INT)
2609: output_operand_lossage ("invalid %Q value");
2610: value = -value;
2611: case 'q': /* print the register or low 8 bits of the integer constant */
2612: if (xc == REG)
2613: goto reg;
2614: if (xc != CONST_INT)
2615: output_operand_lossage ("invalid %q value");
2616: fprintf (file, "%d", value & 0xff);
2617: return;
2618:
2619: case 'w': /* print the integer constant (X == 32 ? 0 : 32 - X) */
2620: if (xc != CONST_INT)
2621: output_operand_lossage ("invalid %o value");
2622: fprintf (file, "%d", value == 32 ? 0 : 32 - value);
2623: return;
2624:
2625: case 'p': /* print the logarithm of the integer constant */
2626: if (xc != CONST_INT
2627: || (value = exact_log2 (value)) < 0)
2628: output_operand_lossage ("invalid %p value");
2629: fprintf (file, "%d", value);
2630: return;
2631:
2632: case 'S': /* compliment the value and then... */
2633: value = ~value;
2634: case 's': /* print the width and offset values forming the integer
2635: constant with a SET instruction. See integer_ok_for_set. */
2636: {
2637: register unsigned mask, uval = value;
2638: register int top, bottom;
2639:
2640: if (xc != CONST_INT)
2641: output_operand_lossage ("invalid %s/S value");
2642: /* All the "one" bits must be contiguous. If so, MASK will be
2643: a power of two or zero. */
2644: mask = (uval | (uval - 1)) + 1;
2645: if (!(uval && POWER_OF_2_or_0 (mask)))
2646: output_operand_lossage ("invalid %s/S value");
2647: top = mask ? exact_log2 (mask) : 32;
2648: bottom = exact_log2 (uval & ~(uval - 1));
2649: fprintf (file,"%d<%d>", top - bottom, bottom);
2650: return;
2651: }
2652:
2653: case 'P': /* print nothing if pc_rtx; output label_ref */
2654: if (xc == LABEL_REF)
2655: output_addr_const (file, x);
2656: else if (xc != PC)
2657: output_operand_lossage ("invalid %P operand");
2658: return;
2659:
2660: case 'L': /* print 0 or 1 if operand is label_ref and then... */
2661: fputc (xc == LABEL_REF ? '1' : '0', file);
2662: case '.': /* print .n if delay slot is used */
2663: fputs ((final_sequence
2664: && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))
2665: ? ".n\t" : "\t", file);
2666: return;
2667:
2668: case 'R': /* reverse the condition of the next print_operand
2669: if operand is a label_ref. */
2670: sequencep++;
2671: reversep = (xc == LABEL_REF);
2672: return;
2673:
2674: case 'B': /* bcnd branch values */
2675: fputs (m88k_pound_sign, file);
2676: switch (xc)
2677: {
2678: case EQ: fputs ("eq0", file); return;
2679: case NE: fputs ("ne0", file); return;
2680: case GT: fputs ("gt0", file); return;
2681: case LE: fputs ("le0", file); return;
2682: case LT: fputs ("lt0", file); return;
2683: case GE: fputs ("ge0", file); return;
2684: default: output_operand_lossage ("invalid %B value");
2685: }
2686:
2687: case 'C': /* bb0/bb1 branch values for comparisons */
2688: fputs (m88k_pound_sign, file);
2689: switch (xc)
2690: {
2691: case EQ: fputs ("eq", file); return;
2692: case NE: fputs ("ne", file); return;
2693: case GT: fputs ("gt", file); return;
2694: case LE: fputs ("le", file); return;
2695: case LT: fputs ("lt", file); return;
2696: case GE: fputs ("ge", file); return;
2697: case GTU: fputs ("hi", file); return;
2698: case LEU: fputs ("ls", file); return;
2699: case LTU: fputs ("lo", file); return;
2700: case GEU: fputs ("hs", file); return;
2701: default: output_operand_lossage ("invalid %C value");
2702: }
2703:
2704: case 'D': /* bcnd branch values for float comparisons */
2705: switch (xc)
2706: {
2707: case EQ: fputs ("0xa", file); return;
2708: case NE: fputs ("0x5", file); return;
2709: case GT: fputs (m88k_pound_sign, file);
2710: fputs ("gt0", file); return;
2711: case LE: fputs ("0xe", file); return;
2712: case LT: fputs ("0x4", file); return;
2713: case GE: fputs ("0xb", file); return;
2714: default: output_operand_lossage ("invalid %D value");
2715: }
2716:
2717: case 'E': /* bcnd branch values for special integers */
2718: switch (xc)
2719: {
2720: case EQ: fputs ("0x8", file); return;
2721: case NE: fputs ("0x7", file); return;
2722: default: output_operand_lossage ("invalid %E value");
2723: }
2724:
2725: case 'd': /* second register of a two register pair */
2726: if (xc != REG)
2727: output_operand_lossage ("`%d' operand isn't a register");
2728: fputs (reg_names[REGNO (x) + 1], file);
2729: return;
2730:
1.1.1.2 ! root 2731: case 'r': /* an immediate 0 should be represented as `r0' */
1.1 root 2732: if (x == const0_rtx)
2733: {
2734: fputs (reg_names[0], file);
2735: return;
2736: }
2737: else if (xc != REG)
2738: output_operand_lossage ("invalid %r value");
2739: case 0:
2740: name:
2741: if (xc == REG)
2742: {
2743: reg:
2744: if (REGNO (x) == ARG_POINTER_REGNUM)
2745: output_operand_lossage ("operand is r0");
2746: else
2747: fputs (reg_names[REGNO (x)], file);
2748: }
2749: else if (xc == PLUS)
2750: output_address (x);
2751: else if (xc == MEM)
2752: output_address (XEXP (x, 0));
2753: else if (xc == CONST_DOUBLE)
2754: output_operand_lossage ("operand is const_double");
2755: else
2756: output_addr_const (file, x);
2757: return;
2758:
2759: case 'g': /* append #got_rel as needed */
2760: if (flag_pic && (xc == SYMBOL_REF || xc == LABEL_REF))
2761: {
2762: output_addr_const (file, x);
2763: fputs ("#got_rel", file);
2764: return;
2765: }
2766: goto name;
2767:
2768: case 'a': /* (standard), assume operand is an address */
2769: case 'c': /* (standard), assume operand is an immediate value */
2770: case 'l': /* (standard), assume operand is a label_ref */
2771: case 'n': /* (standard), like %c, except negate first */
2772: default:
2773: output_operand_lossage ("invalid code");
2774: }
2775: }
2776:
2777: void
2778: print_operand_address (file, addr)
2779: FILE *file;
2780: rtx addr;
2781: {
2782: register rtx reg0, reg1, temp;
2783:
2784: switch (GET_CODE (addr))
2785: {
2786: case REG:
2787: if (REGNO (addr) == ARG_POINTER_REGNUM)
2788: abort ();
2789: else
2790: fprintf (file, "%s,%s", reg_names[0], reg_names [REGNO (addr)]);
2791: break;
2792:
2793: case LO_SUM:
2794: fprintf (file, "%s,%slo16(",
2795: reg_names[REGNO (XEXP (addr, 0))], m88k_pound_sign);
2796: output_addr_const (file, XEXP (addr, 1));
2797: fputc (')', file);
2798: break;
2799:
2800: case PLUS:
2801: reg0 = XEXP (addr, 0);
2802: reg1 = XEXP (addr, 1);
2803: if (GET_CODE (reg0) == MULT || GET_CODE (reg0) == CONST_INT)
2804: {
2805: rtx tmp = reg0;
2806: reg0 = reg1;
2807: reg1 = tmp;
2808: }
2809:
2810: if ((REG_P (reg0) && REGNO (reg0) == ARG_POINTER_REGNUM)
2811: || (REG_P (reg1) && REGNO (reg1) == ARG_POINTER_REGNUM))
2812: abort ();
2813:
2814: else if (REG_P (reg0))
2815: {
2816: if (REG_P (reg1))
2817: fprintf (file, "%s,%s",
2818: reg_names [REGNO (reg0)], reg_names [REGNO (reg1)]);
2819:
2820: else if (GET_CODE (reg1) == CONST_INT)
2821: fprintf (file, "%s,%d",
2822: reg_names [REGNO (reg0)], INTVAL (reg1));
2823:
2824: else if (GET_CODE (reg1) == MULT)
2825: {
2826: rtx mreg = XEXP (reg1, 0);
2827: if (REGNO (mreg) == ARG_POINTER_REGNUM)
2828: abort ();
2829:
2830: fprintf (file, "%s[%s]", reg_names[REGNO (reg0)],
2831: reg_names[REGNO (mreg)]);
2832: }
2833:
2834: else if (GET_CODE (reg1) == ZERO_EXTRACT)
2835: {
2836: fprintf (file, "%s,%slo16(",
2837: reg_names[REGNO (reg0)], m88k_pound_sign);
2838: output_addr_const (file, XEXP (reg1, 0));
2839: fputc (')', file);
2840: }
2841:
2842: else if (flag_pic)
2843: {
2844: fprintf (file, "%s,", reg_names[REGNO (reg0)]);
2845: output_addr_const (file, reg1);
2846: fputs ("#got_rel", file);
2847: }
2848: else abort ();
2849: }
2850:
2851: else
2852: abort ();
2853: break;
2854:
2855: case MULT:
2856: if (REGNO (XEXP (addr, 0)) == ARG_POINTER_REGNUM)
2857: abort ();
2858:
2859: fprintf (file, "%s[%s]",
2860: reg_names[0], reg_names[REGNO (XEXP (addr, 0))]);
2861: break;
2862:
2863: case LSHIFT:
2864: fprintf (file, "%s,%shi16(", reg_names[0], m88k_pound_sign);
2865: output_addr_const (file, XEXP (addr, 0));
2866: fputc (')', file);
2867: break;
2868:
2869: case CONST_INT:
2870: fprintf (file, "%s,%d", reg_names[0], INTVAL (addr));
2871: break;
2872:
2873: default:
2874: fprintf (file, "%s,", reg_names[0]);
2875: if (SHORT_ADDRESS_P (addr, temp))
2876: {
2877: fprintf (file, "%siw16(", m88k_pound_sign);
2878: output_addr_const (file, addr);
2879: fputc (')', file);
2880: }
2881: else
2882: output_addr_const (file, addr);
2883: }
2884: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.