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