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