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