|
|
1.1 root 1: /* Output routines for GCC for ARM/RISCiX.
2: Copyright (C) 1991 Free Software Foundation, Inc.
3: Contributed by Pieter `Tiggr' Schoenmakers ([email protected])
4: and Martin Simmons (@harleqn.co.uk).
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 <stdio.h>
23: #include <assert.h>
24: #include "config.h"
25: #include "rtl.h"
26: #include "regs.h"
27: #include "hard-reg-set.h"
28: #include "real.h"
29: #include "insn-config.h"
30: #include "conditions.h"
31: #include "insn-flags.h"
32: #include "output.h"
33: #include "insn-attr.h"
34: #include "flags.h"
35:
36: /* The maximum number of insns skipped which will be conditionalised if
37: possible. */
38: #define MAX_INSNS_SKIPPED 5
39:
40: /* Some function declarations. */
41: extern void *xmalloc ();
42: extern FILE *asm_out_file;
43: extern char *output_multi_immediate ();
44: extern char *arm_output_asm_insn ();
45: extern void arm_increase_location ();
46:
47: /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
48: must report the mode of the memory reference from PRINT_OPERAND to
49: PRINT_OPERAND_ADDRESS. */
50: int output_memory_reference_mode;
51:
52: /* Nonzero if the prologue must setup `fp'. */
53: int current_function_anonymous_args;
54:
55: /* Location counter of .text segment. */
56: int arm_text_location = 0;
57:
58: /* A hash table is used to store text segment labels and their associated
59: offset from the start of the text segment. */
60: struct label_offset
61: {
62: char *name;
63: int offset;
64: struct label_offset *cdr;
65: };
66:
67: #define LABEL_HASH_SIZE 257
68:
69: static struct label_offset *offset_table[LABEL_HASH_SIZE];
70:
71: /* For an explanation of these variables, see final_prescan_insn below. */
72: int arm_ccfsm_state;
73: int arm_current_cc;
74: rtx arm_target_insn;
75: int arm_target_label;
76: char *arm_condition_codes[];
77:
78: /* Return the number of mov instructions needed to get the constant VALUE into
79: a register. */
80:
81: int
82: arm_const_nmoves (value)
83: register int value;
84: {
85: register int i;
86:
87: if (value == 0)
88: return (1);
89: for (i = 0; value; i++, value &= ~0xff)
90: while ((value & 3) == 0)
91: value = (value >> 2) | ((value & 3) << 30);
92: return (i);
93: } /* arm_const_nmoves */
94:
95:
96: /* Return TRUE if int I is a valid immediate ARM constant. */
97:
98: int
99: const_ok_for_arm (i)
100: int i;
101: {
102: unsigned int mask = ~0xFF;
103:
104: do
105: {
106: if ((i & mask) == 0)
107: return(TRUE);
108: mask = (mask << 2) | (mask >> (32 - 2));
109: } while (mask != ~0xFF);
110:
111: return (FALSE);
112: } /* const_ok_for_arm */
113:
114: /* Return TRUE if rtx X is a valid immediate FPU constant. */
115:
116: int
117: const_double_rtx_ok_for_fpu (x)
118: rtx x;
119: {
120: double d;
121: union real_extract u;
122: u.i[0] = CONST_DOUBLE_LOW(x);
123: u.i[1] = CONST_DOUBLE_HIGH(x);
124: d = u.d;
125:
126: return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0
127: || d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0);
128: } /* const_double_rtx_ok_for_fpu */
129:
130: /* Predicates for `match_operand' and `match_operator'. */
131:
132: /* Return TRUE for valid operands for the rhs of an ARM instruction. */
133:
134: int
135: arm_rhs_operand (op, mode)
136: rtx op;
137: enum machine_mode mode;
138: {
139: return (register_operand (op, mode)
140: || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
141: } /* arm_rhs_operand */
142:
143: /* Return TRUE for valid operands for the rhs of an FPU instruction. */
144:
145: int
146: fpu_rhs_operand (op, mode)
147: rtx op;
148: enum machine_mode mode;
149: {
150: if (register_operand (op, mode))
151: return(TRUE);
152: else if (GET_CODE (op) == CONST_DOUBLE)
153: return (const_double_rtx_ok_for_fpu (op));
154: else return (FALSE);
155: } /* fpu_rhs_operand */
156:
157: /* Return nonzero if OP is a constant power of two. */
158:
159: int
160: power_of_two_operand (op, mode)
161: rtx op;
162: enum machine_mode mode;
163: {
164: if (GET_CODE (op) == CONST_INT)
165: {
166: int value = INTVAL(op);
167: return (value != 0 && (value & (value-1)) == 0);
168: }
169: return (FALSE);
170: } /* power_of_two_operand */
171:
172: /* Return TRUE for a valid operand of a DImode operation.
173: Either: REG, CONST_DOUBLE or MEM(offsetable).
174: Note that this disallows MEM(REG+REG). */
175:
176: int
177: di_operand (op, mode)
178: rtx op;
179: enum machine_mode mode;
180: {
181: if (register_operand (op, mode))
182: return (TRUE);
183:
184: switch (GET_CODE (op))
185: {
186: case CONST_DOUBLE:
187: case CONST_INT:
188: return (TRUE);
189: case MEM:
190: return (memory_address_p (DImode, XEXP (op, 0))
191: && offsettable_address_p (FALSE, DImode, XEXP (op, 0)));
192: default:
193: return (FALSE);
194: }
195: } /* di_operand */
196:
197: /* Return TRUE for valid index operands. */
198:
199: int
200: index_operand (op, mode)
201: rtx op;
202: enum machine_mode mode;
203: {
204: return (register_operand(op, mode)
205: || (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096));
206: } /* index_operand */
207:
208: /* Return TRUE for arithmetic operators which can be combined with a multiply
209: (shift). */
210:
211: int
212: shiftable_operator (x, mode)
213: rtx x;
214: enum machine_mode mode;
215: {
216: if (GET_MODE (x) != mode)
217: return FALSE;
218: else
219: {
220: enum rtx_code code = GET_CODE (x);
221:
222: return (code == PLUS || code == MINUS
223: || code == IOR || code == XOR || code == AND);
224: }
225: } /* shiftable_operator */
226:
227: /* Return TRUE for shift operators. */
228:
229: int
230: shift_operator (x, mode)
231: rtx x;
232: enum machine_mode mode;
233: {
234: if (GET_MODE (x) != mode)
235: return FALSE;
236: else
237: {
238: enum rtx_code code = GET_CODE (x);
239:
240: return (code == ASHIFT || code == LSHIFT
241: || code == ASHIFTRT || code == LSHIFTRT);
242: }
243: } /* shift_operator */
244:
245: /* Routines to output assembly language. */
246:
247: /* Output the operands of a LDM/STM instruction to STREAM.
248: MASK is the ARM register set mask of which only bits 0-15 are important.
249: INSTR is the possibly suffixed base register. HAT unequals zero if a hat
250: must follow the register list. */
251:
252: void
253: print_multi_reg (stream, instr, mask, hat)
254: FILE *stream;
255: char *instr;
256: int mask, hat;
257: {
258: int i;
259: int not_first = FALSE;
260:
261: fprintf (stream, "\t%s, {", instr);
262: for (i = 0; i < 16; i++)
263: if (mask & (1 << i))
264: {
265: if (not_first)
266: fprintf (stream, ", ");
267: fprintf (stream, "%s", reg_names[i]);
268: not_first = TRUE;
269: }
270: fprintf (stream, "}%s\n", hat ? "^" : "");
271: } /* print_multi_reg */
272:
273: /* Output a 'call' insn. */
274:
275: char *
276: output_call (operands)
277: rtx operands[];
278: {
279: operands[0] = XEXP (operands[0], 0);
280:
281: /* Handle calls to lr using ip (which may be clobbered in subr anyway). */
282:
283: if (REGNO (operands[0]) == 14)
284: {
285: operands[0] = gen_rtx (REG, SImode, 12);
286: arm_output_asm_insn ("mov\t%0, lr", operands);
287: }
288: arm_output_asm_insn ("mov\tlr, pc", operands);
289: arm_output_asm_insn ("mov\tpc, %0", operands);
290: return ("");
291: } /* output_call */
292:
293: /* Output a move from arm registers to an fpu registers.
294: OPERANDS[0] is an fpu register.
295: OPERANDS[1] is the first registers of an arm register pair. */
296:
297: char *
298: output_mov_double_fpu_from_arm (operands)
299: rtx operands[];
300: {
301: int arm_reg0 = REGNO (operands[1]);
302: rtx ops[2];
303:
304: if (arm_reg0 == 12)
305: abort();
306: ops[0] = gen_rtx (REG, SImode, arm_reg0);
307: ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
308: arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops);
309: arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands);
310: return ("");
311: } /* output_mov_double_fpu_from_arm */
312:
313: /* Output a move from an fpu register to arm registers.
314: OPERANDS[0] is the first registers of an arm register pair.
315: OPERANDS[1] is an fpu register. */
316:
317: char *
318: output_mov_double_arm_from_fpu (operands)
319: rtx operands[];
320: {
321: int arm_reg0 = REGNO (operands[0]);
322: rtx ops[2];
323:
324: if (arm_reg0 == 12)
325: abort();
326: ops[0] = gen_rtx (REG, SImode, arm_reg0);
327: ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
328: arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands);
329: arm_output_asm_insn ("ldmfd\tsp!, {%0, %1}", ops);
330: return("");
331: } /* output_mov_double_arm_from_fpu */
332:
333: /* Output a move between double words.
334: It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM
335: or MEM<-REG and all MEMs must be offsetable addresses. */
336:
337: char *
338: output_move_double (operands)
339: rtx operands[];
340: {
341: enum rtx_code code0 = GET_CODE (operands[0]);
342: enum rtx_code code1 = GET_CODE (operands[1]);
343: rtx otherops[2];
344:
345: if (code0 == REG)
346: {
347: int reg0 = REGNO (operands[0]);
348:
349: otherops[0] = gen_rtx (REG, SImode, 1 + reg0);
350: if (code1 == REG)
351: {
352: int reg1 = REGNO (operands[1]);
353: if (reg1 == 12)
354: abort();
355: otherops[1] = gen_rtx (REG, SImode, 1 + reg1);
356:
357: /* Ensure the second source is not overwritten */
358: if (reg0 == 1 + reg1)
359: {
360: arm_output_asm_insn("mov\t%0, %1", otherops);
361: arm_output_asm_insn("mov\t%0, %1", operands);
362: }
363: else
364: {
365: arm_output_asm_insn("mov\t%0, %1", operands);
366: arm_output_asm_insn("mov\t%0, %1", otherops);
367: }
368: }
369: else if (code1 == CONST_DOUBLE)
370: {
371: otherops[1] = gen_rtx (CONST_INT, VOIDmode,
372: CONST_DOUBLE_HIGH (operands[1]));
373: operands[1] = gen_rtx (CONST_INT, VOIDmode,
374: CONST_DOUBLE_LOW (operands[1]));
375: arm_output_asm_insn ("mov\t%0, %1", operands);
376: arm_output_asm_insn ("mov\t%0, %1", otherops);
377: }
378: else if (code1 == CONST_INT)
379: {
380: otherops[1] = const0_rtx;
381: arm_output_asm_insn ("mov\t%0, %1", operands);
382: arm_output_asm_insn ("mov\t%0, %1", otherops);
383: }
384: else if (code1 == MEM)
385: {
386: if (GET_CODE (XEXP (operands[1], 0)) == REG)
387: {
388: /* Handle the simple case where address is [r, #0] more
389: efficient. */
390: operands[1] = XEXP (operands[1], 0);
391: arm_output_asm_insn ("ldmia\t%1, %M0", operands);
392: }
393: else
394: {
395: otherops[1] = adj_offsettable_operand (operands[1], 4);
396: /* Take care of overlaping base/data reg. */
397: if (reg_mentioned_p (operands[0], operands[1]))
398: {
399: arm_output_asm_insn ("ldr\t%0, %1", otherops);
400: arm_output_asm_insn ("ldr\t%0, %1", operands);
401: }
402: else
403: {
404: arm_output_asm_insn ("ldr\t%0, %1", operands);
405: arm_output_asm_insn ("ldr\t%0, %1", otherops);
406: }
407: }
408: }
409: else abort(); /* Constraints should prevent this */
410: }
411: else if (code0 == MEM && code1 == REG)
412: {
413: if (REGNO (operands[1]) == 12)
414: abort();
415:
416: if (GET_CODE (XEXP (operands[0], 0)) == REG)
417: {
418: operands[0] = XEXP (operands[0], 0);
419: arm_output_asm_insn ("stmia\t%0, %M1", operands);
420: }
421: else
422: {
423: otherops[0] = adj_offsettable_operand (operands[0], 4);
424: otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));
425: arm_output_asm_insn ("str\t%1, %0", operands);
426: arm_output_asm_insn ("str\t%1, %0", otherops);
427: }
428: }
429: else abort(); /* Constraints should prevent this */
430:
431: return("");
432: } /* output_move_double */
433:
434:
435: /* Output an arbitrary MOV reg, #n.
436: OPERANDS[0] is a register. OPERANDS[1] is a const_int. */
437:
438: char *
439: output_mov_immediate (operands)
440: rtx operands[2];
441: {
442: int n = INTVAL (operands[1]);
443: int n_ones = 0;
444: int i;
445:
446: /* Try to use one MOV */
447:
448: if (const_ok_for_arm (n))
449: return (arm_output_asm_insn ("mov\t%0, %1", operands));
450:
451: /* Try to use one MVN */
452:
453: if (const_ok_for_arm(~n))
454: {
455: operands[1] = gen_rtx (CONST_INT, VOIDmode, ~n);
456: return (arm_output_asm_insn ("mvn\t%0, %1", operands));
457: }
458:
459: /* If all else fails, make it out of ORRs or BICs as appropriate. */
460:
461: for (i=0; i < 32; i++)
462: if (n & 1 << i)
463: n_ones++;
464:
465: if (n_ones > 16) /* Shorter to use MVN with BIC in this case. */
466: output_multi_immediate(operands, "mvn\t%0, %1", "bic\t%0, %0, %1", 1, ~n);
467: else
468: output_multi_immediate(operands, "mov\t%0, %1", "orr\t%0, %0, %1", 1, n);
469: return("");
470: } /* output_mov_immediate */
471:
472:
473: /* Output an ADD r, s, #n where n may be too big for one instruction. If
474: adding zero to one register, output nothing. */
475:
476: char *
477: output_add_immediate (operands)
478: rtx operands[3];
479: {
480: int n = INTVAL (operands[2]);
481:
482: if (n != 0 || REGNO (operands[0]) != REGNO (operands[1]))
483: {
484: if (n < 0)
485: output_multi_immediate (operands,
486: "sub\t%0, %1, %2", "sub\t%0, %0, %2", 2, -n);
487: else
488: output_multi_immediate (operands,
489: "add\t%0, %1, %2", "add\t%0, %0, %2", 2, n);
490: }
491: return("");
492: } /* output_add_immediate */
493:
494:
495: /* Output a multiple immediate operation.
496: OPERANDS is the vector of operands referred to in the output patterns.
497: INSTR1 is the output pattern to use for the first constant.
498: INSTR2 is the output pattern to use for subsequent constants.
499: IMMED_OP is the index of the constant slot in OPERANDS.
500: N is the constant value. */
501:
502: char *
503: output_multi_immediate (operands, instr1, instr2, immed_op, n)
504: rtx operands[];
505: char *instr1, *instr2;
506: int immed_op, n;
507: {
508: if (n == 0)
509: {
510: operands[immed_op] = const0_rtx;
511: arm_output_asm_insn (instr1, operands); /* Quick and easy output */
512: }
513: else
514: {
515: int i;
516: char *instr = instr1;
517:
518: /* Note that n is never zero here (which would give no output) */
519:
520: for (i = 0; i < 32; i += 2)
521: {
522: if (n & (3 << i))
523: {
524: operands[immed_op] = gen_rtx (CONST_INT, VOIDmode,
525: n & (255 << i));
526: arm_output_asm_insn (instr, operands);
527: instr = instr2;
528: i += 6;
529: }
530: }
531: }
532: return ("");
533: } /* output_multi_immediate */
534:
535:
536: /* Return the appropriate ARM instruction for the operation code.
537: The returned result should not be overwritten. OP is the rtx of the
538: operation. SHIFT_FIRST_ARG is TRUE if the first argument of the operator
539: was shifted. */
540:
541: char *
542: arithmetic_instr (op, shift_first_arg)
543: rtx op;
544: {
545: switch (GET_CODE(op))
546: {
547: case PLUS:
548: return ("add");
549: case MINUS:
550: if (shift_first_arg)
551: return ("rsb");
552: else
553: return ("sub");
554: case IOR:
555: return ("orr");
556: case XOR:
557: return ("eor");
558: case AND:
559: return ("and");
560: default:
561: abort();
562: }
563: return (""); /* stupid cc */
564: } /* arithmetic_instr */
565:
566:
567: /* Ensure valid constant shifts and return the appropriate shift mnemonic
568: for the operation code. The returned result should not be overwritten.
569: OP is the rtx code of the shift.
570: SHIFT_PTR points to the shift size operand. */
571:
572: char *
573: shift_instr (op, shift_ptr)
574: enum rtx_code op;
575: rtx *shift_ptr;
576: {
577: int min_shift = 0;
578: int max_shift = 31;
579: char *mnem;
580:
581: switch (op)
582: {
583: case ASHIFT:
584: mnem = "asl";
585: break;
586: case LSHIFT:
587: mnem = "lsl";
588: break;
589: case ASHIFTRT:
590: mnem = "asr";
591: max_shift = 32;
592: break;
593: case LSHIFTRT:
594: mnem = "lsr";
595: max_shift = 32;
596: break;
597: default:
598: abort();
599: }
600:
601: if (GET_CODE (*shift_ptr) == CONST_INT)
602: {
603: int shift = INTVAL (*shift_ptr);
604:
605: if (shift < min_shift)
606: *shift_ptr = gen_rtx (CONST_INT, VOIDmode, 0);
607: else if (shift > max_shift)
608: *shift_ptr = gen_rtx (CONST_INT, VOIDmode, max_shift);
609: }
610: return (mnem);
611: } /* shift_instr */
612:
613:
614: /* Obtain the shift from the POWER of two. */
615:
616: int
617: int_log2 (power)
618: unsigned int power;
619: {
620: int shift = 0;
621:
622: while (((1 << shift) & power) == 0)
623: {
624: if (shift > 31)
625: abort();
626: shift++;
627: }
628: return (shift);
629: } /* int_log2 */
630:
631:
632: /* Output an arithmetic instruction which may set the condition code.
633: OPERANDS[0] is the destination register.
634: OPERANDS[1] is the arithmetic operator expression.
635: OPERANDS[2] is the left hand argument.
636: OPERANDS[3] is the right hand argument.
637: CONST_FIRST_ARG is TRUE if the first argument of the operator was constant.
638: SET_COND is TRUE when the condition code should be set. */
639:
640: char *
641: output_arithmetic (operands, const_first_arg, set_cond)
642: rtx operands[4];
643: int const_first_arg;
644: int set_cond;
645: {
646: char mnemonic[80];
647: char *instr = arithmetic_instr (operands[1], const_first_arg);
648:
649: sprintf (mnemonic, "%s%s\t%%0, %%2, %%3", instr, set_cond ? "s" : "");
650: return (arm_output_asm_insn (mnemonic, operands));
651: } /* output_arithmetic */
652:
653:
654: /* Output an arithmetic instruction with a shift.
655: OPERANDS[0] is the destination register.
656: OPERANDS[1] is the arithmetic operator expression.
657: OPERANDS[2] is the unshifted register.
658: OPERANDS[3] is the shift operator expression.
659: OPERANDS[4] is the shifted register.
660: OPERANDS[5] is the shift constant or register.
661: SHIFT_FIRST_ARG is TRUE if the first argument of the operator was shifted.
662: SET_COND is TRUE when the condition code should be set. */
663:
664: char *
665: output_arithmetic_with_shift (operands, shift_first_arg, set_cond)
666: rtx operands[6];
667: int shift_first_arg;
668: int set_cond;
669: {
670: char mnemonic[80];
671: char *instr = arithmetic_instr (operands[1], shift_first_arg);
672: char *condbit = set_cond ? "s" : "";
673: char *shift = shift_instr (GET_CODE (operands[3]), &operands[5]);
674:
675: sprintf (mnemonic, "%s%s\t%%0, %%2, %%4, %s %%5", instr, condbit, shift);
676: return (arm_output_asm_insn (mnemonic, operands));
677: } /* output_arithmetic_with_shift */
678:
679:
680: /* Output an arithmetic instruction with a power of two multiplication.
681: OPERANDS[0] is the destination register.
682: OPERANDS[1] is the arithmetic operator expression.
683: OPERANDS[2] is the unmultiplied register.
684: OPERANDS[3] is the multiplied register.
685: OPERANDS[4] is the constant multiple (power of two).
686: SHIFT_FIRST_ARG is TRUE if the first arg of the operator was multiplied. */
687:
688: char *
689: output_arithmetic_with_immediate_multiply (operands, shift_first_arg)
690: rtx operands[5];
691: int shift_first_arg;
692: {
693: char mnemonic[80];
694: char *instr = arithmetic_instr (operands[1], shift_first_arg);
695: int shift = int_log2 (INTVAL (operands[4]));
696:
697: sprintf (mnemonic, "%s\t%%0, %%2, %%3, asl#%d", instr, shift);
698: return (arm_output_asm_insn (mnemonic, operands));
699: } /* output_arithmetic_with_immediate_multiply */
700:
701:
702: /* Output a move with a shift.
703: OP is the shift rtx code.
704: OPERANDS[0] = destination register.
705: OPERANDS[1] = source register.
706: OPERANDS[2] = shift constant or register. */
707:
708: char *
709: output_shifted_move (op, operands)
710: enum rtx_code op;
711: rtx operands[2];
712: {
713: char mnemonic[80];
714:
715: if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0)
716: sprintf (mnemonic, "mov\t%%0, %%1");
717: else
718: sprintf (mnemonic, "mov\t%%0, %%1, %s %%2",
719: shift_instr (op, &operands[2]));
720: return (arm_output_asm_insn (mnemonic, operands));
721: } /* output_shifted_move */
722:
723:
724: /* Output a .ascii pseudo-op, keeping track of lengths. This is because
725: /bin/as is horribly restrictive. */
726:
727: void
728: output_ascii_pseudo_op (stream, p, len)
729: FILE *stream;
730: char *p;
731: int len;
732: {
733: int i;
734: int len_so_far = 1000;
735: int chars_so_far = 0;
736:
737: for (i = 0; i < len; i++)
738: {
739: register int c = p[i];
740:
741: if (len_so_far > 50)
742: {
743: if (chars_so_far)
744: fputs ("\"\n", stream);
745: fputs ("\t.ascii\t\"", stream);
746: len_so_far = 0;
747: arm_increase_location (chars_so_far);
748: chars_so_far = 0;
749: }
750:
751: if (c == '\"' || c == '\\')
752: {
753: putc('\\', stream);
754: len_so_far++;
755: }
756: if (c >= ' ' && c < 0177)
757: {
758: putc (c, stream);
759: len_so_far++;
760: }
761: else
762: {
763: fprintf (stream, "\\%03o", c);
764: len_so_far +=4;
765: }
766: chars_so_far++;
767: }
768: fputs ("\"\n", stream);
769: arm_increase_location (chars_so_far);
770: } /* output_ascii_pseudo_op */
771:
772: void
773: output_prologue (f, frame_size)
774: FILE *f;
775: int frame_size;
776: {
777:
778: int reg, live_regs_mask = 0, code_size = 0;
779: rtx operands[3];
780:
781: /* Nonzero if the `fp' (argument pointer) register is needed. */
782: int fp_needed = 0;
783:
784: /* Nonzero if we must stuff some register arguments onto the stack as if
785: they were passed there. */
786: int store_arg_regs = 0;
787:
788: fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n",
789: current_function_args_size, current_function_pretend_args_size, frame_size);
790: fprintf (f, "\t@ frame_pointer_needed = %d, current_function_anonymous_args = %d\n",
791: frame_pointer_needed, current_function_anonymous_args);
792:
793: if (current_function_pretend_args_size || current_function_args_size
794: || frame_pointer_needed || current_function_anonymous_args || TARGET_APCS)
795: fp_needed = 1;
796:
797: if (current_function_anonymous_args && current_function_pretend_args_size)
798: store_arg_regs = 1;
799:
800: for (reg = 4; reg < 10; reg++)
801: if (regs_ever_live[reg])
802: live_regs_mask |= (1 << reg);
803:
804: if (fp_needed)
805: {
806: live_regs_mask |= 0xD800;
807: /* The following statement is probably redundant now
808: because the frame pointer is recorded in regs_ever_live. */
809: if (frame_pointer_needed)
810: live_regs_mask |= (1 << FRAME_POINTER_REGNUM);
811: fputs ("\tmov\tip, sp\n", f);
812: code_size += 4;
813: }
814: else if (regs_ever_live[14])
815: live_regs_mask |= 0x4000;
816:
817: /* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make
818: room. If also STORE_ARG_REGS store the argument registers involved in
819: the created slot (this is for stdarg and varargs). */
820: if (current_function_pretend_args_size)
821: {
822: if (store_arg_regs)
823: {
824: int arg_size, mask = 0;
825:
826: assert (current_function_pretend_args_size <= 16);
827: for (reg = 3, arg_size = current_function_pretend_args_size;
828: arg_size > 0; reg--, arg_size -= 4)
829: mask |= (1 << reg);
830: print_multi_reg (f, "stmfd\tsp!", mask, FALSE);
831: }
832: else
833: {
834: operands[0] = operands[1] = stack_pointer_rtx;
835: operands[2] = gen_rtx (CONST_INT, VOIDmode,
836: -current_function_pretend_args_size);
837: output_add_immediate (operands);
838: }
839: }
840:
841: if (live_regs_mask)
842: {
843: print_multi_reg (f, "stmfd\tsp!", live_regs_mask, FALSE);
844: code_size += 4;
845: }
846:
847: for (reg = 23; reg > 19; reg--)
848: if (regs_ever_live[reg])
849: {
850: fprintf (f, "\tstfe\t%s, [sp, #-12]!\n", reg_names[reg]);
851: code_size += 4;
852: }
853:
854: if (fp_needed)
855: {
856: /* Make `fp' point to saved value of `pc'. */
857:
858: operands[0] = arg_pointer_rtx;
859: operands[1] = gen_rtx (REG, SImode, 12);
860: operands[2] = gen_rtx (CONST_INT, VOIDmode,
861: - (4 + current_function_pretend_args_size));
862: output_add_immediate (operands);
863: }
864:
865: if (frame_pointer_needed)
866: {
867: fprintf (f, "\tmov\trfp, sp\n");
868: code_size += 4;
869: }
870:
871: if (frame_size)
872: {
873: operands[0] = operands[1] = stack_pointer_rtx;
874: operands[2] = gen_rtx (CONST_INT, VOIDmode, -frame_size);
875: output_add_immediate (operands);
876: }
877:
878: arm_increase_location (code_size);
879: } /* output_prologue */
880:
881:
882: void
883: output_epilogue (f, frame_size)
884: FILE *f;
885: int frame_size;
886: {
887: int reg, live_regs_mask = 0, code_size = 0, fp_needed = 0;
888: rtx operands[3];
889:
890: if (current_function_pretend_args_size || current_function_args_size
891: || frame_pointer_needed || current_function_anonymous_args || TARGET_APCS)
892: fp_needed = 1;
893:
894: for (reg = 4; reg < 10; reg++)
895: if (regs_ever_live[reg])
896: live_regs_mask |= (1 << reg);
897:
898: if (fp_needed)
899: {
900: live_regs_mask |= 0xA800;
901: if (frame_pointer_needed)
902: live_regs_mask |= (1 << FRAME_POINTER_REGNUM);
903: }
904: else if (regs_ever_live[14])
905: live_regs_mask |= 0x4000;
906:
907: for (reg = 20; reg < 24; reg++)
908: if (regs_ever_live[reg])
909: {
910: fprintf (f, "\tldfe\t%s, [%s], #12\n", reg_names[reg],
911: frame_pointer_needed ? "rfp" : "sp");
912: code_size += 4;
913: }
914:
915: if (fp_needed)
916: {
917: print_multi_reg (f, "ldmea\tfp", live_regs_mask, TRUE);
918: code_size += 4;
919: }
920: else
921: {
922: if (current_function_pretend_args_size == 0 && regs_ever_live[14])
923: {
924: print_multi_reg (f, "ldmfd\tsp!",
925: (live_regs_mask & ~0x4000) | 0x8000, TRUE);
926: code_size += 4;
927: }
928: else
929: {
930: if (live_regs_mask)
931: {
932: print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE);
933: code_size += 4;
934: }
935: if (current_function_pretend_args_size)
936: {
937: operands[0] = operands[1] = stack_pointer_rtx;
938: operands[2] = gen_rtx (CONST_INT, VOIDmode,
939: current_function_pretend_args_size);
940: output_add_immediate (operands);
941: }
942: fputs ("\tmovs\tpc, lr\n", f);
943: code_size += 4;
944: }
945: }
946: arm_increase_location (code_size);
947: current_function_anonymous_args = 0;
948: } /* output_epilogue */
949:
950: /* Increase the `arm_text_location' by AMOUNT if we're in the text
951: segment. */
952:
953: void
954: arm_increase_location (amount)
955: int amount;
956: {
957: if (in_text_section ())
958: arm_text_location += amount;
959: } /* arm_increase_location */
960:
961:
962: /* Like output_asm_insn (), but also increases the arm_text_location (if in
963: the .text segment, of course, even though this will always be true).
964: Returns the empty string. */
965:
966: char *
967: arm_output_asm_insn (template, operands)
968: char *template;
969: rtx *operands;
970: {
971: extern FILE *asm_out_file;
972:
973: output_asm_insn (template, operands);
974: if (in_text_section ())
975: arm_text_location += 4;
976: fflush (asm_out_file);
977: return ("");
978: } /* arm_output_asm_insn */
979:
980:
981: /* Output a label definition. If this label is within the .text segment, it
982: is stored in OFFSET_TABLE, to be used when building `llc' instructions.
983: Maybe GCC remembers names not starting with a `*' for a long time, but this
984: is a minority anyway, so we just make a copy. Do not store the leading `*'
985: if the name starts with one. */
986:
987: void
988: arm_asm_output_label (stream, name)
989: FILE *stream;
990: char *name;
991: {
992: char *real_name, *s;
993: struct label_offset *cur;
994: int hash = 0;
995:
996: assemble_name (stream, name);
997: fputs (":\n", stream);
998: if (! in_text_section ())
999: return;
1000:
1001: if (name[0] == '*')
1002: {
1003: real_name = xmalloc (1 + strlen (&name[1]));
1004: strcpy (real_name, &name[1]);
1005: }
1006: else
1007: {
1008: real_name = xmalloc (2 + strlen (name));
1009: strcpy (real_name, "_");
1010: strcat (real_name, name);
1011: }
1012: for (s = real_name; *s; s++)
1013: hash += *s;
1014: hash = hash % LABEL_HASH_SIZE;
1015: cur = xmalloc (sizeof (struct label_offset));
1016: cur->name = real_name;
1017: cur->offset = arm_text_location;
1018: cur->cdr = offset_table[hash];
1019: offset_table[hash] = cur;
1020: } /* arm_asm_output_label */
1021:
1022:
1023: /* Output the instructions needed to perform what Martin's /bin/as called
1024: llc: load an SImode thing from the function's constant pool.
1025:
1026: XXX This could be enhanced in that we do not really need a pointer in the
1027: constant pool pointing to the real thing. If we can address this pointer,
1028: we can also address what it is pointing at, in fact, anything in the text
1029: segment which has been defined already within this .s file. */
1030:
1031: char *
1032: arm_output_llc (operands)
1033: rtx *operands;
1034: {
1035: char *s, *name = XSTR (XEXP (operands[1], 0), 0);
1036: struct label_offset *he;
1037: int hash = 0, conditional = (arm_ccfsm_state == 3 || arm_ccfsm_state == 4);
1038:
1039: if (*name != '*')
1040: abort ();
1041:
1042: for (s = &name[1]; *s; s++)
1043: hash += *s;
1044: hash = hash % LABEL_HASH_SIZE;
1045: he = offset_table[hash];
1046: while (he && strcmp (he->name, &name[1]))
1047: he = he->cdr;
1048:
1049: if (!he)
1050: abort ();
1051:
1052: if (arm_text_location + 8 - he->offset < 4095)
1053: {
1054: fprintf (asm_out_file, "\tldr%s\t%s, [pc, #%s - . - 8]\n",
1055: conditional ? arm_condition_codes[arm_current_cc] : "",
1056: reg_names[REGNO (operands[0])], &name[1]);
1057: arm_increase_location (4);
1058: return ("");
1059: }
1060: else
1061: {
1062: int offset = - (arm_text_location + 8 - he->offset);
1063: char *reg_name = reg_names[REGNO (operands[0])];
1064:
1065: /* ??? This is a hack, assuming the constant pool never is more than
1066: (1 + 255) * 4096 == 1Meg away from the PC. */
1067:
1068: if (offset > 1000000)
1069: abort ();
1070:
1071: fprintf (asm_out_file, "\tsub%s\t%s, pc, #(8 + . - %s) & ~4095\n",
1072: conditional ? arm_condition_codes[arm_current_cc] : "",
1073: reg_name, &name[1]);
1074: fprintf (asm_out_file, "\tldr%s\t%s, [%s, #- ((4 + . - %s) & 4095)]\n",
1075: conditional ? arm_condition_codes[arm_current_cc] : "",
1076: reg_name, reg_name, &name[1]);
1077: arm_increase_location (8);
1078: }
1079: return ("");
1080: } /* arm_output_llc */
1081:
1082:
1083: /* Output code resembling an .lcomm directive. /bin/as doesn't have this
1084: directive hence this hack, which works by reserving some `.space' in the
1085: bss segment directly.
1086:
1087: XXX This is a severe hack, which is garanteed NOT to work since it doesn't
1088: define STATIC COMMON space but merely STATIC BSS space. */
1089:
1090: void
1091: output_lcomm_directive (stream, name, size, rounded)
1092: FILE *stream;
1093: char *name;
1094: int size, rounded;
1095: {
1096: fputs ("\n\t.bss\t@ .lcomm\n", stream);
1097: assemble_name (stream, name);
1098: fprintf (stream, ":\t.space\t%d\n", rounded);
1099: if (in_text_section ())
1100: fputs ("\n\t.text\n", stream);
1101: else
1102: fputs ("\n\t.data\n", stream);
1103: } /* output_lcomm_directive */
1104:
1105: /* A finite state machine takes care of noticing whether or not instructions
1106: can be conditionaly executed, and thus decrease execution time and code
1107: size by deleting branch instructions. The fsm is controlled by
1108: final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE. */
1109:
1110: /* The state of the fsm controlling condition codes are:
1111: 0: normal, do nothing special
1112: 1: make ASM_OUTPUT_OPCODE not output this instruction
1113: 2: make ASM_OUTPUT_OPCODE not output this instruction
1114: 3: make instructions conditional
1115: 4: make instructions conditional
1116:
1117: State transitions (state->state by whom under condition):
1118: 0 -> 1 final_prescan_insn if the `target' is a label
1119: 0 -> 2 final_prescan_insn if the `target' is an unconditional branch
1120: 1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch
1121: 2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch
1122: 3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached
1123: (the target label has CODE_LABEL_NUMBER equal to arm_target_label).
1124: 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
1125: (the target insn is arm_target_insn).
1126:
1127: XXX In case the `target' is an unconditional branch, this conditionalising
1128: of the instructions always reduces code size, but not always execution
1129: time. But then, I want to reduce the code size to somewhere near what
1130: /bin/cc produces. */
1131:
1132: /* The condition codes of the ARM, and the inverse function. */
1133: char *arm_condition_codes[] =
1134: {
1135: "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
1136: "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
1137: };
1138:
1139: #define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1)
1140:
1141: /* Returns the index of the ARM condition code string in
1142: `arm_condition_codes'. COMPARISON should be an rtx like
1143: `(eq (...) (...))'. */
1144:
1145: int
1146: get_arm_condition_code (comparison)
1147: rtx comparison;
1148: {
1149: switch (GET_CODE (comparison))
1150: {
1151: case NE: return (1);
1152: case EQ: return (0);
1153: case GE: return (10);
1154: case GT: return (12);
1155: case LE: return (13);
1156: case LT: return (11);
1157: case GEU: return (2);
1158: case GTU: return (8);
1159: case LEU: return (9);
1160: case LTU: return (3);
1161: default: abort ();
1162: }
1163: /*NOTREACHED*/
1164: return (42);
1165: } /* get_arm_condition_code */
1166:
1167:
1168: void
1169: final_prescan_insn (insn, opvec, noperands)
1170: rtx insn;
1171: rtx *opvec;
1172: int noperands;
1173: {
1174: /* BODY will hold the body of INSN. */
1175: register rtx body = PATTERN (insn);
1176:
1177: /* This will be 1 if trying to repeat the trick, and things need to be
1178: reversed if it appears to fail. */
1179: int reverse = 0;
1180:
1181: /* START_INSN will hold the insn from where we start looking. This is the
1182: first insn after the following code_label if REVERSE is true. */
1183: rtx start_insn = insn;
1184:
1185: /* If in state 4, check if the target branch is reached, in order to
1186: change back to state 0. */
1187: if (arm_ccfsm_state == 4)
1188: {
1189: if (insn == arm_target_insn)
1190: arm_ccfsm_state = 0;
1191: return;
1192: }
1193:
1194: /* If in state 3, it is possible to repeat the trick, if this insn is an
1195: unconditional branch to a label, and immediately following this branch
1196: is the previous target label which is only used once, and the label this
1197: branch jumps to is not too far off. */
1198: if (arm_ccfsm_state == 3)
1199: {
1200: if (simplejump_p (insn))
1201: {
1202: start_insn = next_nonnote_insn (start_insn);
1203: if (GET_CODE (start_insn) == BARRIER)
1204: {
1205: /* XXX Isn't this always a barrier? */
1206: start_insn = next_nonnote_insn (start_insn);
1207: }
1208: if (GET_CODE (start_insn) == CODE_LABEL
1209: && CODE_LABEL_NUMBER (start_insn) == arm_target_label
1210: && LABEL_NUSES (start_insn) == 1)
1211: reverse = TRUE;
1212: else
1213: return;
1214: }
1215: else
1216: return;
1217: }
1218:
1219: if (arm_ccfsm_state != 0 && !reverse)
1220: abort ();
1221: if (GET_CODE (insn) != JUMP_INSN)
1222: return;
1223:
1224: if (reverse
1225: || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
1226: && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
1227: {
1228: int insns_skipped = 0, fail = FALSE, succeed = FALSE;
1229: /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */
1230: int then_not_else = TRUE;
1231: rtx this_insn = start_insn, label;
1232:
1233: /* Register the insn jumped to. */
1234: if (reverse)
1235: label = XEXP (SET_SRC (body), 0);
1236: else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
1237: label = XEXP (XEXP (SET_SRC (body), 1), 0);
1238: else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
1239: {
1240: label = XEXP (XEXP (SET_SRC (body), 2), 0);
1241: then_not_else = FALSE;
1242: }
1243: else
1244: abort ();
1245:
1246: /* See how many insns this branch skips, and what kind of insns. If all
1247: insns are okay, and the label or unconditional branch to the same
1248: label is not too far away, succeed. */
1249: for (insns_skipped = 0;
1250: !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
1251: insns_skipped++)
1252: {
1253: rtx scanbody;
1254:
1255: this_insn = next_nonnote_insn (this_insn);
1256: if (!this_insn)
1257: break;
1258:
1259: scanbody = PATTERN (this_insn);
1260:
1261: switch (GET_CODE (this_insn))
1262: {
1263: case CODE_LABEL:
1264: /* Succeed if it is the target label, otherwise fail since
1265: control falls in from somewhere else. */
1266: if (this_insn == label)
1267: {
1268: arm_ccfsm_state = 1;
1269: succeed = TRUE;
1270: }
1271: else
1272: fail = TRUE;
1273: break;
1274:
1275: case BARRIER: /* XXX Is this case necessary? */
1276: /* Succeed if the following insn is the target label.
1277: Otherwise fail. */
1278: this_insn = next_nonnote_insn (this_insn);
1279: if (this_insn == label)
1280: {
1281: arm_ccfsm_state = 1;
1282: succeed = TRUE;
1283: }
1284: else
1285: fail = TRUE;
1286: break;
1287:
1288: case JUMP_INSN:
1289: /* If this is an unconditional branch to the same label, succeed.
1290: If it is to another label, do nothing. If it is conditional,
1291: fail. */
1292: /* XXX Probably, the test for the SET and the PC are unnecessary. */
1293:
1294: if (GET_CODE (scanbody) == SET && GET_CODE (SET_DEST (scanbody)) == PC)
1295: {
1296: if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
1297: && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
1298: {
1299: arm_ccfsm_state = 2;
1300: succeed = TRUE;
1301: }
1302: else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
1303: fail = TRUE;
1304: }
1305: break;
1306:
1307: case INSN:
1308: /* Instructions affecting the condition codes make it fail. */
1309: if (sets_cc0_p (scanbody))
1310: fail = TRUE;
1311: break;
1312:
1313: default:
1314: break;
1315: }
1316: }
1317: if (succeed)
1318: {
1319: if (arm_ccfsm_state == 1 || reverse)
1320: arm_target_label = CODE_LABEL_NUMBER (label);
1321: else if (arm_ccfsm_state == 2)
1322: arm_target_insn = this_insn;
1323: else
1324: abort ();
1325:
1326: /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from what
1327: it was. */
1328: if (!reverse)
1329: arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 0));
1330: if (reverse || then_not_else)
1331: arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
1332: }
1333: }
1334: } /* final_prescan_insn */
1335:
1336: /* EOF */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.