|
|
1.1 root 1: /* Subroutines for insn-output.c for MIPS
2: Contributed by A. Lichnewsky, [email protected].
3: Changes by Michael Meissner, [email protected].
4: Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
5:
6: This file is part of GNU CC.
7:
8: GNU CC is free software; you can redistribute it and/or modify
9: it under the terms of the GNU General Public License as published by
10: the Free Software Foundation; either version 2, or (at your option)
11: any later version.
12:
13: GNU CC is distributed in the hope that it will be useful,
14: but WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: GNU General Public License for more details.
17:
18: You should have received a copy of the GNU General Public License
19: along with GNU CC; see the file COPYING. If not, write to
20: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21:
22: #include "config.h"
23: #include "rtl.h"
24: #include "regs.h"
25: #include "hard-reg-set.h"
26: #include "real.h"
27: #include "insn-config.h"
28: #include "conditions.h"
29: #include "insn-flags.h"
30: #include "insn-attr.h"
31: #include "insn-codes.h"
32: #include "recog.h"
33: #include "output.h"
34:
35: #undef MAX /* sys/param.h may also define these */
36: #undef MIN
37:
38: #include <stdio.h>
39: #include <signal.h>
40: #include <sys/types.h>
41: #include <sys/file.h>
42: #include <ctype.h>
43: #include "tree.h"
44: #include "expr.h"
45: #include "flags.h"
46:
47: #ifndef R_OK
48: #define R_OK 4
49: #define W_OK 2
50: #define X_OK 1
51: #endif
52:
53: #if defined(USG) || defined(NO_STAB_H)
54: #include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */
55: #else
56: #include <stab.h> /* On BSD, use the system's stab.h. */
57: #endif /* not USG */
58:
59: #ifdef __GNU_STAB__
60: #define STAB_CODE_TYPE enum __stab_debug_code
61: #else
62: #define STAB_CODE_TYPE int
63: #endif
64:
65: extern void abort ();
66: extern int atoi ();
67: extern char *getenv ();
68: extern char *mktemp ();
69:
70: extern rtx adj_offsettable_operand ();
71: extern rtx copy_to_reg ();
72: extern void error ();
73: extern void fatal ();
74: extern tree lookup_name ();
75: extern void pfatal_with_name ();
76: extern void warning ();
77:
78: extern tree current_function_decl;
79: extern FILE *asm_out_file;
80:
81: /* Enumeration for all of the relational tests, so that we can build
82: arrays indexed by the test type, and not worry about the order
83: of EQ, NE, etc. */
84:
85: enum internal_test {
86: ITEST_EQ,
87: ITEST_NE,
88: ITEST_GT,
89: ITEST_GE,
90: ITEST_LT,
91: ITEST_LE,
92: ITEST_GTU,
93: ITEST_GEU,
94: ITEST_LTU,
95: ITEST_LEU,
96: ITEST_MAX
97: };
98:
99: /* Global variables for machine-dependent things. */
100:
101: /* Threshold for data being put into the small data/bss area, instead
102: of the normal data area (references to the small data/bss area take
103: 1 instruction, and use the global pointer, references to the normal
104: data area takes 2 instructions). */
105: int mips_section_threshold = -1;
106:
107: /* Count the number of .file directives, so that .loc is up to date. */
108: int num_source_filenames = 0;
109:
110: /* Count the number of sdb related labels are generated (to find block
111: start and end boundaries). */
112: int sdb_label_count = 0;
113:
114: /* Next label # for each statment for Silicon Graphics IRIS systems. */
115: int sym_lineno = 0;
116:
117: /* Non-zero if inside of a function, because the stupid MIPS asm can't
118: handle .files inside of functions. */
119: int inside_function = 0;
120:
121: /* Files to separate the text and the data output, so that all of the data
122: can be emitted before the text, which will mean that the assembler will
123: generate smaller code, based on the global pointer. */
124: FILE *asm_out_data_file;
125: FILE *asm_out_text_file;
126:
127: /* Linked list of all externals that are to be emitted when optimizing
128: for the global pointer if they haven't been declared by the end of
129: the program with an appropriate .comm or initialization. */
130:
131: struct extern_list {
132: struct extern_list *next; /* next external */
133: char *name; /* name of the external */
134: int size; /* size in bytes */
135: } *extern_head = 0;
136:
137: /* Name of the file containing the current function. */
138: char *current_function_file = "";
139:
140: /* Warning given that Mips ECOFF can't support changing files
141: within a function. */
142: int file_in_function_warning = FALSE;
143:
144: /* Whether to suppress issuing .loc's because the user attempted
145: to change the filename within a function. */
146: int ignore_line_number = FALSE;
147:
148: /* Number of nested .set noreorder, noat, nomacro, and volatile requests. */
149: int set_noreorder;
150: int set_noat;
151: int set_nomacro;
152: int set_volatile;
153:
154: /* The next branch instruction is a branch likely, not branch normal. */
155: int mips_branch_likely;
156:
157: /* Count of delay slots and how many are filled. */
158: int dslots_load_total;
159: int dslots_load_filled;
160: int dslots_jump_total;
161: int dslots_jump_filled;
162:
163: /* # of nops needed by previous insn */
164: int dslots_number_nops;
165:
166: /* Number of 1/2/3 word references to data items (ie, not jal's). */
167: int num_refs[3];
168:
169: /* registers to check for load delay */
170: rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4;
171:
172: /* Cached operands, and operator to compare for use in set/branch on
173: condition codes. */
174: rtx branch_cmp[2];
175:
176: /* what type of branch to use */
177: enum cmp_type branch_type;
178:
179: /* Number of previously seen half-pic pointers and references. */
180: static int prev_half_pic_ptrs = 0;
181: static int prev_half_pic_refs = 0;
182:
183: /* which cpu are we scheduling for */
184: enum processor_type mips_cpu;
185:
186: /* which instruction set architecture to use. */
187: int mips_isa;
188:
189: /* Strings to hold which cpu and instruction set architecture to use. */
190: char *mips_cpu_string; /* for -mcpu=<xxx> */
191: char *mips_isa_string; /* for -mips{1,2,3} */
192:
193: /* Array to RTX class classification. At present, we care about
194: whether the operator is an add-type operator, or a divide/modulus,
195: and if divide/modulus, whether it is unsigned. This is for the
196: peephole code. */
197: char mips_rtx_classify[NUM_RTX_CODE];
198:
199: /* Array giving truth value on whether or not a given hard register
200: can support a given mode. */
201: char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
202:
203: /* Current frame information calculated by compute_frame_size. */
204: struct mips_frame_info current_frame_info;
205:
206: /* Zero structure to initialize current_frame_info. */
207: struct mips_frame_info zero_frame_info;
208:
209: /* Temporary filename used to buffer .text until end of program
210: for -mgpopt. */
211: static char *temp_filename;
212:
213: /* List of all MIPS punctuation characters used by print_operand. */
214: char mips_print_operand_punct[256];
215:
216: /* Map GCC register number to debugger register number. */
217: int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
218:
219: /* Buffer to use to enclose a load/store operation with %{ %} to
220: turn on .set volatile. */
221: static char volatile_buffer[60];
222:
223: /* Hardware names for the registers. If -mrnames is used, this
224: will be overwritten with mips_sw_reg_names. */
225:
226: char mips_reg_names[][8] =
227: {
228: "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
229: "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
230: "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
231: "$24", "$25", "$26", "$27", "$28", "$sp", "$fp", "$31",
232: "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
233: "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
234: "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
235: "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
236: "hi", "lo", "$fcr31"
237: };
238:
239: /* Mips software names for the registers, used to overwrite the
240: mips_reg_names array. */
241:
242: char mips_sw_reg_names[][8] =
243: {
244: "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
245: "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
246: "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
247: "t8", "t9", "k0", "k1", "gp", "sp", "$fp", "ra",
248: "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
249: "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
250: "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
251: "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
252: "hi", "lo", "$fcr31"
253: };
254:
255: /* Map hard register number to register class */
256: enum reg_class mips_regno_to_class[] =
257: {
258: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
259: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
260: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
261: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
262: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
263: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
264: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
265: GR_REGS, GR_REGS, GR_REGS, GR_REGS,
266: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
267: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
268: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
269: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
270: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
271: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
272: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
273: FP_REGS, FP_REGS, FP_REGS, FP_REGS,
274: HI_REG, LO_REG, ST_REGS
275: };
276:
277: /* Map register constraint character to register class. */
278: enum reg_class mips_char_to_class[256] =
279: {
280: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
281: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
282: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
283: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
284: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
285: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
286: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
287: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
288: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
289: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
290: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
291: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
292: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
293: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
294: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
295: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
296: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
297: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
298: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
299: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
300: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
301: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
302: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
303: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
304: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
305: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
306: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
307: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
308: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
309: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
310: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
311: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
312: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
313: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
314: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
315: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
316: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
317: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
318: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
319: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
320: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
321: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
322: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
323: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
324: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
325: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
326: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
327: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
328: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
329: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
330: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
331: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
332: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
333: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
334: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
335: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
336: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
337: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
338: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
339: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
340: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
341: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
342: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
343: NO_REGS, NO_REGS, NO_REGS, NO_REGS,
344: };
345:
346:
347: /* Return truth value of whether OP can be used as an operands
348: where a register or 16 bit unsigned integer is needed. */
349:
350: int
351: uns_arith_operand (op, mode)
352: rtx op;
353: enum machine_mode mode;
354: {
355: if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
356: return TRUE;
357:
358: return register_operand (op, mode);
359: }
360:
361: /* Return truth value of whether OP can be used as an operands
362: where a 16 bit integer is needed */
363:
364: int
365: arith_operand (op, mode)
366: rtx op;
367: enum machine_mode mode;
368: {
369: if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
370: return TRUE;
371:
372: return register_operand (op, mode);
373: }
374:
375: /* Return truth value of whether OP can be used as an operand in a two
376: address arithmetic insn (such as set 123456,%o4) of mode MODE. */
377:
378: int
379: arith32_operand (op, mode)
380: rtx op;
381: enum machine_mode mode;
382: {
383: if (GET_CODE (op) == CONST_INT)
384: return TRUE;
385:
386: return register_operand (op, mode);
387: }
388:
389: /* Return truth value of whether OP is a integer which fits in 16 bits */
390:
391: int
392: small_int (op, mode)
393: rtx op;
394: enum machine_mode mode;
395: {
396: return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
397: }
398:
399: /* Return truth value of whether OP is an integer which is too big to
400: be loaded with one instruction. */
401:
402: int
403: large_int (op, mode)
404: rtx op;
405: enum machine_mode mode;
406: {
407: HOST_WIDE_INT value;
408:
409: if (GET_CODE (op) != CONST_INT)
410: return FALSE;
411:
412: value = INTVAL (op);
413: if ((value & ~0x0000ffff) == 0) /* ior reg,$r0,value */
414: return FALSE;
415:
416: if (((unsigned long)(value + 32768)) <= 32767) /* subu reg,$r0,value */
417: return FALSE;
418:
419: if ((value & 0xffff0000) == value) /* lui reg,value>>16 */
420: return FALSE;
421:
422: return TRUE;
423: }
424:
425: /* Return truth value of whether OP is a register or the constant 0. */
426:
427: int
428: reg_or_0_operand (op, mode)
429: rtx op;
430: enum machine_mode mode;
431: {
432: switch (GET_CODE (op))
433: {
434: default:
435: break;
436:
437: case CONST_INT:
438: return (INTVAL (op) == 0);
439:
440: case CONST_DOUBLE:
441: if (CONST_DOUBLE_HIGH (op) != 0 || CONST_DOUBLE_LOW (op) != 0)
442: return FALSE;
443:
444: return TRUE;
445:
446: case REG:
447: case SUBREG:
448: return register_operand (op, mode);
449: }
450:
451: return FALSE;
452: }
453:
454: /* Return truth value of whether OP is one of the special multiply/divide
455: registers (hi, lo). */
456:
457: int
458: md_register_operand (op, mode)
459: rtx op;
460: enum machine_mode mode;
461: {
462: return (GET_MODE_CLASS (mode) == MODE_INT
463: && GET_CODE (op) == REG
464: && MD_REG_P (REGNO (op)));
465: }
466:
467: /* Return truth value of whether OP is the FP status register. */
468:
469: int
470: fpsw_register_operand (op, mode)
471: rtx op;
472: enum machine_mode mode;
473: {
474: return (GET_CODE (op) == REG && ST_REG_P (REGNO (op)));
475: }
476:
477: /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */
478:
479: int
480: mips_const_double_ok (op, mode)
481: rtx op;
482: enum machine_mode mode;
483: {
484: if (GET_CODE (op) != CONST_DOUBLE)
485: return FALSE;
486:
487: if (mode == DImode)
488: return TRUE;
489:
490: if (mode != SFmode && mode != DFmode)
491: return FALSE;
492:
493: if (CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0)
494: return TRUE;
495:
496: #if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
497: if (TARGET_MIPS_AS) /* gas doesn't like li.d/li.s yet */
498: {
499: union { double d; int i[2]; } u;
500: double d;
501:
502: u.i[0] = CONST_DOUBLE_LOW (op);
503: u.i[1] = CONST_DOUBLE_HIGH (op);
504: d = u.d;
505:
506: if (d != d)
507: return FALSE; /* NAN */
508:
509: if (d < 0.0)
510: d = - d;
511:
512: /* Rather than trying to get the accuracy down to the last bit,
513: just use approximate ranges. */
514:
515: if (mode == DFmode && d > 1.0e-300 && d < 1.0e300)
516: return TRUE;
517:
518: if (mode == SFmode && d > 1.0e-38 && d < 1.0e+38)
519: return TRUE;
520: }
521: #endif
522:
523: return FALSE;
524: }
525:
526: /* Return truth value if a memory operand fits in a single instruction
527: (ie, register + small offset). */
528:
529: int
530: simple_memory_operand (op, mode)
531: rtx op;
532: enum machine_mode mode;
533: {
534: rtx addr, plus0, plus1;
535:
536: /* Eliminate non-memory operations */
537: if (GET_CODE (op) != MEM)
538: return FALSE;
539:
540: /* dword operations really put out 2 instructions, so eliminate them. */
541: if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
542: return FALSE;
543:
544: /* Decode the address now. */
545: addr = XEXP (op, 0);
546: switch (GET_CODE (addr))
547: {
548: default:
549: break;
550:
551: case REG:
552: return TRUE;
553:
554: case CONST_INT:
555: return SMALL_INT (op);
556:
557: case PLUS:
558: plus0 = XEXP (addr, 0);
559: plus1 = XEXP (addr, 1);
560: if (GET_CODE (plus0) == REG
561: && GET_CODE (plus1) == CONST_INT
562: && SMALL_INT (plus1))
563: return TRUE;
564:
565: else if (GET_CODE (plus1) == REG
566: && GET_CODE (plus0) == CONST_INT
567: && SMALL_INT (plus0))
568: return TRUE;
569:
570: else
571: return FALSE;
572:
573: #if 0
574: /* We used to allow small symbol refs here (ie, stuff in .sdata
575: or .sbss), but this causes some bugs in G++. Also, it won't
576: interfere if the MIPS linker rewrites the store instruction
577: because the function is PIC. */
578:
579: case LABEL_REF: /* never gp relative */
580: break;
581:
582: case CONST:
583: /* If -G 0, we can never have a GP relative memory operation.
584: Also, save some time if not optimizing. */
585: if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT)
586: return FALSE;
587:
588: {
589: rtx offset = const0_rtx;
590: addr = eliminate_constant_term (addr, &offset);
591: if (GET_CODE (op) != SYMBOL_REF)
592: return FALSE;
593:
594: /* let's be paranoid.... */
595: if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff)
596: return FALSE;
597: }
598: /* fall through */
599:
600: case SYMBOL_REF:
601: return SYMBOL_REF_FLAG (addr);
602: #endif
603: }
604:
605: return FALSE;
606: }
607:
608: /* Return true if the code of this rtx pattern is EQ or NE. */
609:
610: int
611: equality_op (op, mode)
612: rtx op;
613: enum machine_mode mode;
614: {
615: if (mode != GET_MODE (op))
616: return FALSE;
617:
618: return (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0;
619: }
620:
621: /* Return true if the code is a relational operations (EQ, LE, etc.) */
622:
623: int
624: cmp_op (op, mode)
625: rtx op;
626: enum machine_mode mode;
627: {
628: if (mode != GET_MODE (op))
629: return FALSE;
630:
631: return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
632: }
633:
634:
635: /* Genrecog does not take the type of match_operator into consideration,
636: and would complain about two patterns being the same if the same
637: function is used, so make it believe they are different. */
638:
639: int
640: cmp2_op (op, mode)
641: rtx op;
642: enum machine_mode mode;
643: {
644: if (mode != GET_MODE (op))
645: return FALSE;
646:
647: return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
648: }
649:
650: /* Return true if the code is an unsigned relational operations (LEU, etc.) */
651:
652: int
653: uns_cmp_op (op,mode)
654: rtx op;
655: enum machine_mode mode;
656: {
657: if (mode != GET_MODE (op))
658: return FALSE;
659:
660: return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP;
661: }
662:
663: /* Return true if the code is a relational operation FP can use. */
664:
665: int
666: fcmp_op (op, mode)
667: rtx op;
668: enum machine_mode mode;
669: {
670: if (mode != GET_MODE (op))
671: return FALSE;
672:
673: return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;
674: }
675:
676:
677: /* Return true if the operand is either the PC or a label_ref. */
678:
679: int
680: pc_or_label_operand (op, mode)
681: rtx op;
682: enum machine_mode mode;
683: {
684: if (op == pc_rtx)
685: return TRUE;
686:
687: if (GET_CODE (op) == LABEL_REF)
688: return TRUE;
689:
690: return FALSE;
691: }
692:
693: /* Test for a valid operand for a call instruction.
694: Don't allow the arg pointer register or virtual regs
695: since they may change into reg + const, which the patterns
696: can't handle yet. */
697:
698: int
699: call_insn_operand (op, mode)
700: rtx op;
701: enum machine_mode mode;
702: {
703: if (GET_CODE (op) == MEM
704: && (CONSTANT_ADDRESS_P (XEXP (op, 0))
705: || (GET_CODE (XEXP (op, 0)) == REG
706: && XEXP (op, 0) != arg_pointer_rtx
707: && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
708: && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
709: return 1;
710: return 0;
711: }
712:
713: /* Return an operand string if the given instruction's delay slot or
714: wrap it in a .set noreorder section. This is for filling delay
715: slots on load type instructions under GAS, which does no reordering
716: on its own. For the MIPS assembler, all we do is update the filled
717: delay slot statistics.
718:
719: We assume that operands[0] is the target register that is set.
720:
721: In order to check the next insn, most of this functionality is moved
722: to FINAL_PRESCAN_INSN, and we just set the global variables that
723: it needs. */
724:
725: char *
726: mips_fill_delay_slot (ret, type, operands, cur_insn)
727: char *ret; /* normal string to return */
728: enum delay_type type; /* type of delay */
729: rtx operands[]; /* operands to use */
730: rtx cur_insn; /* current insn */
731: {
732: register rtx set_reg;
733: register enum machine_mode mode;
734: register rtx next_insn = (cur_insn) ? NEXT_INSN (cur_insn) : (rtx)0;
735: register int num_nops;
736:
737: if (type == DELAY_LOAD || type == DELAY_FCMP)
738: num_nops = 1;
739:
740: else if (type == DELAY_HILO)
741: num_nops = 2;
742:
743: else
744: num_nops = 0;
745:
746: /* Make sure that we don't put nop's after labels. */
747: next_insn = NEXT_INSN (cur_insn);
748: while (next_insn != (rtx)0 && GET_CODE (next_insn) == NOTE)
749: next_insn = NEXT_INSN (next_insn);
750:
751: dslots_load_total += num_nops;
752: if (TARGET_DEBUG_F_MODE
753: || !optimize
754: || type == DELAY_NONE
755: || operands == (rtx *)0
756: || cur_insn == (rtx)0
757: || next_insn == (rtx)0
758: || GET_CODE (next_insn) == CODE_LABEL
759: || (set_reg = operands[0]) == (rtx)0)
760: {
761: dslots_number_nops = 0;
762: mips_load_reg = (rtx)0;
763: mips_load_reg2 = (rtx)0;
764: mips_load_reg3 = (rtx)0;
765: mips_load_reg4 = (rtx)0;
766: return ret;
767: }
768:
769: set_reg = operands[0];
770: if (set_reg == (rtx)0)
771: return ret;
772:
773: while (GET_CODE (set_reg) == SUBREG)
774: set_reg = SUBREG_REG (set_reg);
775:
776: mode = GET_MODE (set_reg);
777: dslots_number_nops = num_nops;
778: mips_load_reg = set_reg;
779: mips_load_reg2 = (mode == DImode || mode == DFmode)
780: ? gen_rtx (REG, SImode, REGNO (set_reg) + 1)
781: : (rtx)0;
782:
783: if (type == DELAY_HILO)
784: {
785: mips_load_reg3 = gen_rtx (REG, SImode, MD_REG_FIRST);
786: mips_load_reg4 = gen_rtx (REG, SImode, MD_REG_FIRST+1);
787: }
788: else
789: {
790: mips_load_reg3 = 0;
791: mips_load_reg4 = 0;
792: }
793:
794: if (TARGET_GAS && set_noreorder++ == 0)
795: fputs ("\t.set\tnoreorder\n", asm_out_file);
796:
797: return ret;
798: }
799:
800:
801: /* Determine whether a memory reference takes one (based off of the GP pointer),
802: two (normal), or three (label + reg) instructions, and bump the appropriate
803: counter for -mstats. */
804:
805: void
806: mips_count_memory_refs (op, num)
807: rtx op;
808: int num;
809: {
810: int additional = 0;
811: int n_words = 0;
812: rtx addr, plus0, plus1;
813: enum rtx_code code0, code1;
814: int looping;
815:
816: if (TARGET_DEBUG_B_MODE)
817: {
818: fprintf (stderr, "\n========== mips_count_memory_refs:\n");
819: debug_rtx (op);
820: }
821:
822: /* Skip MEM if passed, otherwise handle movsi of address. */
823: addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
824:
825: /* Loop, going through the address RTL */
826: do
827: {
828: looping = FALSE;
829: switch (GET_CODE (addr))
830: {
831: default:
832: break;
833:
834: case REG:
835: case CONST_INT:
836: break;
837:
838: case PLUS:
839: plus0 = XEXP (addr, 0);
840: plus1 = XEXP (addr, 1);
841: code0 = GET_CODE (plus0);
842: code1 = GET_CODE (plus1);
843:
844: if (code0 == REG)
845: {
846: additional++;
847: addr = plus1;
848: looping = TRUE;
849: continue;
850: }
851:
852: if (code0 == CONST_INT)
853: {
854: addr = plus1;
855: looping = TRUE;
856: continue;
857: }
858:
859: if (code1 == REG)
860: {
861: additional++;
862: addr = plus0;
863: looping = TRUE;
864: continue;
865: }
866:
867: if (code1 == CONST_INT)
868: {
869: addr = plus0;
870: looping = TRUE;
871: continue;
872: }
873:
874: if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
875: {
876: addr = plus0;
877: looping = TRUE;
878: continue;
879: }
880:
881: if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
882: {
883: addr = plus1;
884: looping = TRUE;
885: continue;
886: }
887:
888: break;
889:
890: case LABEL_REF:
891: n_words = 2; /* always 2 words */
892: break;
893:
894: case CONST:
895: addr = XEXP (addr, 0);
896: looping = TRUE;
897: continue;
898:
899: case SYMBOL_REF:
900: n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
901: break;
902: }
903: }
904: while (looping);
905:
906: if (n_words == 0)
907: return;
908:
909: n_words += additional;
910: if (n_words > 3)
911: n_words = 3;
912:
913: num_refs[n_words-1] += num;
914: }
915:
916:
917: /* Return the appropriate instructions to move one operand to another. */
918:
919: char *
920: mips_move_1word (operands, insn, unsignedp)
921: rtx operands[];
922: rtx insn;
923: int unsignedp;
924: {
925: char *ret = 0;
926: rtx op0 = operands[0];
927: rtx op1 = operands[1];
928: enum rtx_code code0 = GET_CODE (op0);
929: enum rtx_code code1 = GET_CODE (op1);
930: enum machine_mode mode = GET_MODE (op0);
931: int subreg_word0 = 0;
932: int subreg_word1 = 0;
933: enum delay_type delay = DELAY_NONE;
934:
935: while (code0 == SUBREG)
936: {
937: subreg_word0 += SUBREG_WORD (op0);
938: op0 = SUBREG_REG (op0);
939: code0 = GET_CODE (op0);
940: }
941:
942: while (code1 == SUBREG)
943: {
944: subreg_word1 += SUBREG_WORD (op1);
945: op1 = SUBREG_REG (op1);
946: code1 = GET_CODE (op1);
947: }
948:
949: if (code0 == REG)
950: {
951: int regno0 = REGNO (op0) + subreg_word0;
952:
953: if (code1 == REG)
954: {
955: int regno1 = REGNO (op1) + subreg_word1;
956:
957: /* Just in case, don't do anything for assigning a register
958: to itself, unless we are filling a delay slot. */
959: if (regno0 == regno1 && set_nomacro == 0)
960: ret = "";
961:
962: else if (GP_REG_P (regno0))
963: {
964: if (GP_REG_P (regno1))
965: ret = "move\t%0,%1";
966:
967: else if (MD_REG_P (regno1))
968: {
969: delay = DELAY_HILO;
970: ret = "mf%1\t%0";
971: }
972:
973: else
974: {
975: delay = DELAY_LOAD;
976: if (FP_REG_P (regno1))
977: ret = "mfc1\t%0,%1";
978:
979: else if (regno1 == FPSW_REGNUM)
980: ret = "cfc1\t%0,$31";
981: }
982: }
983:
984: else if (FP_REG_P (regno0))
985: {
986: if (GP_REG_P (regno1))
987: {
988: delay = DELAY_LOAD;
989: ret = "mtc1\t%1,%0";
990: }
991:
992: if (FP_REG_P (regno1))
993: ret = "mov.s\t%0,%1";
994: }
995:
996: else if (MD_REG_P (regno0))
997: {
998: if (GP_REG_P (regno1))
999: {
1000: delay = DELAY_HILO;
1001: ret = "mt%0\t%1";
1002: }
1003: }
1004:
1005: else if (regno0 == FPSW_REGNUM)
1006: {
1007: if (GP_REG_P (regno1))
1008: {
1009: delay = DELAY_LOAD;
1010: ret = "ctc1\t%0,$31";
1011: }
1012: }
1013: }
1014:
1015: else if (code1 == MEM)
1016: {
1017: delay = DELAY_LOAD;
1018:
1019: if (TARGET_STATS)
1020: mips_count_memory_refs (op1, 1);
1021:
1022: if (GP_REG_P (regno0))
1023: {
1024: /* For loads, use the mode of the memory item, instead of the
1025: target, so zero/sign extend can use this code as well. */
1026: switch (GET_MODE (op1))
1027: {
1028: default: break;
1029: case SFmode: ret = "lw\t%0,%1"; break;
1030: case SImode: ret = "lw\t%0,%1"; break;
1031: case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break;
1032: case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break;
1033: }
1034: }
1035:
1036: else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
1037: ret = "l.s\t%0,%1";
1038:
1039: if (ret != (char *)0 && MEM_VOLATILE_P (op1))
1040: {
1041: int i = strlen (ret);
1042: if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
1043: abort ();
1044:
1045: sprintf (volatile_buffer, "%%{%s%%}", ret);
1046: ret = volatile_buffer;
1047: }
1048: }
1049:
1050: else if (code1 == CONST_INT)
1051: {
1052: if (INTVAL (op1) == 0)
1053: {
1054: if (GP_REG_P (regno0))
1055: ret = "move\t%0,%z1";
1056:
1057: else if (FP_REG_P (regno0))
1058: {
1059: delay = DELAY_LOAD;
1060: ret = "mtc1\t%z1,%0";
1061: }
1062: }
1063:
1064: else if (GP_REG_P (regno0))
1065: ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1";
1066: }
1067:
1068: else if (code1 == CONST_DOUBLE && mode == SFmode)
1069: {
1070: if (CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
1071: {
1072: if (GP_REG_P (regno0))
1073: ret = "move\t%0,%.";
1074:
1075: else if (FP_REG_P (regno0))
1076: {
1077: delay = DELAY_LOAD;
1078: ret = "mtc1\t%.,%0";
1079: }
1080: }
1081:
1082: else
1083: {
1084: delay = DELAY_LOAD;
1085: ret = "li.s\t%0,%1";
1086: }
1087: }
1088:
1089: else if (code1 == LABEL_REF)
1090: {
1091: if (TARGET_STATS)
1092: mips_count_memory_refs (op1, 1);
1093:
1094: ret = "la\t%0,%a1";
1095: }
1096:
1097: else if (code1 == SYMBOL_REF || code1 == CONST)
1098: {
1099: if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1))
1100: {
1101: rtx offset = const0_rtx;
1102:
1103: if (GET_CODE (op1) == CONST)
1104: op1 = eliminate_constant_term (XEXP (op1, 0), &offset);
1105:
1106: if (GET_CODE (op1) == SYMBOL_REF)
1107: {
1108: operands[2] = HALF_PIC_PTR (op1);
1109:
1110: if (TARGET_STATS)
1111: mips_count_memory_refs (operands[2], 1);
1112:
1113: if (INTVAL (offset) == 0)
1114: {
1115: delay = DELAY_LOAD;
1116: ret = "lw\t%0,%2";
1117: }
1118: else
1119: {
1120: dslots_load_total++;
1121: operands[3] = offset;
1122: ret = (SMALL_INT (offset))
1123: ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
1124: : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]";
1125: }
1126: }
1127: }
1128: else
1129: {
1130: if (TARGET_STATS)
1131: mips_count_memory_refs (op1, 1);
1132:
1133: ret = "la\t%0,%a1";
1134: }
1135: }
1136:
1137: else if (code1 == PLUS)
1138: {
1139: rtx add_op0 = XEXP (op1, 0);
1140: rtx add_op1 = XEXP (op1, 1);
1141:
1142: if (GET_CODE (XEXP (op1, 1)) == REG && GET_CODE (XEXP (op1, 0)) == CONST_INT)
1143: {
1144: add_op0 = XEXP (op1, 1); /* reverse operands */
1145: add_op1 = XEXP (op1, 0);
1146: }
1147:
1148: operands[2] = add_op0;
1149: operands[3] = add_op1;
1150: ret = "add%:\t%0,%2,%3";
1151: }
1152: }
1153:
1154: else if (code0 == MEM)
1155: {
1156: if (TARGET_STATS)
1157: mips_count_memory_refs (op0, 1);
1158:
1159: if (code1 == REG)
1160: {
1161: int regno1 = REGNO (op1) + subreg_word1;
1162:
1163: if (GP_REG_P (regno1))
1164: {
1165: switch (mode)
1166: {
1167: default: break;
1168: case SFmode: ret = "sw\t%1,%0"; break;
1169: case SImode: ret = "sw\t%1,%0"; break;
1170: case HImode: ret = "sh\t%1,%0"; break;
1171: case QImode: ret = "sb\t%1,%0"; break;
1172: }
1173: }
1174:
1175: else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
1176: ret = "s.s\t%1,%0";
1177: }
1178:
1179: else if (code1 == CONST_INT && INTVAL (op1) == 0)
1180: {
1181: switch (mode)
1182: {
1183: default: break;
1184: case SFmode: ret = "sw\t%z1,%0"; break;
1185: case SImode: ret = "sw\t%z1,%0"; break;
1186: case HImode: ret = "sh\t%z1,%0"; break;
1187: case QImode: ret = "sb\t%z1,%0"; break;
1188: }
1189: }
1190:
1191: else if (code1 == CONST_DOUBLE && CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
1192: {
1193: switch (mode)
1194: {
1195: default: break;
1196: case SFmode: ret = "sw\t%.,%0"; break;
1197: case SImode: ret = "sw\t%.,%0"; break;
1198: case HImode: ret = "sh\t%.,%0"; break;
1199: case QImode: ret = "sb\t%.,%0"; break;
1200: }
1201: }
1202:
1203: if (ret != (char *)0 && MEM_VOLATILE_P (op0))
1204: {
1205: int i = strlen (ret);
1206: if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
1207: abort ();
1208:
1209: sprintf (volatile_buffer, "%%{%s%%}", ret);
1210: ret = volatile_buffer;
1211: }
1212: }
1213:
1214: if (ret == (char *)0)
1215: {
1216: abort_with_insn (insn, "Bad move");
1217: return 0;
1218: }
1219:
1220: if (delay != DELAY_NONE)
1221: return mips_fill_delay_slot (ret, delay, operands, insn);
1222:
1223: return ret;
1224: }
1225:
1226:
1227: /* Return the appropriate instructions to move 2 words */
1228:
1229: char *
1230: mips_move_2words (operands, insn)
1231: rtx operands[];
1232: rtx insn;
1233: {
1234: char *ret = 0;
1235: rtx op0 = operands[0];
1236: rtx op1 = operands[1];
1237: enum rtx_code code0 = GET_CODE (operands[0]);
1238: enum rtx_code code1 = GET_CODE (operands[1]);
1239: int subreg_word0 = 0;
1240: int subreg_word1 = 0;
1241: enum delay_type delay = DELAY_NONE;
1242:
1243: while (code0 == SUBREG)
1244: {
1245: subreg_word0 += SUBREG_WORD (op0);
1246: op0 = SUBREG_REG (op0);
1247: code0 = GET_CODE (op0);
1248: }
1249:
1250: while (code1 == SUBREG)
1251: {
1252: subreg_word1 += SUBREG_WORD (op1);
1253: op1 = SUBREG_REG (op1);
1254: code1 = GET_CODE (op1);
1255: }
1256:
1257: if (code0 == REG)
1258: {
1259: int regno0 = REGNO (op0) + subreg_word0;
1260:
1261: if (code1 == REG)
1262: {
1263: int regno1 = REGNO (op1) + subreg_word1;
1264:
1265: /* Just in case, don't do anything for assigning a register
1266: to itself, unless we are filling a delay slot. */
1267: if (regno0 == regno1 && set_nomacro == 0)
1268: ret = "";
1269:
1270: else if (FP_REG_P (regno0))
1271: {
1272: if (FP_REG_P (regno1))
1273: ret = "mov.d\t%0,%1";
1274:
1275: else
1276: {
1277: delay = DELAY_LOAD;
1278: ret = (TARGET_FLOAT64)
1279: ? "dmtc1\t%1,%0"
1280: : "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
1281: }
1282: }
1283:
1284: else if (FP_REG_P (regno1))
1285: {
1286: delay = DELAY_LOAD;
1287: ret = (TARGET_FLOAT64)
1288: ? "dmfc1\t%0,%1"
1289: : "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
1290: }
1291:
1292: else if (MD_REG_P (regno0) && GP_REG_P (regno1))
1293: {
1294: delay = DELAY_HILO;
1295: ret = "mthi\t%M1\n\tmtlo\t%L1";
1296: }
1297:
1298: else if (GP_REG_P (regno0) && MD_REG_P (regno1))
1299: {
1300: delay = DELAY_HILO;
1301: ret = "mfhi\t%M0\n\tmflo\t%L0";
1302: }
1303:
1304: else if (regno0 != (regno1+1))
1305: ret = "move\t%0,%1\n\tmove\t%D0,%D1";
1306:
1307: else
1308: ret = "move\t%D0,%D1\n\tmove\t%0,%1";
1309: }
1310:
1311: else if (code1 == CONST_DOUBLE)
1312: {
1313: if (CONST_DOUBLE_HIGH (op1) != 0 || CONST_DOUBLE_LOW (op1) != 0)
1314: {
1315: if (GET_MODE (op1) == DFmode)
1316: {
1317: delay = DELAY_LOAD;
1318: ret = "li.d\t%0,%1";
1319: }
1320:
1321: else
1322: {
1323: operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
1324: operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
1325: ret = "li\t%M0,%3\n\tli\t%L0,%2";
1326: }
1327: }
1328:
1329: else
1330: {
1331: if (GP_REG_P (regno0))
1332: ret = "move\t%0,%.\n\tmove\t%D0,%.";
1333:
1334: else if (FP_REG_P (regno0))
1335: {
1336: delay = DELAY_LOAD;
1337: ret = (TARGET_FLOAT64)
1338: ? "dmtc1\t%.,%0"
1339: : "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
1340: }
1341: }
1342: }
1343:
1344: else if (code1 == CONST_INT && INTVAL (op1) == 0)
1345: {
1346: if (GP_REG_P (regno0))
1347: ret = "move\t%0,%.\n\tmove\t%D0,%.";
1348:
1349: else if (FP_REG_P (regno0))
1350: {
1351: delay = DELAY_LOAD;
1352: ret = (TARGET_FLOAT64)
1353: ? "dmtc1\t%.,%0"
1354: : "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
1355: }
1356: }
1357:
1358: else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0))
1359: {
1360: operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
1361: ret = "li\t%M0,%2\n\tli\t%L0,%1";
1362: }
1363:
1364: else if (code1 == MEM)
1365: {
1366: delay = DELAY_LOAD;
1367:
1368: if (TARGET_STATS)
1369: mips_count_memory_refs (op1, 2);
1370:
1371: if (FP_REG_P (regno0))
1372: ret = "l.d\t%0,%1";
1373:
1374: else if (offsettable_address_p (1, DFmode, XEXP (op1, 0)))
1375: {
1376: operands[2] = adj_offsettable_operand (op1, 4);
1377: if (reg_mentioned_p (op0, op1))
1378: ret = "lw\t%D0,%2\n\tlw\t%0,%1";
1379: else
1380: ret = "lw\t%0,%1\n\tlw\t%D0,%2";
1381: }
1382:
1383: if (ret != (char *)0 && MEM_VOLATILE_P (op1))
1384: {
1385: int i = strlen (ret);
1386: if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
1387: abort ();
1388:
1389: sprintf (volatile_buffer, "%%{%s%%}", ret);
1390: ret = volatile_buffer;
1391: }
1392: }
1393: }
1394:
1395: else if (code0 == MEM)
1396: {
1397: if (code1 == REG)
1398: {
1399: int regno1 = REGNO (op1) + subreg_word1;
1400:
1401: if (FP_REG_P (regno1))
1402: ret = "s.d\t%1,%0";
1403:
1404: else if (offsettable_address_p (1, DFmode, XEXP (op0, 0)))
1405: {
1406: operands[2] = adj_offsettable_operand (op0, 4);
1407: ret = "sw\t%1,%0\n\tsw\t%D1,%2";
1408: }
1409: }
1410:
1411: else if (code1 == CONST_DOUBLE
1412: && CONST_DOUBLE_HIGH (op1) == 0
1413: && CONST_DOUBLE_LOW (op1) == 0
1414: && offsettable_address_p (1, DFmode, XEXP (op0, 0)))
1415: {
1416: if (TARGET_FLOAT64)
1417: ret = "sd\t%.,%0";
1418: else
1419: {
1420: operands[2] = adj_offsettable_operand (op0, 4);
1421: ret = "sw\t%.,%0\n\tsw\t%.,%2";
1422: }
1423: }
1424:
1425: if (TARGET_STATS)
1426: mips_count_memory_refs (op0, 2);
1427:
1428: if (ret != (char *)0 && MEM_VOLATILE_P (op0))
1429: {
1430: int i = strlen (ret);
1431: if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
1432: abort ();
1433:
1434: sprintf (volatile_buffer, "%%{%s%%}", ret);
1435: ret = volatile_buffer;
1436: }
1437: }
1438:
1439: if (ret == (char *)0)
1440: {
1441: abort_with_insn (insn, "Bad move");
1442: return 0;
1443: }
1444:
1445: if (delay != DELAY_NONE)
1446: return mips_fill_delay_slot (ret, delay, operands, insn);
1447:
1448: return ret;
1449: }
1450:
1451:
1452: /* Provide the costs of an addressing mode that contains ADDR.
1453: If ADDR is not a valid address, its cost is irrelevant. */
1454:
1455: int
1456: mips_address_cost (addr)
1457: rtx addr;
1458: {
1459: switch (GET_CODE (addr))
1460: {
1461: default:
1462: break;
1463:
1464: case LO_SUM:
1465: case HIGH:
1466: return 1;
1467:
1468: case LABEL_REF:
1469: return 2;
1470:
1471: case CONST:
1472: {
1473: rtx offset = const0_rtx;
1474: addr = eliminate_constant_term (addr, &offset);
1475: if (GET_CODE (addr) == LABEL_REF)
1476: return 2;
1477:
1478: if (GET_CODE (addr) != SYMBOL_REF)
1479: return 4;
1480:
1481: if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)
1482: return 2;
1483: }
1484: /* fall through */
1485:
1486: case SYMBOL_REF:
1487: return SYMBOL_REF_FLAG (addr) ? 1 : 2;
1488:
1489: case PLUS:
1490: {
1491: register rtx plus0 = XEXP (addr, 0);
1492: register rtx plus1 = XEXP (addr, 1);
1493:
1494: if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
1495: {
1496: plus0 = XEXP (addr, 1);
1497: plus1 = XEXP (addr, 0);
1498: }
1499:
1500: if (GET_CODE (plus0) != REG)
1501: break;
1502:
1503: switch (GET_CODE (plus1))
1504: {
1505: default:
1506: break;
1507:
1508: case CONST_INT:
1509: {
1510: int value = INTVAL (plus1);
1511: return (value < -32768 || value > 32767) ? 2 : 1;
1512: }
1513:
1514: case CONST:
1515: case SYMBOL_REF:
1516: case LABEL_REF:
1517: case HIGH:
1518: case LO_SUM:
1519: return mips_address_cost (plus1) + 1;
1520: }
1521: }
1522: }
1523:
1524: return 4;
1525: }
1526:
1527:
1528: /* Make normal rtx_code into something we can index from an array */
1529:
1530: static enum internal_test
1531: map_test_to_internal_test (test_code)
1532: enum rtx_code test_code;
1533: {
1534: enum internal_test test = ITEST_MAX;
1535:
1536: switch (test_code)
1537: {
1538: default: break;
1539: case EQ: test = ITEST_EQ; break;
1540: case NE: test = ITEST_NE; break;
1541: case GT: test = ITEST_GT; break;
1542: case GE: test = ITEST_GE; break;
1543: case LT: test = ITEST_LT; break;
1544: case LE: test = ITEST_LE; break;
1545: case GTU: test = ITEST_GTU; break;
1546: case GEU: test = ITEST_GEU; break;
1547: case LTU: test = ITEST_LTU; break;
1548: case LEU: test = ITEST_LEU; break;
1549: }
1550:
1551: return test;
1552: }
1553:
1554:
1555: /* Generate the code to compare two integer values. The return value is:
1556: (reg:SI xx) The pseudo register the comparison is in
1557: (rtx)0 No register, generate a simple branch. */
1558:
1559: rtx
1560: gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
1561: enum rtx_code test_code; /* relational test (EQ, etc) */
1562: rtx result; /* result to store comp. or 0 if branch */
1563: rtx cmp0; /* first operand to compare */
1564: rtx cmp1; /* second operand to compare */
1565: int *p_invert; /* NULL or ptr to hold whether branch needs */
1566: /* to reverse its test */
1567: {
1568: struct cmp_info {
1569: enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */
1570: int const_low; /* low bound of constant we can accept */
1571: int const_high; /* high bound of constant we can accept */
1572: int const_add; /* constant to add (convert LE -> LT) */
1573: int reverse_regs; /* reverse registers in test */
1574: int invert_const; /* != 0 if invert value if cmp1 is constant */
1575: int invert_reg; /* != 0 if invert value if cmp1 is register */
1576: int unsignedp; /* != 0 for unsigned comparisons. */
1577: };
1578:
1579: static struct cmp_info info[ (int)ITEST_MAX ] = {
1580:
1581: { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */
1582: { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */
1583: { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */
1584: { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */
1585: { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */
1586: { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */
1587: { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */
1588: { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */
1589: { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */
1590: { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */
1591: };
1592:
1593: enum internal_test test;
1594: struct cmp_info *p_info;
1595: int branch_p;
1596: int eqne_p;
1597: int invert;
1598: rtx reg;
1599: rtx reg2;
1600:
1601: test = map_test_to_internal_test (test_code);
1602: if (test == ITEST_MAX)
1603: abort ();
1604:
1605: p_info = &info[ (int)test ];
1606: eqne_p = (p_info->test_code == XOR);
1607:
1608: /* Eliminate simple branches */
1609: branch_p = (result == (rtx)0);
1610: if (branch_p)
1611: {
1612: if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
1613: {
1614: /* Comparisons against zero are simple branches */
1615: if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
1616: return (rtx)0;
1617:
1618: /* Test for beq/bne. */
1619: if (eqne_p)
1620: return (rtx)0;
1621: }
1622:
1623: /* allocate a pseudo to calculate the value in. */
1624: result = gen_reg_rtx (SImode);
1625: }
1626:
1627: /* Make sure we can handle any constants given to us. */
1628: if (GET_CODE (cmp0) == CONST_INT)
1629: cmp0 = force_reg (SImode, cmp0);
1630:
1631: if (GET_CODE (cmp1) == CONST_INT)
1632: {
1633: HOST_WIDE_INT value = INTVAL (cmp1);
1634: if (value < p_info->const_low || value > p_info->const_high)
1635: cmp1 = force_reg (SImode, cmp1);
1636: }
1637:
1638: /* See if we need to invert the result. */
1639: invert = (GET_CODE (cmp1) == CONST_INT)
1640: ? p_info->invert_const
1641: : p_info->invert_reg;
1642:
1643: if (p_invert != (int *)0)
1644: {
1645: *p_invert = invert;
1646: invert = FALSE;
1647: }
1648:
1649: /* Comparison to constants, may involve adding 1 to change a LT into LE.
1650: Comparison between two registers, may involve switching operands. */
1651: if (GET_CODE (cmp1) == CONST_INT)
1652: {
1653: if (p_info->const_add != 0)
1654: {
1655: HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
1656: /* If modification of cmp1 caused overflow,
1657: we would get the wrong answer if we follow the usual path;
1658: thus, x > 0xffffffffu would turn into x > 0u. */
1659: if ((p_info->unsignedp
1660: ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1)
1661: : new > INTVAL (cmp1))
1662: != (p_info->const_add > 0))
1663: /* 1 is the right value in the LE and LEU case.
1664: In the GT and GTU case, *p_invert is already set,
1665: so this is effectively 0. */
1666: return force_reg (SImode, const1_rtx);
1667: else
1668: cmp1 = GEN_INT (new);
1669: }
1670: }
1671: else if (p_info->reverse_regs)
1672: {
1673: rtx temp = cmp0;
1674: cmp0 = cmp1;
1675: cmp1 = temp;
1676: }
1677:
1678: if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
1679: reg = cmp0;
1680: else
1681: {
1682: reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result;
1683: emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1));
1684: }
1685:
1686: if (test == ITEST_NE)
1687: {
1688: emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx));
1689: invert = FALSE;
1690: }
1691:
1692: else if (test == ITEST_EQ)
1693: {
1694: reg2 = (invert) ? gen_reg_rtx (SImode) : result;
1695: emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx));
1696: reg = reg2;
1697: }
1698:
1699: if (invert)
1700: emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx));
1701:
1702: return result;
1703: }
1704:
1705:
1706: /* Emit the common code for doing conditional branches.
1707: operand[0] is the label to jump to.
1708: The comparison operands are saved away by cmp{si,sf,df}. */
1709:
1710: void
1711: gen_conditional_branch (operands, test_code)
1712: rtx operands[];
1713: enum rtx_code test_code;
1714: {
1715: static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = {
1716: { /* CMP_SI */
1717: SImode, /* eq */
1718: SImode, /* ne */
1719: SImode, /* gt */
1720: SImode, /* ge */
1721: SImode, /* lt */
1722: SImode, /* le */
1723: SImode, /* gtu */
1724: SImode, /* geu */
1725: SImode, /* ltu */
1726: SImode, /* leu */
1727: },
1728: { /* CMP_SF */
1729: CC_FPmode, /* eq */
1730: CC_REV_FPmode, /* ne */
1731: CC_FPmode, /* gt */
1732: CC_FPmode, /* ge */
1733: CC_FPmode, /* lt */
1734: CC_FPmode, /* le */
1735: VOIDmode, /* gtu */
1736: VOIDmode, /* geu */
1737: VOIDmode, /* ltu */
1738: VOIDmode, /* leu */
1739: },
1740: { /* CMP_DF */
1741: CC_FPmode, /* eq */
1742: CC_REV_FPmode, /* ne */
1743: CC_FPmode, /* gt */
1744: CC_FPmode, /* ge */
1745: CC_FPmode, /* lt */
1746: CC_FPmode, /* le */
1747: VOIDmode, /* gtu */
1748: VOIDmode, /* geu */
1749: VOIDmode, /* ltu */
1750: VOIDmode, /* leu */
1751: },
1752: };
1753:
1754: enum machine_mode mode;
1755: enum cmp_type type = branch_type;
1756: rtx cmp0 = branch_cmp[0];
1757: rtx cmp1 = branch_cmp[1];
1758: rtx label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
1759: rtx label2 = pc_rtx;
1760: rtx reg = (rtx)0;
1761: int invert = 0;
1762: enum internal_test test = map_test_to_internal_test (test_code);
1763:
1764: if (test == ITEST_MAX)
1765: {
1766: mode = SImode;
1767: goto fail;
1768: }
1769:
1770: /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode). */
1771: mode = mode_map[(int)type][(int)test];
1772: if (mode == VOIDmode)
1773: goto fail;
1774:
1775: switch (branch_type)
1776: {
1777: default:
1778: goto fail;
1779:
1780: case CMP_SI:
1781: reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
1782: if (reg != (rtx)0)
1783: {
1784: cmp0 = reg;
1785: cmp1 = const0_rtx;
1786: test_code = NE;
1787: }
1788:
1789: /* Make sure not non-zero constant if ==/!= */
1790: else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
1791: cmp1 = force_reg (SImode, cmp1);
1792:
1793: break;
1794:
1795: case CMP_DF:
1796: case CMP_SF:
1797: {
1798: rtx reg = gen_rtx (REG, mode, FPSW_REGNUM);
1799: emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1)));
1800: cmp0 = reg;
1801: cmp1 = const0_rtx;
1802: test_code = NE;
1803: }
1804: break;
1805: }
1806:
1807: /* Generate the jump */
1808: if (invert)
1809: {
1810: label2 = label1;
1811: label1 = pc_rtx;
1812: }
1813:
1814: emit_jump_insn (gen_rtx (SET, VOIDmode,
1815: pc_rtx,
1816: gen_rtx (IF_THEN_ELSE, VOIDmode,
1817: gen_rtx (test_code, mode, cmp0, cmp1),
1818: label1,
1819: label2)));
1820:
1821: return;
1822:
1823: fail:
1824: abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test");
1825: }
1826:
1827:
1828: #define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT)
1829:
1830: /* Internal code to generate the load and store of one word/short/byte.
1831: The load is emitted directly, and the store insn is returned. */
1832:
1833: #if 0
1834: static rtx
1835: block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src)
1836: rtx src_reg; /* register holding source memory address */
1837: rtx dest_reg; /* register holding dest. memory address */
1838: int *p_bytes; /* pointer to # bytes remaining */
1839: int *p_offset; /* pointer to current offset */
1840: int align; /* alignment */
1841: rtx orig_src; /* original source for making a reg note */
1842: {
1843: int bytes; /* # bytes remaining */
1844: int offset; /* offset to use */
1845: int size; /* size in bytes of load/store */
1846: enum machine_mode mode; /* mode to use for load/store */
1847: rtx reg; /* temporary register */
1848: rtx src_addr; /* source address */
1849: rtx dest_addr; /* destination address */
1850: rtx insn; /* insn of the load */
1851: rtx orig_src_addr; /* original source address */
1852: rtx (*load_func)(); /* function to generate load insn */
1853: rtx (*store_func)(); /* function to generate destination insn */
1854:
1855: bytes = *p_bytes;
1856: if (bytes <= 0 || align <= 0)
1857: abort ();
1858:
1859: if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
1860: {
1861: mode = SImode;
1862: size = UNITS_PER_WORD;
1863: load_func = gen_movsi;
1864: store_func = gen_movsi;
1865: }
1866:
1867: #if 0
1868: /* Don't generate unaligned moves here, rather defer those to the
1869: general movestrsi_internal pattern. */
1870: else if (bytes >= UNITS_PER_WORD)
1871: {
1872: mode = SImode;
1873: size = UNITS_PER_WORD;
1874: load_func = gen_movsi_ulw;
1875: store_func = gen_movsi_usw;
1876: }
1877: #endif
1878:
1879: else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
1880: {
1881: mode = HImode;
1882: size = UNITS_PER_SHORT;
1883: load_func = gen_movhi;
1884: store_func = gen_movhi;
1885: }
1886:
1887: else
1888: {
1889: mode = QImode;
1890: size = 1;
1891: load_func = gen_movqi;
1892: store_func = gen_movqi;
1893: }
1894:
1895: offset = *p_offset;
1896: *p_offset = offset + size;
1897: *p_bytes = bytes - size;
1898:
1899: if (offset == 0)
1900: {
1901: src_addr = src_reg;
1902: dest_addr = dest_reg;
1903: }
1904: else
1905: {
1906: src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset));
1907: dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
1908: }
1909:
1910: reg = gen_reg_rtx (mode);
1911: insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
1912: orig_src_addr = XEXP (orig_src, 0);
1913: if (CONSTANT_P (orig_src_addr))
1914: REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV,
1915: plus_constant (orig_src_addr, offset),
1916: REG_NOTES (insn));
1917:
1918: return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);
1919: }
1920: #endif
1921:
1922:
1923: /* Write a series of loads/stores to move some bytes. Generate load/stores as follows:
1924:
1925: load 1
1926: load 2
1927: load 3
1928: store 1
1929: load 4
1930: store 2
1931: load 5
1932: store 3
1933: ...
1934:
1935: This way, no NOP's are needed, except at the end, and only
1936: two temp registers are needed. Two delay slots are used
1937: in deference to the R4000. */
1938:
1939: #if 0
1940: static void
1941: block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
1942: rtx dest_reg; /* register holding destination address */
1943: rtx src_reg; /* register holding source address */
1944: int bytes; /* # bytes to move */
1945: int align; /* max alignment to assume */
1946: rtx orig_src; /* original source for making a reg note */
1947: {
1948: int offset = 0;
1949: rtx prev2_store = (rtx)0;
1950: rtx prev_store = (rtx)0;
1951: rtx cur_store = (rtx)0;
1952:
1953: while (bytes > 0)
1954: {
1955: /* Is there a store to do? */
1956: if (prev2_store)
1957: emit_insn (prev2_store);
1958:
1959: prev2_store = prev_store;
1960: prev_store = cur_store;
1961: cur_store = block_move_load_store (dest_reg, src_reg,
1962: &bytes, &offset,
1963: align, orig_src);
1964: }
1965:
1966: /* Finish up last three stores. */
1967: if (prev2_store)
1968: emit_insn (prev2_store);
1969:
1970: if (prev_store)
1971: emit_insn (prev_store);
1972:
1973: if (cur_store)
1974: emit_insn (cur_store);
1975: }
1976: #endif
1977:
1978:
1979: /* Write a loop to move a constant number of bytes. Generate load/stores as follows:
1980:
1981: do {
1982: temp1 = src[0];
1983: temp2 = src[1];
1984: ...
1985: temp<last> = src[MAX_MOVE_REGS-1];
1986: dest[0] = temp1;
1987: dest[1] = temp2;
1988: ...
1989: dest[MAX_MOVE_REGS-1] = temp<last>;
1990: src += MAX_MOVE_REGS;
1991: dest += MAX_MOVE_REGS;
1992: } while (src != final);
1993:
1994: This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp
1995: registers are needed.
1996:
1997: Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3
1998: cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every
1999: (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses. */
2000:
2001: #define MAX_MOVE_REGS 4
2002: #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
2003:
2004: static void
2005: block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
2006: rtx dest_reg; /* register holding destination address */
2007: rtx src_reg; /* register holding source address */
2008: int bytes; /* # bytes to move */
2009: int align; /* alignment */
2010: rtx orig_src; /* original source for making a reg note */
2011: {
2012: rtx dest_mem = gen_rtx (MEM, BLKmode, dest_reg);
2013: rtx src_mem = gen_rtx (MEM, BLKmode, src_reg);
2014: rtx align_rtx = GEN_INT (align);
2015: rtx label;
2016: rtx final_src;
2017: rtx bytes_rtx;
2018: int leftover;
2019:
2020: if (bytes < 2*MAX_MOVE_BYTES)
2021: abort ();
2022:
2023: leftover = bytes % MAX_MOVE_BYTES;
2024: bytes -= leftover;
2025:
2026: label = gen_label_rtx ();
2027: final_src = gen_reg_rtx (Pmode);
2028: bytes_rtx = GEN_INT (bytes);
2029:
2030: if (bytes > 0x7fff)
2031: {
2032: emit_insn (gen_movsi (final_src, bytes_rtx));
2033: emit_insn (gen_addsi3 (final_src, final_src, src_reg));
2034: }
2035: else
2036: emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
2037:
2038: emit_label (label);
2039:
2040: bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
2041: emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
2042: emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
2043: emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
2044: emit_insn (gen_cmpsi (src_reg, final_src));
2045: emit_jump_insn (gen_bne (label));
2046:
2047: if (leftover)
2048: emit_insn (gen_movstrsi_internal (dest_mem, src_mem,
2049: GEN_INT (leftover),
2050: align_rtx));
2051: }
2052:
2053:
2054: /* Use a library function to move some bytes. */
2055:
2056: static void
2057: block_move_call (dest_reg, src_reg, bytes_rtx)
2058: rtx dest_reg;
2059: rtx src_reg;
2060: rtx bytes_rtx;
2061: {
2062: #ifdef TARGET_MEM_FUNCTIONS
2063: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
2064: VOIDmode, 3,
2065: dest_reg, Pmode,
2066: src_reg, Pmode,
2067: bytes_rtx, SImode);
2068: #else
2069: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0,
2070: VOIDmode, 3,
2071: src_reg, Pmode,
2072: dest_reg, Pmode,
2073: bytes_rtx, SImode);
2074: #endif
2075: }
2076:
2077:
2078: /* Expand string/block move operations.
2079:
2080: operands[0] is the pointer to the destination.
2081: operands[1] is the pointer to the source.
2082: operands[2] is the number of bytes to move.
2083: operands[3] is the alignment. */
2084:
2085: void
2086: expand_block_move (operands)
2087: rtx operands[];
2088: {
2089: rtx bytes_rtx = operands[2];
2090: rtx align_rtx = operands[3];
2091: int constp = (GET_CODE (bytes_rtx) == CONST_INT);
2092: int bytes = (constp ? INTVAL (bytes_rtx) : 0);
2093: int align = INTVAL (align_rtx);
2094: rtx orig_src = operands[1];
2095: rtx src_reg;
2096: rtx dest_reg;
2097:
2098: if (constp && bytes <= 0)
2099: return;
2100:
2101: if (align > UNITS_PER_WORD)
2102: align = UNITS_PER_WORD;
2103:
2104: /* Move the address into scratch registers. */
2105: dest_reg = copy_addr_to_reg (XEXP (operands[0], 0));
2106: src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
2107:
2108: if (TARGET_MEMCPY)
2109: block_move_call (dest_reg, src_reg, bytes_rtx);
2110:
2111: #if 0
2112: else if (constp && bytes <= 3*align)
2113: block_move_sequence (dest_reg, src_reg, bytes, align, orig_src);
2114: #endif
2115:
2116: else if (constp && bytes <= 2*MAX_MOVE_BYTES)
2117: emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
2118: gen_rtx (MEM, BLKmode, src_reg),
2119: bytes_rtx, align_rtx));
2120:
2121: else if (constp && align >= UNITS_PER_WORD && optimize)
2122: block_move_loop (dest_reg, src_reg, bytes, align, orig_src);
2123:
2124: else if (constp && optimize)
2125: {
2126: /* If the alignment is not word aligned, generate a test at
2127: runtime, to see whether things wound up aligned, and we
2128: can use the faster lw/sw instead ulw/usw. */
2129:
2130: rtx temp = gen_reg_rtx (Pmode);
2131: rtx aligned_label = gen_label_rtx ();
2132: rtx join_label = gen_label_rtx ();
2133: int leftover = bytes % MAX_MOVE_BYTES;
2134:
2135: bytes -= leftover;
2136:
2137: emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
2138: emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1)));
2139: emit_insn (gen_cmpsi (temp, const0_rtx));
2140: emit_jump_insn (gen_beq (aligned_label));
2141:
2142: /* Unaligned loop. */
2143: block_move_loop (dest_reg, src_reg, bytes, 1, orig_src);
2144: emit_jump_insn (gen_jump (join_label));
2145: emit_barrier ();
2146:
2147: /* Aligned loop. */
2148: emit_label (aligned_label);
2149: block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src);
2150: emit_label (join_label);
2151:
2152: /* Bytes at the end of the loop. */
2153: if (leftover)
2154: {
2155: #if 0
2156: if (leftover <= 3*align)
2157: block_move_sequence (dest_reg, src_reg, leftover, align, orig_src);
2158:
2159: else
2160: #endif
2161: emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
2162: gen_rtx (MEM, BLKmode, src_reg),
2163: GEN_INT (leftover),
2164: GEN_INT (align)));
2165: }
2166: }
2167:
2168: else
2169: block_move_call (dest_reg, src_reg, bytes_rtx);
2170: }
2171:
2172:
2173: /* Emit load/stores for a small constant block_move.
2174:
2175: operands[0] is the memory address of the destination.
2176: operands[1] is the memory address of the source.
2177: operands[2] is the number of bytes to move.
2178: operands[3] is the alignment.
2179: operands[4] is a temp register.
2180: operands[5] is a temp register.
2181: ...
2182: operands[3+num_regs] is the last temp register.
2183:
2184: The block move type can be one of the following:
2185: BLOCK_MOVE_NORMAL Do all of the block move.
2186: BLOCK_MOVE_NOT_LAST Do all but the last store.
2187: BLOCK_MOVE_LAST Do just the last store. */
2188:
2189: char *
2190: output_block_move (insn, operands, num_regs, move_type)
2191: rtx insn;
2192: rtx operands[];
2193: int num_regs;
2194: enum block_move_type move_type;
2195: {
2196: rtx dest_reg = XEXP (operands[0], 0);
2197: rtx src_reg = XEXP (operands[1], 0);
2198: int bytes = INTVAL (operands[2]);
2199: int align = INTVAL (operands[3]);
2200: int num = 0;
2201: int offset = 0;
2202: int use_lwl_lwr = FALSE;
2203: int last_operand = num_regs+4;
2204: int safe_regs = 4;
2205: int i;
2206: rtx xoperands[10];
2207:
2208: struct {
2209: char *load; /* load insn without nop */
2210: char *load_nop; /* load insn with trailing nop */
2211: char *store; /* store insn */
2212: char *final; /* if last_store used: NULL or swr */
2213: char *last_store; /* last store instruction */
2214: int offset; /* current offset */
2215: enum machine_mode mode; /* mode to use on (MEM) */
2216: } load_store[4];
2217:
2218: /* Detect a bug in GCC, where it can give us a register
2219: the same as one of the addressing registers and reduce
2220: the number of registers available. */
2221: for (i = 4;
2222: i < last_operand && safe_regs < (sizeof(xoperands) / sizeof(xoperands[0]));
2223: i++)
2224: {
2225: if (!reg_mentioned_p (operands[i], operands[0])
2226: && !reg_mentioned_p (operands[i], operands[1]))
2227:
2228: xoperands[safe_regs++] = operands[i];
2229: }
2230:
2231: if (safe_regs < last_operand)
2232: {
2233: xoperands[0] = operands[0];
2234: xoperands[1] = operands[1];
2235: xoperands[2] = operands[2];
2236: xoperands[3] = operands[3];
2237: return output_block_move (insn, xoperands, safe_regs-4, move_type);
2238: }
2239:
2240: /* If we are given global or static addresses, and we would be
2241: emitting a few instructions, try to save time by using a
2242: temporary register for the pointer. */
2243: if (num_regs > 2 && (bytes > 2*align || move_type != BLOCK_MOVE_NORMAL))
2244: {
2245: if (CONSTANT_P (src_reg))
2246: {
2247: if (TARGET_STATS)
2248: mips_count_memory_refs (operands[1], 1);
2249:
2250: src_reg = operands[ 3 + num_regs-- ];
2251: if (move_type != BLOCK_MOVE_LAST)
2252: {
2253: xoperands[1] = operands[1];
2254: xoperands[0] = src_reg;
2255: output_asm_insn ("la\t%0,%1", xoperands);
2256: }
2257: }
2258:
2259: if (CONSTANT_P (dest_reg))
2260: {
2261: if (TARGET_STATS)
2262: mips_count_memory_refs (operands[0], 1);
2263:
2264: dest_reg = operands[ 3 + num_regs-- ];
2265: if (move_type != BLOCK_MOVE_LAST)
2266: {
2267: xoperands[1] = operands[0];
2268: xoperands[0] = dest_reg;
2269: output_asm_insn ("la\t%0,%1", xoperands);
2270: }
2271: }
2272: }
2273:
2274: if (num_regs > (sizeof (load_store) / sizeof (load_store[0])))
2275: num_regs = (sizeof (load_store) / sizeof (load_store[0]));
2276:
2277: else if (num_regs < 1)
2278: abort_with_insn (insn, "Cannot do block move, not enough scratch registers");
2279:
2280: if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0)
2281: output_asm_insn (".set\tnoreorder", operands);
2282:
2283: while (bytes > 0)
2284: {
2285: load_store[num].offset = offset;
2286:
2287: if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
2288: {
2289: load_store[num].load = "lw\t%0,%1";
2290: load_store[num].load_nop = "lw\t%0,%1%#";
2291: load_store[num].store = "sw\t%0,%1";
2292: load_store[num].last_store = "sw\t%0,%1";
2293: load_store[num].final = (char *)0;
2294: load_store[num].mode = SImode;
2295: offset += UNITS_PER_WORD;
2296: bytes -= UNITS_PER_WORD;
2297: }
2298:
2299: else if (bytes >= UNITS_PER_WORD)
2300: {
2301: #if BYTES_BIG_ENDIAN
2302: load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
2303: load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
2304: load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
2305: load_store[num].last_store = "swr\t%0,%2";
2306: load_store[num].final = "swl\t%0,%1";
2307: #else
2308: load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
2309: load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
2310: load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
2311: load_store[num].last_store = "swr\t%0,%1";
2312: load_store[num].final = "swl\t%0,%2";
2313: #endif
2314: load_store[num].mode = SImode;
2315: offset += UNITS_PER_WORD;
2316: bytes -= UNITS_PER_WORD;
2317: use_lwl_lwr = TRUE;
2318: }
2319:
2320: else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
2321: {
2322: load_store[num].load = "lh\t%0,%1";
2323: load_store[num].load_nop = "lh\t%0,%1%#";
2324: load_store[num].store = "sh\t%0,%1";
2325: load_store[num].last_store = "sh\t%0,%1";
2326: load_store[num].final = (char *)0;
2327: load_store[num].offset = offset;
2328: load_store[num].mode = HImode;
2329: offset += UNITS_PER_SHORT;
2330: bytes -= UNITS_PER_SHORT;
2331: }
2332:
2333: else
2334: {
2335: load_store[num].load = "lb\t%0,%1";
2336: load_store[num].load_nop = "lb\t%0,%1%#";
2337: load_store[num].store = "sb\t%0,%1";
2338: load_store[num].last_store = "sb\t%0,%1";
2339: load_store[num].final = (char *)0;
2340: load_store[num].mode = QImode;
2341: offset++;
2342: bytes--;
2343: }
2344:
2345: if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
2346: {
2347: dslots_load_total++;
2348: dslots_load_filled++;
2349:
2350: if (CONSTANT_P (src_reg))
2351: mips_count_memory_refs (src_reg, 1);
2352:
2353: if (CONSTANT_P (dest_reg))
2354: mips_count_memory_refs (dest_reg, 1);
2355: }
2356:
2357: /* Emit load/stores now if we have run out of registers or are
2358: at the end of the move. */
2359:
2360: if (++num == num_regs || bytes == 0)
2361: {
2362: /* If only load/store, we need a NOP after the load. */
2363: if (num == 1)
2364: {
2365: load_store[0].load = load_store[0].load_nop;
2366: if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
2367: dslots_load_filled--;
2368: }
2369:
2370: if (move_type != BLOCK_MOVE_LAST)
2371: {
2372: for (i = 0; i < num; i++)
2373: {
2374: int offset;
2375:
2376: if (!operands[i+4])
2377: abort ();
2378:
2379: if (GET_MODE (operands[i+4]) != load_store[i].mode)
2380: operands[i+4] = gen_rtx (REG, load_store[i].mode, REGNO (operands[i+4]));
2381:
2382: offset = load_store[i].offset;
2383: xoperands[0] = operands[i+4];
2384: xoperands[1] = gen_rtx (MEM, load_store[i].mode,
2385: plus_constant (src_reg, offset));
2386:
2387: if (use_lwl_lwr)
2388: xoperands[2] = gen_rtx (MEM, load_store[i].mode,
2389: plus_constant (src_reg, UNITS_PER_WORD-1+offset));
2390:
2391: output_asm_insn (load_store[i].load, xoperands);
2392: }
2393: }
2394:
2395: for (i = 0; i < num; i++)
2396: {
2397: int last_p = (i == num-1 && bytes == 0);
2398: int offset = load_store[i].offset;
2399:
2400: xoperands[0] = operands[i+4];
2401: xoperands[1] = gen_rtx (MEM, load_store[i].mode,
2402: plus_constant (dest_reg, offset));
2403:
2404:
2405: if (use_lwl_lwr)
2406: xoperands[2] = gen_rtx (MEM, load_store[i].mode,
2407: plus_constant (dest_reg, UNITS_PER_WORD-1+offset));
2408:
2409: if (move_type == BLOCK_MOVE_NORMAL)
2410: output_asm_insn (load_store[i].store, xoperands);
2411:
2412: else if (move_type == BLOCK_MOVE_NOT_LAST)
2413: {
2414: if (!last_p)
2415: output_asm_insn (load_store[i].store, xoperands);
2416:
2417: else if (load_store[i].final != (char *)0)
2418: output_asm_insn (load_store[i].final, xoperands);
2419: }
2420:
2421: else if (last_p)
2422: output_asm_insn (load_store[i].last_store, xoperands);
2423: }
2424:
2425: num = 0; /* reset load_store */
2426: use_lwl_lwr = FALSE; /* reset whether or not we used lwl/lwr */
2427: }
2428: }
2429:
2430: if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0)
2431: output_asm_insn (".set\treorder", operands);
2432:
2433: return "";
2434: }
2435:
2436:
2437: /* Argument support functions. */
2438:
2439: /* Initialize CUMULATIVE_ARGS for a function. */
2440:
2441: void
2442: init_cumulative_args (cum, fntype, libname)
2443: CUMULATIVE_ARGS *cum; /* argument info to initialize */
2444: tree fntype; /* tree ptr for function decl */
2445: rtx libname; /* SYMBOL_REF of library name or 0 */
2446: {
2447: static CUMULATIVE_ARGS zero_cum;
2448: tree param, next_param;
2449:
2450: if (TARGET_DEBUG_E_MODE)
2451: {
2452: fprintf (stderr, "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
2453: if (!fntype)
2454: fputc ('\n', stderr);
2455:
2456: else
2457: {
2458: tree ret_type = TREE_TYPE (fntype);
2459: fprintf (stderr, ", fntype code = %s, ret code = %s\n",
2460: tree_code_name[ (int)TREE_CODE (fntype) ],
2461: tree_code_name[ (int)TREE_CODE (ret_type) ]);
2462: }
2463: }
2464:
2465: *cum = zero_cum;
2466:
2467: /* Determine if this function has variable arguments. This is
2468: indicated by the last argument being 'void_type_mode' if there
2469: are no variable arguments. The standard MIPS calling sequence
2470: passes all arguments in the general purpose registers in this
2471: case. */
2472:
2473: for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
2474: param != (tree)0;
2475: param = next_param)
2476: {
2477: next_param = TREE_CHAIN (param);
2478: if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node)
2479: cum->gp_reg_found = 1;
2480: }
2481: }
2482:
2483: /* Advance the argument to the next argument position. */
2484:
2485: void
2486: function_arg_advance (cum, mode, type, named)
2487: CUMULATIVE_ARGS *cum; /* current arg information */
2488: enum machine_mode mode; /* current arg mode */
2489: tree type; /* type of the argument or 0 if lib support */
2490: int named; /* whether or not the argument was named */
2491: {
2492: if (TARGET_DEBUG_E_MODE)
2493: fprintf (stderr,
2494: "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n\n",
2495: cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
2496: type, named);
2497:
2498: cum->arg_number++;
2499: switch (mode)
2500: {
2501: default:
2502: error ("Illegal mode given to function_arg_advance");
2503: break;
2504:
2505: case VOIDmode:
2506: break;
2507:
2508: case BLKmode:
2509: cum->gp_reg_found = 1;
2510: cum->arg_words += (int_size_in_bytes (type) + 3) / 4;
2511: break;
2512:
2513: case SFmode:
2514: cum->arg_words++;
2515: break;
2516:
2517: case DFmode:
2518: cum->arg_words += 2;
2519: break;
2520:
2521: case DImode:
2522: cum->gp_reg_found = 1;
2523: cum->arg_words += 2;
2524: break;
2525:
2526: case QImode:
2527: case HImode:
2528: case SImode:
2529: cum->gp_reg_found = 1;
2530: cum->arg_words++;
2531: break;
2532: }
2533: }
2534:
2535: /* Return a RTL expression containing the register for the given mode,
2536: or 0 if the argument is too be passed on the stack. */
2537:
2538: struct rtx_def *
2539: function_arg (cum, mode, type, named)
2540: CUMULATIVE_ARGS *cum; /* current arg information */
2541: enum machine_mode mode; /* current arg mode */
2542: tree type; /* type of the argument or 0 if lib support */
2543: int named; /* != 0 for normal args, == 0 for ... args */
2544: {
2545: rtx ret;
2546: int regbase = -1;
2547: int bias = 0;
2548: int struct_p = ((type != (tree)0)
2549: && (TREE_CODE (type) == RECORD_TYPE
2550: || TREE_CODE (type) == UNION_TYPE));
2551:
2552: if (TARGET_DEBUG_E_MODE)
2553: fprintf (stderr,
2554: "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d ) = ",
2555: cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
2556: type, named);
2557:
2558: switch (mode)
2559: {
2560: default:
2561: error ("Illegal mode given to function_arg");
2562: break;
2563:
2564: case SFmode:
2565: if (cum->gp_reg_found || cum->arg_number >= 2)
2566: regbase = GP_ARG_FIRST;
2567: else {
2568: regbase = (TARGET_SOFT_FLOAT) ? GP_ARG_FIRST : FP_ARG_FIRST;
2569: if (cum->arg_words == 1) /* first arg was float */
2570: bias = 1; /* use correct reg */
2571: }
2572:
2573: break;
2574:
2575: case DFmode:
2576: cum->arg_words += (cum->arg_words & 1);
2577: regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT)
2578: ? GP_ARG_FIRST
2579: : FP_ARG_FIRST;
2580: break;
2581:
2582: case BLKmode:
2583: if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD)
2584: cum->arg_words += (cum->arg_words & 1);
2585:
2586: regbase = GP_ARG_FIRST;
2587: break;
2588:
2589: case VOIDmode:
2590: case QImode:
2591: case HImode:
2592: case SImode:
2593: regbase = GP_ARG_FIRST;
2594: break;
2595:
2596: case DImode:
2597: cum->arg_words += (cum->arg_words & 1);
2598: regbase = GP_ARG_FIRST;
2599: }
2600:
2601: if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
2602: {
2603: if (TARGET_DEBUG_E_MODE)
2604: fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
2605:
2606: ret = (rtx)0;
2607: }
2608: else
2609: {
2610: if (regbase == -1)
2611: abort ();
2612:
2613: ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias);
2614:
2615: if (TARGET_DEBUG_E_MODE)
2616: fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias],
2617: struct_p ? ", [struct]" : "");
2618:
2619: /* The following is a hack in order to pass 1 byte structures
2620: the same way that the MIPS compiler does (namely by passing
2621: the structure in the high byte or half word of the register).
2622: This also makes varargs work. If we have such a structure,
2623: we save the adjustment RTL, and the call define expands will
2624: emit them. For the VOIDmode argument (argument after the
2625: last real argument, pass back a parallel vector holding each
2626: of the adjustments. */
2627:
2628: if (struct_p && (mode == QImode || mode == HImode))
2629: {
2630: rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode));
2631: rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias);
2632: cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
2633: }
2634: }
2635:
2636: if (mode == VOIDmode && cum->num_adjusts > 0)
2637: ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust));
2638:
2639: return ret;
2640: }
2641:
2642:
2643: int
2644: function_arg_partial_nregs (cum, mode, type, named)
2645: CUMULATIVE_ARGS *cum; /* current arg information */
2646: enum machine_mode mode; /* current arg mode */
2647: tree type; /* type of the argument or 0 if lib support */
2648: int named; /* != 0 for normal args, == 0 for ... args */
2649: {
2650: if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS)
2651: {
2652: int words = (int_size_in_bytes (type) + 3) / 4;
2653:
2654: if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS)
2655: return 0; /* structure fits in registers */
2656:
2657: if (TARGET_DEBUG_E_MODE)
2658: fprintf (stderr, "function_arg_partial_nregs = %d\n",
2659: MAX_ARGS_IN_REGISTERS - cum->arg_words);
2660:
2661: return MAX_ARGS_IN_REGISTERS - cum->arg_words;
2662: }
2663:
2664: else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1)
2665: {
2666: if (TARGET_DEBUG_E_MODE)
2667: fprintf (stderr, "function_arg_partial_nregs = 1\n");
2668:
2669: return 1;
2670: }
2671:
2672: return 0;
2673: }
2674:
2675:
2676: /* Print the options used in the assembly file. */
2677:
2678: static struct {char *name; int value;} target_switches []
2679: = TARGET_SWITCHES;
2680:
2681: void
2682: print_options (out)
2683: FILE *out;
2684: {
2685: int line_len;
2686: int len;
2687: int j;
2688: char **p;
2689: int mask = TARGET_DEFAULT;
2690:
2691: /* Allow assembly language comparisons with -mdebug eliminating the
2692: compiler version number and switch lists. */
2693:
2694: if (TARGET_DEBUG_MODE)
2695: return;
2696:
2697: fprintf (out, "\n # %s %s", language_string, version_string);
2698: #ifdef TARGET_VERSION_INTERNAL
2699: TARGET_VERSION_INTERNAL (out);
2700: #endif
2701: #ifdef __GNUC__
2702: fprintf (out, " compiled by GNU C\n\n");
2703: #else
2704: fprintf (out, " compiled by CC\n\n");
2705: #endif
2706:
2707: fprintf (out, " # Cc1 defaults:");
2708: line_len = 32767;
2709: for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
2710: {
2711: if (target_switches[j].name[0] != '\0'
2712: && target_switches[j].value > 0
2713: && (target_switches[j].value & mask) == target_switches[j].value)
2714: {
2715: mask &= ~ target_switches[j].value;
2716: len = strlen (target_switches[j].name) + 1;
2717: if (len + line_len > 79)
2718: {
2719: line_len = 2;
2720: fputs ("\n #", out);
2721: }
2722: fprintf (out, " -m%s", target_switches[j].name);
2723: line_len += len;
2724: }
2725: }
2726:
2727: fprintf (out, "\n\n # Cc1 arguments (-G value = %d, Cpu = %s, ISA = %d):",
2728: mips_section_threshold, mips_cpu_string, mips_isa);
2729:
2730: line_len = 32767;
2731: for (p = &save_argv[1]; *p != (char *)0; p++)
2732: {
2733: char *arg = *p;
2734: if (*arg == '-')
2735: {
2736: len = strlen (arg) + 1;
2737: if (len + line_len > 79)
2738: {
2739: line_len = 2;
2740: fputs ("\n #", out);
2741: }
2742: fprintf (out, " %s", *p);
2743: line_len += len;
2744: }
2745: }
2746:
2747: fputs ("\n\n", out);
2748: }
2749:
2750:
2751: /* Abort after printing out a specific insn. */
2752:
2753: void
2754: abort_with_insn (insn, reason)
2755: rtx insn;
2756: char *reason;
2757: {
2758: error (reason);
2759: debug_rtx (insn);
2760: abort ();
2761: }
2762:
2763: /* Write a message to stderr (for use in macros expanded in files that do not
2764: include stdio.h). */
2765:
2766: void
2767: trace (s, s1, s2)
2768: char *s, *s1, *s2;
2769: {
2770: fprintf (stderr, s, s1, s2);
2771: }
2772:
2773:
2774: #ifdef SIGINFO
2775:
2776: static void
2777: siginfo (signo)
2778: int signo;
2779: {
2780: fprintf (stderr, "compiling '%s' in '%s'\n",
2781: (current_function_name != (char *)0) ? current_function_name : "<toplevel>",
2782: (current_function_file != (char *)0) ? current_function_file : "<no file>");
2783: fflush (stderr);
2784: }
2785: #endif /* SIGINFO */
2786:
2787:
2788: /* Set up the threshold for data to go into the small data area, instead
2789: of the normal data area, and detect any conflicts in the switches. */
2790:
2791: void
2792: override_options ()
2793: {
2794: register int i, start;
2795: register int regno;
2796: register enum machine_mode mode;
2797:
2798: mips_section_threshold = (g_switch_set) ? g_switch_value : MIPS_DEFAULT_GVALUE;
2799:
2800: /* Identify the processor type */
2801: if (mips_cpu_string == (char *)0
2802: || !strcmp (mips_cpu_string, "default")
2803: || !strcmp (mips_cpu_string, "DEFAULT"))
2804: {
2805: mips_cpu_string = "default";
2806: mips_cpu = PROCESSOR_DEFAULT;
2807: }
2808:
2809: else
2810: {
2811: char *p = mips_cpu_string;
2812:
2813: if (*p == 'r' || *p == 'R')
2814: p++;
2815:
2816: /* Since there is no difference between a R2000 and R3000 in
2817: terms of the scheduler, we collapse them into just an R3000. */
2818:
2819: mips_cpu = PROCESSOR_DEFAULT;
2820: switch (*p)
2821: {
2822: case '2':
2823: if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K"))
2824: mips_cpu = PROCESSOR_R3000;
2825: break;
2826:
2827: case '3':
2828: if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K"))
2829: mips_cpu = PROCESSOR_R3000;
2830: break;
2831:
2832: case '4':
2833: if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
2834: mips_cpu = PROCESSOR_R4000;
2835: break;
2836:
2837: case '6':
2838: if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K"))
2839: mips_cpu = PROCESSOR_R6000;
2840: break;
2841: }
2842:
2843: if (mips_cpu == PROCESSOR_DEFAULT)
2844: {
2845: error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
2846: mips_cpu_string = "default";
2847: }
2848: }
2849:
2850: /* Now get the architectural level. */
2851: if (mips_isa_string == (char *)0)
2852: mips_isa = 1;
2853:
2854: else if (isdigit (*mips_isa_string))
2855: mips_isa = atoi (mips_isa_string);
2856:
2857: else
2858: {
2859: error ("bad value (%s) for -mips switch", mips_isa_string);
2860: mips_isa = 1;
2861: }
2862:
2863: if (mips_isa < 0 || mips_isa > 3)
2864: error ("-mips%d not supported", mips_isa);
2865:
2866: else if (mips_isa > 1
2867: && (mips_cpu == PROCESSOR_DEFAULT || mips_cpu == PROCESSOR_R3000))
2868: error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
2869:
2870: else if (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)
2871: error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
2872:
2873: /* make sure sizes of ints/longs/etc. are ok */
2874: if (mips_isa < 3)
2875: {
2876: if (TARGET_INT64)
2877: fatal ("Only the r4000 can support 64 bit ints");
2878:
2879: else if (TARGET_LONG64)
2880: fatal ("Only the r4000 can support 64 bit longs");
2881:
2882: else if (TARGET_LLONG128)
2883: fatal ("Only the r4000 can support 128 bit long longs");
2884:
2885: else if (TARGET_FLOAT64)
2886: fatal ("Only the r4000 can support 64 bit fp registers");
2887: }
2888: else if (TARGET_INT64 || TARGET_LONG64 || TARGET_LLONG128 || TARGET_FLOAT64)
2889: warning ("r4000 64/128 bit types not yet supported");
2890:
2891: /* Tell halfpic.c that we have half-pic code if we do. */
2892: if (TARGET_HALF_PIC)
2893: HALF_PIC_INIT ();
2894:
2895: /* -mrnames says to use the MIPS software convention for register
2896: names instead of the hardware names (ie, a0 instead of $4).
2897: We do this by switching the names in mips_reg_names, which the
2898: reg_names points into via the REGISTER_NAMES macro. */
2899:
2900: if (TARGET_NAME_REGS)
2901: {
2902: if (TARGET_GAS)
2903: {
2904: target_flags &= ~ MASK_NAME_REGS;
2905: error ("Gas does not support the MIPS software register name convention.");
2906: }
2907: else
2908: bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names));
2909: }
2910:
2911: /* If this is OSF/1, set up a SIGINFO handler so we can see what function
2912: is currently being compiled. */
2913: #ifdef SIGINFO
2914: if (getenv ("GCC_SIGINFO") != (char *)0)
2915: {
2916: struct sigaction action;
2917: action.sa_handler = siginfo;
2918: action.sa_mask = 0;
2919: action.sa_flags = SA_RESTART;
2920: sigaction (SIGINFO, &action, (struct sigaction *)0);
2921: }
2922: #endif
2923:
2924: #if defined(_IOLBF)
2925: #if defined(ultrix) || defined(__ultrix) || defined(__OSF1__) || defined(__osf__) || defined(osf)
2926: /* If -mstats and -quiet, make stderr line buffered. */
2927: if (quiet_flag && TARGET_STATS)
2928: setvbuf (stderr, (char *)0, _IOLBF, BUFSIZ);
2929: #endif
2930: #endif
2931:
2932: /* Set up the classification arrays now. */
2933: mips_rtx_classify[(int)PLUS] = CLASS_ADD_OP;
2934: mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP;
2935: mips_rtx_classify[(int)DIV] = CLASS_DIVMOD_OP;
2936: mips_rtx_classify[(int)MOD] = CLASS_DIVMOD_OP;
2937: mips_rtx_classify[(int)UDIV] = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
2938: mips_rtx_classify[(int)UMOD] = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
2939: mips_rtx_classify[(int)EQ] = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
2940: mips_rtx_classify[(int)NE] = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
2941: mips_rtx_classify[(int)GT] = CLASS_CMP_OP | CLASS_FCMP_OP;
2942: mips_rtx_classify[(int)GE] = CLASS_CMP_OP | CLASS_FCMP_OP;
2943: mips_rtx_classify[(int)LT] = CLASS_CMP_OP | CLASS_FCMP_OP;
2944: mips_rtx_classify[(int)LE] = CLASS_CMP_OP | CLASS_FCMP_OP;
2945: mips_rtx_classify[(int)GTU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
2946: mips_rtx_classify[(int)GEU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
2947: mips_rtx_classify[(int)LTU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
2948: mips_rtx_classify[(int)LEU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
2949:
2950: mips_print_operand_punct['?'] = TRUE;
2951: mips_print_operand_punct['#'] = TRUE;
2952: mips_print_operand_punct['&'] = TRUE;
2953: mips_print_operand_punct['!'] = TRUE;
2954: mips_print_operand_punct['*'] = TRUE;
2955: mips_print_operand_punct['@'] = TRUE;
2956: mips_print_operand_punct['.'] = TRUE;
2957: mips_print_operand_punct['('] = TRUE;
2958: mips_print_operand_punct[')'] = TRUE;
2959: mips_print_operand_punct['['] = TRUE;
2960: mips_print_operand_punct[']'] = TRUE;
2961: mips_print_operand_punct['<'] = TRUE;
2962: mips_print_operand_punct['>'] = TRUE;
2963: mips_print_operand_punct['{'] = TRUE;
2964: mips_print_operand_punct['}'] = TRUE;
2965:
2966: mips_char_to_class['d'] = GR_REGS;
2967: mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
2968: mips_char_to_class['h'] = HI_REG;
2969: mips_char_to_class['l'] = LO_REG;
2970: mips_char_to_class['x'] = MD_REGS;
2971: mips_char_to_class['y'] = GR_REGS;
2972: mips_char_to_class['z'] = ST_REGS;
2973:
2974: /* Set up array to map GCC register number to debug register number.
2975: Ignore the special purpose register numbers. */
2976:
2977: for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
2978: mips_dbx_regno[i] = -1;
2979:
2980: start = GP_DBX_FIRST - GP_REG_FIRST;
2981: for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
2982: mips_dbx_regno[i] = i + start;
2983:
2984: start = FP_DBX_FIRST - FP_REG_FIRST;
2985: for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
2986: mips_dbx_regno[i] = i + start;
2987:
2988: /* Set up array giving whether a given register can hold a given mode.
2989: At present, restrict ints from being in FP registers, because reload
2990: is a little enthusiastic about storing extra values in FP registers,
2991: and this is not good for things like OS kernels. Also, due to the
2992: mandatory delay, it is as fast to load from cached memory as to move
2993: from the FP register. */
2994:
2995: for (mode = VOIDmode;
2996: mode != MAX_MACHINE_MODE;
2997: mode = (enum machine_mode)((int)mode + 1))
2998: {
2999: register int size = GET_MODE_SIZE (mode);
3000: register enum mode_class class = GET_MODE_CLASS (mode);
3001:
3002: for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
3003: {
3004: register int temp;
3005:
3006: if (mode == CC_FPmode || mode == CC_REV_FPmode)
3007: temp = (regno == FPSW_REGNUM);
3008:
3009: else if (GP_REG_P (regno))
3010: temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
3011:
3012: else if (FP_REG_P (regno))
3013: temp = ((TARGET_FLOAT64 || ((regno & 1) == 0))
3014: && (class == MODE_FLOAT
3015: || class == MODE_COMPLEX_FLOAT
3016: || (TARGET_DEBUG_H_MODE && class == MODE_INT)));
3017:
3018: else if (MD_REG_P (regno))
3019: temp = (mode == SImode || (regno == MD_REG_FIRST && mode == DImode));
3020:
3021: else
3022: temp = FALSE;
3023:
3024: mips_hard_regno_mode_ok[(int)mode][regno] = temp;
3025: }
3026: }
3027: }
3028:
3029:
3030: /*
3031: * The MIPS debug format wants all automatic variables and arguments
3032: * to be in terms of the virtual frame pointer (stack pointer before
3033: * any adjustment in the function), while the MIPS 3.0 linker wants
3034: * the frame pointer to be the stack pointer after the initial
3035: * adjustment. So, we do the adjustment here. The arg pointer (which
3036: * is eliminated) points to the virtual frame pointer, while the frame
3037: * pointer (which may be eliminated) points to the stack pointer after
3038: * the initial adjustments.
3039: */
3040:
3041: int
3042: mips_debugger_offset (addr, offset)
3043: rtx addr;
3044: int offset;
3045: {
3046: rtx offset2 = const0_rtx;
3047: rtx reg = eliminate_constant_term (addr, &offset2);
3048:
3049: if (!offset)
3050: offset = INTVAL (offset2);
3051:
3052: if (reg == stack_pointer_rtx || reg == frame_pointer_rtx)
3053: {
3054: int frame_size = (!current_frame_info.initialized)
3055: ? compute_frame_size (get_frame_size ())
3056: : current_frame_info.total_size;
3057:
3058: offset = offset - frame_size;
3059: }
3060: /* sdbout_parms does not want this to crash for unrecognized cases. */
3061: #if 0
3062: else if (reg != arg_pointer_rtx)
3063: abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer.");
3064: #endif
3065:
3066: return offset;
3067: }
3068:
3069:
3070: /* A C compound statement to output to stdio stream STREAM the
3071: assembler syntax for an instruction operand X. X is an RTL
3072: expression.
3073:
3074: CODE is a value that can be used to specify one of several ways
3075: of printing the operand. It is used when identical operands
3076: must be printed differently depending on the context. CODE
3077: comes from the `%' specification that was used to request
3078: printing of the operand. If the specification was just `%DIGIT'
3079: then CODE is 0; if the specification was `%LTR DIGIT' then CODE
3080: is the ASCII code for LTR.
3081:
3082: If X is a register, this macro should print the register's name.
3083: The names can be found in an array `reg_names' whose type is
3084: `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
3085:
3086: When the machine description has a specification `%PUNCT' (a `%'
3087: followed by a punctuation character), this macro is called with
3088: a null pointer for X and the punctuation character for CODE.
3089:
3090: The MIPS specific codes are:
3091:
3092: 'X' X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
3093: 'x' X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
3094: 'd' output integer constant in decimal,
3095: 'z' if the operand is 0, use $0 instead of normal operand.
3096: 'D' print second register of double-word register operand.
3097: 'L' print low-order register of double-word register operand.
3098: 'M' print high-order register of double-word register operand.
3099: 'C' print part of opcode for a branch condition.
3100: 'N' print part of opcode for a branch condition, inverted.
3101: '(' Turn on .set noreorder
3102: ')' Turn on .set reorder
3103: '[' Turn on .set noat
3104: ']' Turn on .set at
3105: '<' Turn on .set nomacro
3106: '>' Turn on .set macro
3107: '{' Turn on .set volatile (not GAS)
3108: '}' Turn on .set novolatile (not GAS)
3109: '&' Turn on .set noreorder if filling delay slots
3110: '*' Turn on both .set noreorder and .set nomacro if filling delay slots
3111: '!' Turn on .set nomacro if filling delay slots
3112: '#' Print nop if in a .set noreorder section.
3113: '?' Print 'l' if we are to use a branch likely instead of normal branch.
3114: '@' Print the name of the assembler temporary register (at or $1).
3115: '.' Print the name of the register with a hard-wired zero (zero or $0). */
3116:
3117: void
3118: print_operand (file, op, letter)
3119: FILE *file; /* file to write to */
3120: rtx op; /* operand to print */
3121: int letter; /* %<letter> or 0 */
3122: {
3123: register enum rtx_code code;
3124:
3125: if (PRINT_OPERAND_PUNCT_VALID_P (letter))
3126: {
3127: switch (letter)
3128: {
3129: default:
3130: error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
3131: break;
3132:
3133: case '?':
3134: if (mips_branch_likely)
3135: putc ('l', file);
3136: break;
3137:
3138: case '@':
3139: fputs (reg_names [GP_REG_FIRST + 1], file);
3140: break;
3141:
3142: case '.':
3143: fputs (reg_names [GP_REG_FIRST + 0], file);
3144: break;
3145:
3146: case '&':
3147: if (final_sequence != 0 && set_noreorder++ == 0)
3148: fputs (".set\tnoreorder\n\t", file);
3149: break;
3150:
3151: case '*':
3152: if (final_sequence != 0)
3153: {
3154: if (set_noreorder++ == 0)
3155: fputs (".set\tnoreorder\n\t", file);
3156:
3157: if (set_nomacro++ == 0)
3158: fputs (".set\tnomacro\n\t", file);
3159: }
3160: break;
3161:
3162: case '!':
3163: if (final_sequence != 0 && set_nomacro++ == 0)
3164: fputs ("\n\t.set\tnomacro", file);
3165: break;
3166:
3167: case '#':
3168: if (set_noreorder != 0)
3169: fputs ("\n\tnop", file);
3170:
3171: else if (TARGET_GAS || TARGET_STATS)
3172: fputs ("\n\t#nop", file);
3173:
3174: break;
3175:
3176: case '(':
3177: if (set_noreorder++ == 0)
3178: fputs (".set\tnoreorder\n\t", file);
3179: break;
3180:
3181: case ')':
3182: if (set_noreorder == 0)
3183: error ("internal error: %%) found without a %%( in assembler pattern");
3184:
3185: else if (--set_noreorder == 0)
3186: fputs ("\n\t.set\treorder", file);
3187:
3188: break;
3189:
3190: case '[':
3191: if (set_noat++ == 0)
3192: fputs (".set\tnoat\n\t", file);
3193: break;
3194:
3195: case ']':
3196: if (set_noat == 0)
3197: error ("internal error: %%] found without a %%[ in assembler pattern");
3198:
3199: else if (--set_noat == 0)
3200: fputs ("\n\t.set\tat", file);
3201:
3202: break;
3203:
3204: case '<':
3205: if (set_nomacro++ == 0)
3206: fputs (".set\tnomacro\n\t", file);
3207: break;
3208:
3209: case '>':
3210: if (set_nomacro == 0)
3211: error ("internal error: %%> found without a %%< in assembler pattern");
3212:
3213: else if (--set_nomacro == 0)
3214: fputs ("\n\t.set\tmacro", file);
3215:
3216: break;
3217:
3218: case '{':
3219: if (set_volatile++ == 0)
3220: fprintf (file, "%s.set\tvolatile\n\t", (TARGET_MIPS_AS) ? "" : "#");
3221: break;
3222:
3223: case '}':
3224: if (set_volatile == 0)
3225: error ("internal error: %%} found without a %%{ in assembler pattern");
3226:
3227: else if (--set_volatile == 0)
3228: fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#");
3229:
3230: break;
3231: }
3232: return;
3233: }
3234:
3235: if (! op)
3236: {
3237: error ("PRINT_OPERAND null pointer");
3238: return;
3239: }
3240:
3241: code = GET_CODE (op);
3242: if (letter == 'C')
3243: switch (code)
3244: {
3245: case EQ: fputs ("eq", file); break;
3246: case NE: fputs ("ne", file); break;
3247: case GT: fputs ("gt", file); break;
3248: case GE: fputs ("ge", file); break;
3249: case LT: fputs ("lt", file); break;
3250: case LE: fputs ("le", file); break;
3251: case GTU: fputs ("gtu", file); break;
3252: case GEU: fputs ("geu", file); break;
3253: case LTU: fputs ("ltu", file); break;
3254: case LEU: fputs ("leu", file); break;
3255:
3256: default:
3257: abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%C");
3258: }
3259:
3260: else if (letter == 'N')
3261: switch (code)
3262: {
3263: case EQ: fputs ("ne", file); break;
3264: case NE: fputs ("eq", file); break;
3265: case GT: fputs ("le", file); break;
3266: case GE: fputs ("lt", file); break;
3267: case LT: fputs ("ge", file); break;
3268: case LE: fputs ("gt", file); break;
3269: case GTU: fputs ("leu", file); break;
3270: case GEU: fputs ("ltu", file); break;
3271: case LTU: fputs ("geu", file); break;
3272: case LEU: fputs ("gtu", file); break;
3273:
3274: default:
3275: abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%N");
3276: }
3277:
3278: else if (code == REG)
3279: {
3280: register int regnum = REGNO (op);
3281:
3282: if (letter == 'M')
3283: regnum += MOST_SIGNIFICANT_WORD;
3284:
3285: else if (letter == 'L')
3286: regnum += LEAST_SIGNIFICANT_WORD;
3287:
3288: else if (letter == 'D')
3289: regnum++;
3290:
3291: fprintf (file, "%s", reg_names[regnum]);
3292: }
3293:
3294: else if (code == MEM)
3295: output_address (XEXP (op, 0));
3296:
3297: else if (code == CONST_DOUBLE)
3298: {
3299: #if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
3300: union { double d; int i[2]; } u;
3301: u.i[0] = CONST_DOUBLE_LOW (op);
3302: u.i[1] = CONST_DOUBLE_HIGH (op);
3303: if (GET_MODE (op) == SFmode)
3304: {
3305: float f;
3306: f = u.d;
3307: u.d = f;
3308: }
3309: fprintf (file, "%.20e", u.d);
3310: #else
3311: fatal ("CONST_DOUBLE found in cross compilation");
3312: #endif
3313: }
3314:
3315: else if ((letter == 'x') && (GET_CODE(op) == CONST_INT))
3316: fprintf (file, "0x%04x", 0xffff & (INTVAL(op)));
3317:
3318: else if ((letter == 'X') && (GET_CODE(op) == CONST_INT))
3319: fprintf (file, "0x%08x", INTVAL(op));
3320:
3321: else if ((letter == 'd') && (GET_CODE(op) == CONST_INT))
3322: fprintf (file, "%d", (INTVAL(op)));
3323:
3324: else if (letter == 'z'
3325: && (GET_CODE (op) == CONST_INT)
3326: && INTVAL (op) == 0)
3327: fputs (reg_names[GP_REG_FIRST], file);
3328:
3329: else if (letter == 'd' || letter == 'x' || letter == 'X')
3330: fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", letter);
3331:
3332: else
3333: output_addr_const (file, op);
3334: }
3335:
3336:
3337: /* A C compound statement to output to stdio stream STREAM the
3338: assembler syntax for an instruction operand that is a memory
3339: reference whose address is ADDR. ADDR is an RTL expression.
3340:
3341: On some machines, the syntax for a symbolic address depends on
3342: the section that the address refers to. On these machines,
3343: define the macro `ENCODE_SECTION_INFO' to store the information
3344: into the `symbol_ref', and then check for it here. */
3345:
3346: void
3347: print_operand_address (file, addr)
3348: FILE *file;
3349: rtx addr;
3350: {
3351: if (!addr)
3352: error ("PRINT_OPERAND_ADDRESS, null pointer");
3353:
3354: else
3355: switch (GET_CODE (addr))
3356: {
3357: default:
3358: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #1");
3359: break;
3360:
3361: case REG:
3362: if (REGNO (addr) == ARG_POINTER_REGNUM)
3363: abort_with_insn (addr, "Arg pointer not eliminated.");
3364:
3365: fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
3366: break;
3367:
3368: case PLUS:
3369: {
3370: register rtx reg = (rtx)0;
3371: register rtx offset = (rtx)0;
3372: register rtx arg0 = XEXP (addr, 0);
3373: register rtx arg1 = XEXP (addr, 1);
3374:
3375: if (GET_CODE (arg0) == REG)
3376: {
3377: reg = arg0;
3378: offset = arg1;
3379: if (GET_CODE (offset) == REG)
3380: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
3381: }
3382: else if (GET_CODE (arg1) == REG)
3383: {
3384: reg = arg1;
3385: offset = arg0;
3386: }
3387: else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
3388: {
3389: output_addr_const (file, addr);
3390: break;
3391: }
3392: else
3393: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
3394:
3395: if (!CONSTANT_P (offset))
3396: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2");
3397:
3398: if (REGNO (reg) == ARG_POINTER_REGNUM)
3399: abort_with_insn (addr, "Arg pointer not eliminated.");
3400:
3401: output_addr_const (file, offset);
3402: fprintf (file, "(%s)", reg_names [REGNO (reg)]);
3403: }
3404: break;
3405:
3406: case LABEL_REF:
3407: case SYMBOL_REF:
3408: case CONST_INT:
3409: case CONST:
3410: output_addr_const (file, addr);
3411: break;
3412: }
3413: }
3414:
3415:
3416: /* If optimizing for the global pointer, keep track of all of
3417: the externs, so that at the end of the file, we can emit
3418: the appropriate .extern declaration for them, before writing
3419: out the text section. We assume that all names passed to
3420: us are in the permanent obstack, so that they will be valid
3421: at the end of the compilation.
3422:
3423: If we have -G 0, or the extern size is unknown, don't bother
3424: emitting the .externs. */
3425:
3426: int
3427: mips_output_external (file, decl, name)
3428: FILE *file;
3429: tree decl;
3430: char *name;
3431: {
3432: register struct extern_list *p;
3433: int len;
3434:
3435: if (TARGET_GP_OPT
3436: && mips_section_threshold != 0
3437: && ((TREE_CODE (decl)) != FUNCTION_DECL)
3438: && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0))
3439: {
3440: p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list));
3441: p->next = extern_head;
3442: p->name = name;
3443: p->size = len;
3444: extern_head = p;
3445: }
3446: return 0;
3447: }
3448:
3449:
3450: /* Compute a string to use as a temporary file name. */
3451:
3452: static FILE *
3453: make_temp_file ()
3454: {
3455: FILE *stream;
3456: char *base = getenv ("TMPDIR");
3457: int len;
3458:
3459: if (base == (char *)0)
3460: {
3461: #ifdef P_tmpdir
3462: if (access (P_tmpdir, R_OK | W_OK) == 0)
3463: base = P_tmpdir;
3464: else
3465: #endif
3466: if (access ("/usr/tmp", R_OK | W_OK) == 0)
3467: base = "/usr/tmp/";
3468: else
3469: base = "/tmp/";
3470: }
3471:
3472: len = strlen (base);
3473: temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX"));
3474: strcpy (temp_filename, base);
3475: if (len > 0 && temp_filename[len-1] != '/')
3476: temp_filename[len++] = '/';
3477:
3478: strcpy (temp_filename + len, "ccXXXXXX");
3479: mktemp (temp_filename);
3480:
3481: stream = fopen (temp_filename, "w+");
3482: if (!stream)
3483: pfatal_with_name (temp_filename);
3484:
3485: unlink (temp_filename);
3486: return stream;
3487: }
3488:
3489:
3490: /* Emit a new filename to a stream. If this is MIPS ECOFF, watch out
3491: for .file's that start within a function. If we are smuggling stabs, try to
3492: put out a MIPS ECOFF file and a stab. */
3493:
3494: void
3495: mips_output_filename (stream, name)
3496: FILE *stream;
3497: char *name;
3498: {
3499: static int first_time = TRUE;
3500: char ltext_label_name[100];
3501:
3502: if (first_time)
3503: {
3504: first_time = FALSE;
3505: SET_FILE_NUMBER ();
3506: current_function_file = name;
3507: fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name);
3508: if (!TARGET_GAS && write_symbols == DBX_DEBUG)
3509: fprintf (stream, "\t#@stabs\n");
3510: }
3511:
3512: else if (!TARGET_GAS && write_symbols == DBX_DEBUG)
3513: {
3514: ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
3515: fprintf (stream, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP,
3516: name, N_SOL, <ext_label_name[1]);
3517: }
3518:
3519: else if (name != current_function_file
3520: && strcmp (name, current_function_file) != 0)
3521: {
3522: if (inside_function && !TARGET_GAS)
3523: {
3524: if (!file_in_function_warning)
3525: {
3526: file_in_function_warning = TRUE;
3527: ignore_line_number = TRUE;
3528: warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
3529: }
3530:
3531: fprintf (stream, "\t#.file\t%d \"%s\"\n", num_source_filenames, name);
3532: }
3533:
3534: else
3535: {
3536: SET_FILE_NUMBER ();
3537: current_function_file = name;
3538: fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name);
3539: }
3540: }
3541: }
3542:
3543:
3544: /* Emit a linenumber. For encapsulated stabs, we need to put out a stab
3545: as well as a .loc, since it is possible that MIPS ECOFF might not be
3546: able to represent the location for inlines that come from a different
3547: file. */
3548:
3549: void
3550: mips_output_lineno (stream, line)
3551: FILE *stream;
3552: int line;
3553: {
3554: if (!TARGET_GAS && write_symbols == DBX_DEBUG)
3555: {
3556: ++sym_lineno;
3557: fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n",
3558: sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno);
3559: }
3560:
3561: else
3562: {
3563: fprintf (stream, "\n\t%s.loc\t%d %d\n",
3564: (ignore_line_number) ? "#" : "",
3565: num_source_filenames, line);
3566:
3567: LABEL_AFTER_LOC (stream);
3568: }
3569: }
3570:
3571:
3572: /* If defined, a C statement to be executed just prior to the
3573: output of assembler code for INSN, to modify the extracted
3574: operands so they will be output differently.
3575:
3576: Here the argument OPVEC is the vector containing the operands
3577: extracted from INSN, and NOPERANDS is the number of elements of
3578: the vector which contain meaningful data for this insn. The
3579: contents of this vector are what will be used to convert the
3580: insn template into assembler code, so you can change the
3581: assembler output by changing the contents of the vector.
3582:
3583: We use it to check if the current insn needs a nop in front of it
3584: because of load delays, and also to update the delay slot
3585: statistics. */
3586:
3587: void
3588: final_prescan_insn (insn, opvec, noperands)
3589: rtx insn;
3590: rtx opvec[];
3591: int noperands;
3592: {
3593: if (dslots_number_nops > 0)
3594: {
3595: rtx pattern = PATTERN (insn);
3596: int length = get_attr_length (insn);
3597:
3598: /* Do we need to emit a NOP? */
3599: if (length == 0
3600: || (mips_load_reg != (rtx)0 && reg_mentioned_p (mips_load_reg, pattern))
3601: || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern))
3602: || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern))
3603: || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern)))
3604: fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
3605:
3606: else
3607: dslots_load_filled++;
3608:
3609: while (--dslots_number_nops > 0)
3610: fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
3611:
3612: mips_load_reg = (rtx)0;
3613: mips_load_reg2 = (rtx)0;
3614: mips_load_reg3 = (rtx)0;
3615: mips_load_reg4 = (rtx)0;
3616:
3617: if (set_noreorder && --set_noreorder == 0)
3618: fputs ("\t.set\treorder\n", asm_out_file);
3619: }
3620:
3621: if (TARGET_STATS)
3622: {
3623: enum rtx_code code = GET_CODE (insn);
3624: if (code == JUMP_INSN || code == CALL_INSN)
3625: dslots_jump_total++;
3626: }
3627: }
3628:
3629:
3630: /* Output at beginning of assembler file.
3631: If we are optimizing to use the global pointer, create a temporary
3632: file to hold all of the text stuff, and write it out to the end.
3633: This is needed because the MIPS assembler is evidently one pass,
3634: and if it hasn't seen the relevant .comm/.lcomm/.extern/.sdata
3635: declaration when the code is processed, it generates a two
3636: instruction sequence. */
3637:
3638: void
3639: mips_asm_file_start (stream)
3640: FILE *stream;
3641: {
3642: ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
3643:
3644: /* Versions of the MIPS assembler before 2.20 generate errors
3645: if a branch inside of a .set noreorder section jumps to a
3646: label outside of the .set noreorder section. Revision 2.20
3647: just set nobopt silently rather than fixing the bug. */
3648:
3649: if (TARGET_MIPS_AS && optimize && flag_delayed_branch)
3650: fprintf (stream, "\t.set\tnobopt\n");
3651:
3652: /* Generate the pseudo ops that the Pyramid based System V.4 wants. */
3653: if (TARGET_ABICALLS)
3654: fprintf (stream, "\t.abicalls\n");
3655:
3656: if (TARGET_GP_OPT)
3657: {
3658: asm_out_data_file = stream;
3659: asm_out_text_file = make_temp_file ();
3660: }
3661: else
3662: asm_out_data_file = asm_out_text_file = stream;
3663:
3664: if (TARGET_NAME_REGS)
3665: fprintf (asm_out_file, "#include <regdef.h>\n");
3666:
3667: print_options (stream);
3668: }
3669:
3670:
3671: /* If we are optimizing the global pointer, emit the text section now
3672: and any small externs which did not have .comm, etc that are
3673: needed. Also, give a warning if the data area is more than 32K and
3674: -pic because 3 instructions are needed to reference the data
3675: pointers. */
3676:
3677: void
3678: mips_asm_file_end (file)
3679: FILE *file;
3680: {
3681: char buffer[8192];
3682: tree name_tree;
3683: struct extern_list *p;
3684: int len;
3685:
3686: if (HALF_PIC_P ())
3687: HALF_PIC_FINISH (file);
3688:
3689: if (TARGET_GP_OPT)
3690: {
3691: if (extern_head)
3692: fputs ("\n", file);
3693:
3694: for (p = extern_head; p != 0; p = p->next)
3695: {
3696: name_tree = get_identifier (p->name);
3697:
3698: /* Positively ensure only one .extern for any given symbol. */
3699: if (! TREE_ASM_WRITTEN (name_tree))
3700: {
3701: TREE_ASM_WRITTEN (name_tree) = 1;
3702: fputs ("\t.extern\t", file);
3703: assemble_name (file, p->name);
3704: fprintf (file, ", %d\n", p->size);
3705: }
3706: }
3707:
3708: fprintf (file, "\n\t.text\n");
3709: rewind (asm_out_text_file);
3710: if (ferror (asm_out_text_file))
3711: fatal_io_error (temp_filename);
3712:
3713: while ((len = fread (buffer, 1, sizeof (buffer), asm_out_text_file)) > 0)
3714: if (fwrite (buffer, 1, len, file) != len)
3715: pfatal_with_name (asm_file_name);
3716:
3717: if (len < 0)
3718: pfatal_with_name (temp_filename);
3719:
3720: if (fclose (asm_out_text_file) != 0)
3721: pfatal_with_name (temp_filename);
3722: }
3723: }
3724:
3725:
3726: /* Emit either a label, .comm, or .lcomm directive, and mark
3727: that the symbol is used, so that we don't emit an .extern
3728: for it in mips_asm_file_end. */
3729:
3730: void
3731: mips_declare_object (stream, name, init_string, final_string, size)
3732: FILE *stream;
3733: char *name;
3734: char *init_string;
3735: char *final_string;
3736: int size;
3737: {
3738: fputs (init_string, stream); /* "", "\t.comm\t", or "\t.lcomm\t" */
3739: assemble_name (stream, name);
3740: fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */
3741:
3742: if (TARGET_GP_OPT && mips_section_threshold != 0)
3743: {
3744: tree name_tree = get_identifier (name);
3745: TREE_ASM_WRITTEN (name_tree) = 1;
3746: }
3747: }
3748:
3749:
3750: /* Output a double precision value to the assembler. If both the
3751: host and target are IEEE, emit the values in hex. */
3752:
3753: void
3754: mips_output_double (stream, value)
3755: FILE *stream;
3756: REAL_VALUE_TYPE value;
3757: {
3758: #ifdef REAL_VALUE_TO_TARGET_DOUBLE
3759: long value_long[2];
3760: REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
3761:
3762: fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n",
3763: value_long[0], value, value_long[1]);
3764: #else
3765: fprintf (stream, "\t.double\t%.20g\n", value);
3766: #endif
3767: }
3768:
3769:
3770: /* Output a single precision value to the assembler. If both the
3771: host and target are IEEE, emit the values in hex. */
3772:
3773: void
3774: mips_output_float (stream, value)
3775: FILE *stream;
3776: REAL_VALUE_TYPE value;
3777: {
3778: #ifdef REAL_VALUE_TO_TARGET_SINGLE
3779: long value_long;
3780: REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
3781:
3782: fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value);
3783: #else
3784: fprintf (stream, "\t.float\t%.12g\n", value);
3785: #endif
3786: }
3787:
3788:
3789: /* Return TRUE if any register used in the epilogue is used. This to insure
3790: any insn put into the epilogue delay slots is safe. */
3791:
3792: int
3793: epilogue_reg_mentioned_p (insn)
3794: rtx insn;
3795: {
3796: register char *fmt;
3797: register int i;
3798: register enum rtx_code code;
3799: register int regno;
3800:
3801: if (insn == (rtx)0)
3802: return 0;
3803:
3804: if (GET_CODE (insn) == LABEL_REF)
3805: return 0;
3806:
3807: code = GET_CODE (insn);
3808: switch (code)
3809: {
3810: case REG:
3811: regno = REGNO (insn);
3812: if (regno == STACK_POINTER_REGNUM)
3813: return 1;
3814:
3815: if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)
3816: return 1;
3817:
3818: if (!call_used_regs[regno])
3819: return 1;
3820:
3821: if (regno != MIPS_TEMP1_REGNUM && regno != MIPS_TEMP2_REGNUM)
3822: return 0;
3823:
3824: if (!current_frame_info.initialized)
3825: compute_frame_size (get_frame_size ());
3826:
3827: return (current_frame_info.total_size >= 32768);
3828:
3829: case SCRATCH:
3830: case CC0:
3831: case PC:
3832: case CONST_INT:
3833: case CONST_DOUBLE:
3834: return 0;
3835: }
3836:
3837: fmt = GET_RTX_FORMAT (code);
3838: for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
3839: {
3840: if (fmt[i] == 'E')
3841: {
3842: register int j;
3843: for (j = XVECLEN (insn, i) - 1; j >= 0; j--)
3844: if (epilogue_reg_mentioned_p (XVECEXP (insn, i, j)))
3845: return 1;
3846: }
3847: else if (fmt[i] == 'e' && epilogue_reg_mentioned_p (XEXP (insn, i)))
3848: return 1;
3849: }
3850:
3851: return 0;
3852: }
3853:
3854:
3855: /* Return the bytes needed to compute the frame pointer from the current
3856: stack pointer.
3857:
3858: Mips stack frames look like:
3859:
3860: Before call After call
3861: +-----------------------+ +-----------------------+
3862: high | | | |
3863: mem. | | | |
3864: | caller's temps. | | caller's temps. |
3865: | | | |
3866: +-----------------------+ +-----------------------+
3867: | | | |
3868: | arguments on stack. | | arguments on stack. |
3869: | | | |
3870: +-----------------------+ +-----------------------+
3871: | 4 words to save | | 4 words to save |
3872: | arguments passed | | arguments passed |
3873: | in registers, even | | in registers, even |
3874: SP->| if not passed. | FP->| if not passed. |
3875: +-----------------------+ +-----------------------+
3876: | |
3877: | GP save for V.4 abi |
3878: | |
3879: +-----------------------+
3880: | |
3881: | fp register save |
3882: | |
3883: +-----------------------+
3884: | |
3885: | gp register save |
3886: | |
3887: +-----------------------+
3888: | |
3889: | local variables |
3890: | |
3891: +-----------------------+
3892: | |
3893: | alloca allocations |
3894: | |
3895: +-----------------------+
3896: | |
3897: | arguments on stack |
3898: | |
3899: +-----------------------+
3900: | 4 words to save |
3901: | arguments passed |
3902: | in registers, even |
3903: low SP->| if not passed. |
3904: memory +-----------------------+
3905:
3906: */
3907:
3908: long
3909: compute_frame_size (size)
3910: int size; /* # of var. bytes allocated */
3911: {
3912: int regno;
3913: long total_size; /* # bytes that the entire frame takes up */
3914: long var_size; /* # bytes that variables take up */
3915: long args_size; /* # bytes that outgoing arguments take up */
3916: long extra_size; /* # extra bytes */
3917: long gp_reg_rounded; /* # bytes needed to store gp after rounding */
3918: long gp_reg_size; /* # bytes needed to store gp regs */
3919: long fp_reg_size; /* # bytes needed to store fp regs */
3920: long mask; /* mask of saved gp registers */
3921: long fmask; /* mask of saved fp registers */
3922: int fp_inc; /* 1 or 2 depending on the size of fp regs */
3923: long fp_bits; /* bitmask to use for each fp register */
3924:
3925: gp_reg_size = 0;
3926: fp_reg_size = 0;
3927: mask = 0;
3928: fmask = 0;
3929: extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
3930: var_size = MIPS_STACK_ALIGN (size);
3931: args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
3932:
3933: /* The MIPS 3.0 linker does not like functions that dynamically
3934: allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
3935: looks like we are trying to create a second frame pointer to the
3936: function, so allocate some stack space to make it happy. */
3937:
3938: if (args_size == 0 && current_function_calls_alloca)
3939: args_size = 4*UNITS_PER_WORD;
3940:
3941: total_size = var_size + args_size + extra_size;
3942:
3943: /* Calculate space needed for gp registers. */
3944: for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
3945: {
3946: if (MUST_SAVE_REGISTER (regno))
3947: {
3948: gp_reg_size += UNITS_PER_WORD;
3949: mask |= 1L << (regno - GP_REG_FIRST);
3950: }
3951: }
3952:
3953: /* Calculate space needed for fp registers. */
3954: if (TARGET_FLOAT64)
3955: {
3956: fp_inc = 1;
3957: fp_bits = 1;
3958: }
3959: else
3960: {
3961: fp_inc = 2;
3962: fp_bits = 3;
3963: }
3964:
3965: for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += fp_inc)
3966: {
3967: if (regs_ever_live[regno] && !call_used_regs[regno])
3968: {
3969: fp_reg_size += 2*UNITS_PER_WORD;
3970: fmask |= fp_bits << (regno - FP_REG_FIRST);
3971: }
3972: }
3973:
3974: gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
3975: total_size += gp_reg_rounded + fp_reg_size;
3976:
3977: if (total_size == extra_size)
3978: total_size = extra_size = 0;
3979:
3980: /* Save other computed information. */
3981: current_frame_info.total_size = total_size;
3982: current_frame_info.var_size = var_size;
3983: current_frame_info.args_size = args_size;
3984: current_frame_info.extra_size = extra_size;
3985: current_frame_info.gp_reg_size = gp_reg_size;
3986: current_frame_info.fp_reg_size = fp_reg_size;
3987: current_frame_info.mask = mask;
3988: current_frame_info.fmask = fmask;
3989: current_frame_info.initialized = reload_completed;
3990: current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
3991: current_frame_info.num_fp = fp_reg_size / (2*UNITS_PER_WORD);
3992:
3993: if (mask)
3994: {
3995: unsigned long offset = args_size + var_size + gp_reg_size - UNITS_PER_WORD;
3996: current_frame_info.gp_sp_offset = offset;
3997: current_frame_info.gp_save_offset = offset - total_size;
3998: }
3999: else
4000: {
4001: current_frame_info.gp_sp_offset = 0;
4002: current_frame_info.gp_save_offset = 0;
4003: }
4004:
4005:
4006: if (fmask)
4007: {
4008: unsigned long offset = args_size + var_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD;
4009: current_frame_info.fp_sp_offset = offset;
4010: current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
4011: }
4012: else
4013: {
4014: current_frame_info.fp_sp_offset = 0;
4015: current_frame_info.fp_save_offset = 0;
4016: }
4017:
4018: /* Ok, we're done. */
4019: return total_size;
4020: }
4021:
4022:
4023: /* Common code to emit the insns (or to write the instructions to a file)
4024: to save/restore registers.
4025:
4026: Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg)
4027: is not modified within save_restore_insns. */
4028:
4029: #define BITSET_P(value,bit) (((value) & (1L << (bit))) != 0)
4030:
4031: static void
4032: save_restore_insns (store_p, large_reg, large_offset, file)
4033: int store_p; /* true if this is prologue */
4034: rtx large_reg; /* register holding large offset constant or NULL */
4035: long large_offset; /* large constant offset value */
4036: FILE *file; /* file to write instructions to instead of making RTL */
4037: {
4038: long mask = current_frame_info.mask;
4039: long fmask = current_frame_info.fmask;
4040: int regno;
4041: rtx base_reg_rtx;
4042: long base_offset;
4043: long gp_offset;
4044: long fp_offset;
4045: long end_offset;
4046:
4047: if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST))
4048: abort ();
4049:
4050: if (mask == 0 && fmask == 0)
4051: return;
4052:
4053: /* Save registers starting from high to low. The debuggers prefer
4054: at least the return register be stored at func+4, and also it
4055: allows us not to need a nop in the epilog if at least one
4056: register is reloaded in addition to return address. */
4057:
4058: /* Save GP registers if needed. */
4059: if (mask)
4060: {
4061: /* Pick which pointer to use as a base register. For small
4062: frames, just use the stack pointer. Otherwise, use a
4063: temporary register. Save 2 cycles if the save area is near
4064: the end of a large frame, by reusing the constant created in
4065: the prologue/epilogue to adjust the stack frame. */
4066:
4067: gp_offset = current_frame_info.gp_sp_offset;
4068: end_offset = gp_offset - (current_frame_info.gp_reg_size - UNITS_PER_WORD);
4069:
4070: if (gp_offset < 0 || end_offset < 0)
4071: fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
4072: gp_offset, end_offset);
4073:
4074: else if (gp_offset < 32768)
4075: {
4076: base_reg_rtx = stack_pointer_rtx;
4077: base_offset = 0;
4078: }
4079:
4080: else if (large_reg != (rtx)0
4081: && (((unsigned long)(large_offset - gp_offset)) < 32768)
4082: && (((unsigned long)(large_offset - end_offset)) < 32768))
4083: {
4084: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
4085: base_offset = large_offset;
4086: if (file == (FILE *)0)
4087: emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
4088: else
4089: fprintf (file, "\taddu\t%s,%s,%s\n",
4090: reg_names[MIPS_TEMP2_REGNUM],
4091: reg_names[REGNO (large_reg)],
4092: reg_names[STACK_POINTER_REGNUM]);
4093: }
4094:
4095: else
4096: {
4097: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
4098: base_offset = gp_offset;
4099: if (file == (FILE *)0)
4100: {
4101: emit_move_insn (base_reg_rtx, GEN_INT (gp_offset));
4102: emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
4103: }
4104: else
4105: fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
4106: reg_names[MIPS_TEMP2_REGNUM],
4107: (long)base_offset,
4108: (long)base_offset,
4109: reg_names[MIPS_TEMP2_REGNUM],
4110: reg_names[MIPS_TEMP2_REGNUM],
4111: reg_names[STACK_POINTER_REGNUM]);
4112: }
4113:
4114: for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
4115: {
4116: if (BITSET_P (mask, regno - GP_REG_FIRST))
4117: {
4118: if (file == (FILE *)0)
4119: {
4120: rtx reg_rtx = gen_rtx (REG, Pmode, regno);
4121: rtx mem_rtx = gen_rtx (MEM, Pmode,
4122: gen_rtx (PLUS, Pmode, base_reg_rtx,
4123: GEN_INT (gp_offset - base_offset)));
4124:
4125: if (store_p)
4126: emit_move_insn (mem_rtx, reg_rtx);
4127: else
4128: emit_move_insn (reg_rtx, mem_rtx);
4129: }
4130: else
4131: fprintf (file, "\t%s\t%s,%ld(%s)\n",
4132: (store_p) ? "sw" : "lw",
4133: reg_names[regno],
4134: gp_offset - base_offset,
4135: reg_names[REGNO(base_reg_rtx)]);
4136:
4137: gp_offset -= UNITS_PER_WORD;
4138: }
4139: }
4140: }
4141: else
4142: {
4143: base_reg_rtx = (rtx)0; /* Make sure these are initialzed */
4144: base_offset = 0;
4145: }
4146:
4147: /* Save floating point registers if needed. */
4148: if (fmask)
4149: {
4150: int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
4151:
4152: /* Pick which pointer to use as a base register. */
4153: fp_offset = current_frame_info.fp_sp_offset;
4154: end_offset = fp_offset - (current_frame_info.fp_reg_size - 2*UNITS_PER_WORD);
4155:
4156: if (fp_offset < 0 || end_offset < 0)
4157: fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.",
4158: fp_offset, end_offset);
4159:
4160: else if (fp_offset < 32768)
4161: {
4162: base_reg_rtx = stack_pointer_rtx;
4163: base_offset = 0;
4164: }
4165:
4166: else if (base_reg_rtx != (rtx)0
4167: && (((unsigned long)(base_offset - fp_offset)) < 32768)
4168: && (((unsigned long)(base_offset - end_offset)) < 32768))
4169: {
4170: ; /* already set up for gp registers above */
4171: }
4172:
4173: else if (large_reg != (rtx)0
4174: && (((unsigned long)(large_offset - fp_offset)) < 32768)
4175: && (((unsigned long)(large_offset - end_offset)) < 32768))
4176: {
4177: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
4178: base_offset = large_offset;
4179: if (file == (FILE *)0)
4180: emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
4181: else
4182: fprintf (file, "\taddu\t%s,%s,%s\n",
4183: reg_names[MIPS_TEMP2_REGNUM],
4184: reg_names[REGNO (large_reg)],
4185: reg_names[STACK_POINTER_REGNUM]);
4186: }
4187:
4188: else
4189: {
4190: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
4191: base_offset = fp_offset;
4192: if (file == (FILE *)0)
4193: {
4194: emit_move_insn (base_reg_rtx, GEN_INT (fp_offset));
4195: emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
4196: }
4197: else
4198: fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
4199: reg_names[MIPS_TEMP2_REGNUM],
4200: (long)base_offset,
4201: (long)base_offset,
4202: reg_names[MIPS_TEMP2_REGNUM],
4203: reg_names[MIPS_TEMP2_REGNUM],
4204: reg_names[STACK_POINTER_REGNUM]);
4205: }
4206:
4207: for (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc)
4208: {
4209: if (BITSET_P (fmask, regno - FP_REG_FIRST))
4210: {
4211: if (file == (FILE *)0)
4212: {
4213: rtx reg_rtx = gen_rtx (REG, DFmode, regno);
4214: rtx mem_rtx = gen_rtx (MEM, DFmode,
4215: gen_rtx (PLUS, Pmode, base_reg_rtx,
4216: GEN_INT (fp_offset - base_offset)));
4217:
4218: if (store_p)
4219: emit_move_insn (mem_rtx, reg_rtx);
4220: else
4221: emit_move_insn (reg_rtx, mem_rtx);
4222: }
4223: else
4224: fprintf (file, "\t%s\t%s,%ld(%s)\n",
4225: (store_p) ? "s.d" : "l.d",
4226: reg_names[regno],
4227: fp_offset - base_offset,
4228: reg_names[REGNO(base_reg_rtx)]);
4229:
4230:
4231: fp_offset -= 2*UNITS_PER_WORD;
4232: }
4233: }
4234: }
4235: }
4236:
4237:
4238: /* Set up the stack and frame (if desired) for the function. */
4239:
4240: void
4241: function_prologue (file, size)
4242: FILE *file;
4243: int size;
4244: {
4245: long tsize = current_frame_info.total_size;
4246:
4247: ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
4248:
4249: if (debug_info_level != DINFO_LEVEL_TERSE)
4250: ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
4251:
4252: inside_function = 1;
4253: fputs ("\t.ent\t", file);
4254: assemble_name (file, current_function_name);
4255: fputs ("\n", file);
4256:
4257: assemble_name (file, current_function_name);
4258: fputs (":\n", file);
4259:
4260: if (TARGET_ABICALLS)
4261: fprintf (file,
4262: "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
4263: reg_names[ GP_REG_FIRST + 25 ]);
4264:
4265: tsize = current_frame_info.total_size;
4266: if (tsize > 0 && TARGET_ABICALLS)
4267: fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET);
4268:
4269: fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n",
4270: reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ],
4271: tsize,
4272: reg_names[31 + GP_REG_FIRST],
4273: current_frame_info.var_size,
4274: current_frame_info.num_gp,
4275: current_frame_info.num_fp,
4276: current_function_outgoing_args_size,
4277: current_frame_info.extra_size);
4278:
4279: fprintf (file, "\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n",
4280: current_frame_info.mask,
4281: current_frame_info.gp_save_offset,
4282: current_frame_info.fmask,
4283: current_frame_info.fp_save_offset);
4284: }
4285:
4286:
4287: /* Expand the prologue into a bunch of separate insns. */
4288:
4289: void
4290: mips_expand_prologue ()
4291: {
4292: int regno;
4293: long tsize;
4294: rtx tmp_rtx = (rtx)0;
4295: char *arg_name = (char *)0;
4296: tree fndecl = current_function_decl;
4297: tree fntype = TREE_TYPE (fndecl);
4298: tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
4299: ? DECL_ARGUMENTS (fndecl)
4300: : 0;
4301: rtx next_arg_reg;
4302: int i;
4303: tree next_arg;
4304: tree cur_arg;
4305: CUMULATIVE_ARGS args_so_far;
4306:
4307: /* Determine the last argument, and get its name. */
4308:
4309: INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
4310: regno = GP_ARG_FIRST;
4311:
4312: for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
4313: {
4314: tree type = DECL_ARG_TYPE (cur_arg);
4315: enum machine_mode passed_mode = TYPE_MODE (type);
4316: rtx entry_parm = FUNCTION_ARG (args_so_far,
4317: passed_mode,
4318: DECL_ARG_TYPE (cur_arg),
4319: 1);
4320:
4321: if (entry_parm)
4322: {
4323: int words;
4324:
4325: /* passed in a register, so will get homed automatically */
4326: if (GET_MODE (entry_parm) == BLKmode)
4327: words = (int_size_in_bytes (type) + 3) / 4;
4328: else
4329: words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
4330:
4331: regno = REGNO (entry_parm) + words - 1;
4332: }
4333: else
4334: {
4335: regno = GP_ARG_LAST+1;
4336: break;
4337: }
4338:
4339: FUNCTION_ARG_ADVANCE (args_so_far,
4340: passed_mode,
4341: DECL_ARG_TYPE (cur_arg),
4342: 1);
4343:
4344: next_arg = TREE_CHAIN (cur_arg);
4345: if (next_arg == (tree)0)
4346: {
4347: if (DECL_NAME (cur_arg))
4348: arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg));
4349:
4350: break;
4351: }
4352: }
4353:
4354: /* In order to pass small structures by value in registers
4355: compatibly with the MIPS compiler, we need to shift the value
4356: into the high part of the register. Function_arg has encoded a
4357: PARALLEL rtx, holding a vector of adjustments to be made as the
4358: next_arg_reg variable, so we split up the insns, and emit them
4359: separately. */
4360:
4361: next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
4362: if (next_arg_reg != (rtx)0 && GET_CODE (next_arg_reg) == PARALLEL)
4363: {
4364: rtvec adjust = XVEC (next_arg_reg, 0);
4365: int num = GET_NUM_ELEM (adjust);
4366:
4367: for (i = 0; i < num; i++)
4368: {
4369: rtx pattern = RTVEC_ELT (adjust, i);
4370: if (GET_CODE (pattern) != SET
4371: || GET_CODE (SET_SRC (pattern)) != ASHIFT)
4372: abort_with_insn (pattern, "Insn is not a shift");
4373:
4374: PUT_CODE (SET_SRC (pattern), ASHIFTRT);
4375: emit_insn (pattern);
4376: }
4377: }
4378:
4379: /* If this function is a varargs function, store any registers that
4380: would normally hold arguments ($4 - $7) on the stack. */
4381: if ((TYPE_ARG_TYPES (fntype) != 0
4382: && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
4383: || (arg_name != (char *)0
4384: && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0)
4385: || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0))))
4386: {
4387: for (; regno <= GP_ARG_LAST; regno++)
4388: {
4389: rtx ptr = stack_pointer_rtx;
4390: if (regno != GP_ARG_FIRST)
4391: ptr = gen_rtx (PLUS, Pmode, ptr,
4392: GEN_INT ((regno - GP_ARG_FIRST) * UNITS_PER_WORD));
4393:
4394: emit_move_insn (gen_rtx (MEM, Pmode, ptr), gen_rtx (REG, Pmode, regno));
4395: }
4396: }
4397:
4398: tsize = compute_frame_size (get_frame_size ());
4399: if (tsize > 0)
4400: {
4401: rtx tsize_rtx = GEN_INT (tsize);
4402:
4403: if (tsize > 32767)
4404: {
4405: tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
4406: emit_move_insn (tmp_rtx, tsize_rtx);
4407: tsize_rtx = tmp_rtx;
4408: }
4409:
4410: emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
4411:
4412: save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
4413:
4414: if (frame_pointer_needed)
4415: emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
4416: }
4417:
4418: /* If we are profiling, make sure no instructions are scheduled before
4419: the call to mcount. */
4420:
4421: if (profile_flag || profile_block_flag)
4422: emit_insn (gen_blockage ());
4423: }
4424:
4425:
4426: /* Do any necessary cleanup after a function to restore stack, frame, and regs. */
4427:
4428: #define RA_MASK ((long) 0x80000000) /* 1 << 31 */
4429:
4430: void
4431: function_epilogue (file, size)
4432: FILE *file;
4433: int size;
4434: {
4435: long tsize;
4436: char *sp_str = reg_names[STACK_POINTER_REGNUM];
4437: char *t1_str = reg_names[MIPS_TEMP1_REGNUM];
4438: rtx epilogue_delay = current_function_epilogue_delay_list;
4439: int noreorder = !TARGET_MIPS_AS || (epilogue_delay != 0);
4440: int noepilogue = FALSE;
4441: int load_nop = FALSE;
4442: int load_only_r31;
4443: rtx tmp_rtx = (rtx)0;
4444: rtx restore_rtx;
4445: int i;
4446:
4447: /* The epilogue does not depend on any registers, but the stack
4448: registers, so we assume that if we have 1 pending nop, it can be
4449: ignored, and 2 it must be filled (2 nops occur for integer
4450: multiply and divide). */
4451:
4452: if (dslots_number_nops > 0)
4453: {
4454: if (dslots_number_nops == 1)
4455: {
4456: dslots_number_nops = 0;
4457: dslots_load_filled++;
4458: }
4459: else
4460: {
4461: while (--dslots_number_nops > 0)
4462: fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
4463: }
4464:
4465: if (set_noreorder > 0 && --set_noreorder == 0)
4466: fputs ("\t.set\treorder\n", file);
4467: }
4468:
4469: if (set_noat != 0)
4470: {
4471: set_noat = 0;
4472: fputs ("\t.set\tat\n", file);
4473: error ("internal gcc error: .set noat left on in epilogue");
4474: }
4475:
4476: if (set_nomacro != 0)
4477: {
4478: set_nomacro = 0;
4479: fputs ("\t.set\tmacro\n", file);
4480: error ("internal gcc error: .set nomacro left on in epilogue");
4481: }
4482:
4483: if (set_noreorder != 0)
4484: {
4485: set_noreorder = 0;
4486: fputs ("\t.set\treorder\n", file);
4487: error ("internal gcc error: .set noreorder left on in epilogue");
4488: }
4489:
4490: if (set_volatile != 0)
4491: {
4492: set_volatile = 0;
4493: fprintf (file, "\t#.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#");
4494: error ("internal gcc error: .set volatile left on in epilogue");
4495: }
4496:
4497: size = MIPS_STACK_ALIGN (size);
4498: tsize = (!current_frame_info.initialized)
4499: ? compute_frame_size (size)
4500: : current_frame_info.total_size;
4501:
4502: if (tsize == 0 && epilogue_delay == 0)
4503: {
4504: rtx insn = get_last_insn ();
4505:
4506: /* If the last insn was a BARRIER, we don't have to write any code
4507: because a jump (aka return) was put there. */
4508: if (GET_CODE (insn) == NOTE)
4509: insn = prev_nonnote_insn (insn);
4510: if (insn && GET_CODE (insn) == BARRIER)
4511: noepilogue = TRUE;
4512:
4513: noreorder = FALSE;
4514: }
4515:
4516: if (!noepilogue)
4517: {
4518: /* In the reload sequence, we don't need to fill the load delay
4519: slots for most of the loads, also see if we can fill the final
4520: delay slot if not otherwise filled by the reload sequence. */
4521:
4522: if (noreorder)
4523: fprintf (file, "\t.set\tnoreorder\n");
4524:
4525: if (tsize > 32767)
4526: {
4527: fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n", t1_str, (long)tsize, (long)tsize);
4528: tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
4529: }
4530:
4531: if (frame_pointer_needed)
4532: fprintf (file, "\tmove\t%s,%s\t\t\t# sp not trusted here\n",
4533: sp_str, reg_names[FRAME_POINTER_REGNUM]);
4534:
4535: save_restore_insns (FALSE, tmp_rtx, tsize, file);
4536:
4537: load_only_r31 = (current_frame_info.mask == RA_MASK
4538: && current_frame_info.fmask == 0);
4539:
4540: if (noreorder)
4541: {
4542: /* If the only register saved is the return address, we need a
4543: nop, unless we have an instruction to put into it. Otherwise
4544: we don't since reloading multiple registers doesn't reference
4545: the register being loaded. */
4546:
4547: if (load_only_r31)
4548: {
4549: if (epilogue_delay)
4550: final_scan_insn (XEXP (epilogue_delay, 0),
4551: file,
4552: 1, /* optimize */
4553: -2, /* prescan */
4554: 1); /* nopeepholes */
4555: else
4556: {
4557: fprintf (file, "\tnop\n");
4558: load_nop = TRUE;
4559: }
4560: }
4561:
4562: fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
4563:
4564: if (tsize > 32767)
4565: fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
4566:
4567: else if (tsize > 0)
4568: fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
4569:
4570: else if (!load_only_r31 && epilogue_delay != 0)
4571: final_scan_insn (XEXP (epilogue_delay, 0),
4572: file,
4573: 1, /* optimize */
4574: -2, /* prescan */
4575: 1); /* nopeepholes */
4576:
4577: fprintf (file, "\t.set\treorder\n");
4578: }
4579:
4580: else
4581: {
4582: if (tsize > 32767)
4583: fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
4584:
4585: else if (tsize > 0)
4586: fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
4587:
4588: fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
4589: }
4590: }
4591:
4592: fputs ("\t.end\t", file);
4593: assemble_name (file, current_function_name);
4594: fputs ("\n", file);
4595:
4596: if (TARGET_STATS)
4597: {
4598: int num_gp_regs = current_frame_info.gp_reg_size / 4;
4599: int num_fp_regs = current_frame_info.fp_reg_size / 8;
4600: int num_regs = num_gp_regs + num_fp_regs;
4601: char *name = current_function_name;
4602:
4603: if (name[0] == '*')
4604: name++;
4605:
4606: dslots_load_total += num_regs;
4607:
4608: if (!noepilogue)
4609: dslots_jump_total++;
4610:
4611: if (noreorder)
4612: {
4613: dslots_load_filled += num_regs;
4614:
4615: /* If the only register saved is the return register, we
4616: can't fill this register's delay slot. */
4617:
4618: if (load_only_r31 && epilogue_delay == 0)
4619: dslots_load_filled--;
4620:
4621: if (tsize > 0 || (!load_only_r31 && epilogue_delay != 0))
4622: dslots_jump_filled++;
4623: }
4624:
4625: fprintf (stderr,
4626: "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3ld reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d",
4627: name,
4628: (frame_pointer_needed) ? 'y' : 'n',
4629: ((current_frame_info.mask & RA_MASK) != 0) ? 'n' : 'y',
4630: (current_function_calls_alloca) ? 'y' : 'n',
4631: (current_function_calls_setjmp) ? 'y' : 'n',
4632: (long)current_frame_info.total_size,
4633: (long)current_function_outgoing_args_size,
4634: num_gp_regs, num_fp_regs,
4635: dslots_load_total, dslots_load_filled,
4636: dslots_jump_total, dslots_jump_filled,
4637: num_refs[0], num_refs[1], num_refs[2]);
4638:
4639: if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs)
4640: {
4641: fprintf (stderr, " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs);
4642: prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS;
4643: }
4644:
4645: if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs)
4646: {
4647: fprintf (stderr, " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs);
4648: prev_half_pic_refs = HALF_PIC_NUMBER_REFS;
4649: }
4650:
4651: fputc ('\n', stderr);
4652: }
4653:
4654: /* Reset state info for each function. */
4655: inside_function = FALSE;
4656: ignore_line_number = FALSE;
4657: dslots_load_total = 0;
4658: dslots_jump_total = 0;
4659: dslots_load_filled = 0;
4660: dslots_jump_filled = 0;
4661: num_refs[0] = 0;
4662: num_refs[1] = 0;
4663: num_refs[2] = 0;
4664: mips_load_reg = (rtx)0;
4665: mips_load_reg2 = (rtx)0;
4666: current_frame_info = zero_frame_info;
4667:
4668: /* Restore the output file if optimizing the GP (optimizing the GP causes
4669: the text to be diverted to a tempfile, so that data decls come before
4670: references to the data). */
4671:
4672: if (TARGET_GP_OPT)
4673: asm_out_file = asm_out_data_file;
4674: }
4675:
4676:
4677: /* Expand the epilogue into a bunch of separate insns. */
4678:
4679: void
4680: mips_expand_epilogue ()
4681: {
4682: long tsize = current_frame_info.total_size;
4683: rtx tsize_rtx = GEN_INT (tsize);
4684: rtx tmp_rtx = (rtx)0;
4685:
4686: if (tsize > 32767)
4687: {
4688: tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
4689: emit_move_insn (tmp_rtx, tsize_rtx);
4690: tsize_rtx = tmp_rtx;
4691: }
4692:
4693: if (tsize > 0)
4694: {
4695: if (frame_pointer_needed)
4696: emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
4697:
4698: save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0);
4699:
4700: emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
4701: }
4702:
4703: emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31)));
4704: }
4705:
4706:
4707: /* Define the number of delay slots needed for the function epilogue.
4708:
4709: On the mips, we need a slot if either no stack has been allocated,
4710: or the only register saved is the return register. */
4711:
4712: int
4713: mips_epilogue_delay_slots ()
4714: {
4715: if (!current_frame_info.initialized)
4716: (void) compute_frame_size (get_frame_size ());
4717:
4718: if (current_frame_info.total_size == 0)
4719: return 1;
4720:
4721: if (current_frame_info.mask == RA_MASK && current_frame_info.fmask == 0)
4722: return 1;
4723:
4724: return 0;
4725: }
4726:
4727:
4728: /* Return true if this function is known to have a null epilogue.
4729: This allows the optimizer to omit jumps to jumps if no stack
4730: was created. */
4731:
4732: int
4733: simple_epilogue_p ()
4734: {
4735: if (!reload_completed)
4736: return 0;
4737:
4738: if (current_frame_info.initialized)
4739: return current_frame_info.total_size == 0;
4740:
4741: return (compute_frame_size (get_frame_size ())) == 0;
4742: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.