]> gcc.gnu.org Git - gcc.git/blame - gcc/config/arm/arm.c
(WORD_SWITCH_TAKES_ARG): Delete sparc related cruft.
[gcc.git] / gcc / config / arm / arm.c
CommitLineData
cce8749e
CH
1/* Output routines for GCC for ARM/RISCiX.
2 Copyright (C) 1991 Free Software Foundation, Inc.
3 Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
4 and Martin Simmons (@harleqn.co.uk).
5
6This file is part of GNU CC.
7
8GNU CC is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13GNU CC is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU CC; see the file COPYING. If not, write to
20the 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. */
cce8749e
CH
41extern FILE *asm_out_file;
42extern char *output_multi_immediate ();
43extern char *arm_output_asm_insn ();
44extern 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. */
49int output_memory_reference_mode;
50
51/* Nonzero if the prologue must setup `fp'. */
52int current_function_anonymous_args;
53
54/* Location counter of .text segment. */
55int 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. */
59struct label_offset
60{
61 char *name;
62 int offset;
63 struct label_offset *cdr;
64};
65
66#define LABEL_HASH_SIZE 257
67
68static struct label_offset *offset_table[LABEL_HASH_SIZE];
69
70/* For an explanation of these variables, see final_prescan_insn below. */
71int arm_ccfsm_state;
72int arm_current_cc;
73rtx arm_target_insn;
74int arm_target_label;
75char *arm_condition_codes[];
76\f
77/* Return the number of mov instructions needed to get the constant VALUE into
78 a register. */
79
80int
81arm_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
97int
98const_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
115int
116const_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\f
129/* Predicates for `match_operand' and `match_operator'. */
130
131/* Return TRUE for valid operands for the rhs of an ARM instruction. */
132
133int
134arm_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
144int
145fpu_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
158int
159power_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.
172 Either: REG, CONST_DOUBLE or MEM(offsettable).
173 Note that this disallows MEM(REG+REG). */
174
175int
176di_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
198int
199index_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
210int
211shiftable_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
228int
229shift_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\f
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
251void
252print_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
274char *
275output_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
296char *
297output_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
316char *
317output_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
334 or MEM<-REG and all MEMs must be offsettable addresses. */
335
336char *
337output_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);
395 /* Take care of overlapping base/data reg. */
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
437char *
438output_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
475char *
476output_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
501char *
502output_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
540char *
541arithmetic_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
571char *
572shift_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
615int
616int_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
639char *
640output_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
663char *
664output_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
687char *
688output_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
707char *
708output_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
726void
727output_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\f
771void
772output_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
881void
882output_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 {
921 if (current_function_pretend_args_size == 0 && regs_ever_live[14])
922 {
923 print_multi_reg (f, "ldmfd\tsp!",
924 (live_regs_mask & ~0x4000) | 0x8000, TRUE);
925 code_size += 4;
926 }
927 else
928 {
929 if (live_regs_mask)
930 {
931 print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE);
932 code_size += 4;
933 }
934 if (current_function_pretend_args_size)
935 {
936 operands[0] = operands[1] = stack_pointer_rtx;
937 operands[2] = gen_rtx (CONST_INT, VOIDmode,
938 current_function_pretend_args_size);
939 output_add_immediate (operands);
940 }
941 fputs ("\tmovs\tpc, lr\n", f);
942 code_size += 4;
943 }
944 }
945 arm_increase_location (code_size);
946 current_function_anonymous_args = 0;
947} /* output_epilogue */
948\f
949/* Increase the `arm_text_location' by AMOUNT if we're in the text
950 segment. */
951
952void
953arm_increase_location (amount)
954 int amount;
955{
956 if (in_text_section ())
957 arm_text_location += amount;
958} /* arm_increase_location */
959
960
961/* Like output_asm_insn (), but also increases the arm_text_location (if in
962 the .text segment, of course, even though this will always be true).
963 Returns the empty string. */
964
965char *
966arm_output_asm_insn (template, operands)
967 char *template;
968 rtx *operands;
969{
970 extern FILE *asm_out_file;
971
972 output_asm_insn (template, operands);
973 if (in_text_section ())
974 arm_text_location += 4;
975 fflush (asm_out_file);
976 return ("");
977} /* arm_output_asm_insn */
978
979
980/* Output a label definition. If this label is within the .text segment, it
981 is stored in OFFSET_TABLE, to be used when building `llc' instructions.
982 Maybe GCC remembers names not starting with a `*' for a long time, but this
983 is a minority anyway, so we just make a copy. Do not store the leading `*'
984 if the name starts with one. */
985
986void
987arm_asm_output_label (stream, name)
988 FILE *stream;
989 char *name;
990{
991 char *real_name, *s;
992 struct label_offset *cur;
993 int hash = 0;
994
995 assemble_name (stream, name);
996 fputs (":\n", stream);
997 if (! in_text_section ())
998 return;
999
1000 if (name[0] == '*')
1001 {
1002 real_name = xmalloc (1 + strlen (&name[1]));
1003 strcpy (real_name, &name[1]);
1004 }
1005 else
1006 {
1007 real_name = xmalloc (2 + strlen (name));
1008 strcpy (real_name, "_");
1009 strcat (real_name, name);
1010 }
1011 for (s = real_name; *s; s++)
1012 hash += *s;
1013 hash = hash % LABEL_HASH_SIZE;
838928c2 1014 cur = (struct label_offset *) xmalloc (sizeof (struct label_offset));
cce8749e
CH
1015 cur->name = real_name;
1016 cur->offset = arm_text_location;
1017 cur->cdr = offset_table[hash];
1018 offset_table[hash] = cur;
1019} /* arm_asm_output_label */
1020
1021
1022/* Output the instructions needed to perform what Martin's /bin/as called
1023 llc: load an SImode thing from the function's constant pool.
1024
1025 XXX This could be enhanced in that we do not really need a pointer in the
1026 constant pool pointing to the real thing. If we can address this pointer,
1027 we can also address what it is pointing at, in fact, anything in the text
1028 segment which has been defined already within this .s file. */
1029
1030char *
1031arm_output_llc (operands)
1032 rtx *operands;
1033{
1034 char *s, *name = XSTR (XEXP (operands[1], 0), 0);
1035 struct label_offset *he;
1036 int hash = 0, conditional = (arm_ccfsm_state == 3 || arm_ccfsm_state == 4);
1037
1038 if (*name != '*')
1039 abort ();
1040
1041 for (s = &name[1]; *s; s++)
1042 hash += *s;
1043 hash = hash % LABEL_HASH_SIZE;
1044 he = offset_table[hash];
1045 while (he && strcmp (he->name, &name[1]))
1046 he = he->cdr;
1047
1048 if (!he)
1049 abort ();
1050
1051 if (arm_text_location + 8 - he->offset < 4095)
1052 {
1053 fprintf (asm_out_file, "\tldr%s\t%s, [pc, #%s - . - 8]\n",
1054 conditional ? arm_condition_codes[arm_current_cc] : "",
1055 reg_names[REGNO (operands[0])], &name[1]);
1056 arm_increase_location (4);
1057 return ("");
1058 }
1059 else
1060 {
1061 int offset = - (arm_text_location + 8 - he->offset);
1062 char *reg_name = reg_names[REGNO (operands[0])];
1063
1064 /* ??? This is a hack, assuming the constant pool never is more than
1065 (1 + 255) * 4096 == 1Meg away from the PC. */
1066
1067 if (offset > 1000000)
1068 abort ();
1069
1070 fprintf (asm_out_file, "\tsub%s\t%s, pc, #(8 + . - %s) & ~4095\n",
1071 conditional ? arm_condition_codes[arm_current_cc] : "",
1072 reg_name, &name[1]);
1073 fprintf (asm_out_file, "\tldr%s\t%s, [%s, #- ((4 + . - %s) & 4095)]\n",
1074 conditional ? arm_condition_codes[arm_current_cc] : "",
1075 reg_name, reg_name, &name[1]);
1076 arm_increase_location (8);
1077 }
1078 return ("");
1079} /* arm_output_llc */
1080
1081
1082/* Output code resembling an .lcomm directive. /bin/as doesn't have this
1083 directive hence this hack, which works by reserving some `.space' in the
1084 bss segment directly.
1085
1086 XXX This is a severe hack, which is guaranteed NOT to work since it doesn't
1087 define STATIC COMMON space but merely STATIC BSS space. */
1088
1089void
1090output_lcomm_directive (stream, name, size, rounded)
1091 FILE *stream;
1092 char *name;
1093 int size, rounded;
1094{
1095 fputs ("\n\t.bss\t@ .lcomm\n", stream);
1096 assemble_name (stream, name);
1097 fprintf (stream, ":\t.space\t%d\n", rounded);
1098 if (in_text_section ())
1099 fputs ("\n\t.text\n", stream);
1100 else
1101 fputs ("\n\t.data\n", stream);
1102} /* output_lcomm_directive */
1103\f
1104/* A finite state machine takes care of noticing whether or not instructions
1105 can be conditionally executed, and thus decrease execution time and code
1106 size by deleting branch instructions. The fsm is controlled by
1107 final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE. */
1108
1109/* The state of the fsm controlling condition codes are:
1110 0: normal, do nothing special
1111 1: make ASM_OUTPUT_OPCODE not output this instruction
1112 2: make ASM_OUTPUT_OPCODE not output this instruction
1113 3: make instructions conditional
1114 4: make instructions conditional
1115
1116 State transitions (state->state by whom under condition):
1117 0 -> 1 final_prescan_insn if the `target' is a label
1118 0 -> 2 final_prescan_insn if the `target' is an unconditional branch
1119 1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch
1120 2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch
1121 3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached
1122 (the target label has CODE_LABEL_NUMBER equal to arm_target_label).
1123 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
1124 (the target insn is arm_target_insn).
1125
1126 XXX In case the `target' is an unconditional branch, this conditionalising
1127 of the instructions always reduces code size, but not always execution
1128 time. But then, I want to reduce the code size to somewhere near what
1129 /bin/cc produces. */
1130
1131/* The condition codes of the ARM, and the inverse function. */
1132char *arm_condition_codes[] =
1133{
1134 "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
1135 "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
1136};
1137
1138#define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1)
1139
1140/* Returns the index of the ARM condition code string in
1141 `arm_condition_codes'. COMPARISON should be an rtx like
1142 `(eq (...) (...))'. */
1143
1144int
1145get_arm_condition_code (comparison)
1146 rtx comparison;
1147{
1148 switch (GET_CODE (comparison))
1149 {
1150 case NE: return (1);
1151 case EQ: return (0);
1152 case GE: return (10);
1153 case GT: return (12);
1154 case LE: return (13);
1155 case LT: return (11);
1156 case GEU: return (2);
1157 case GTU: return (8);
1158 case LEU: return (9);
1159 case LTU: return (3);
1160 default: abort ();
1161 }
1162 /*NOTREACHED*/
1163 return (42);
1164} /* get_arm_condition_code */
1165
1166
1167void
1168final_prescan_insn (insn, opvec, noperands)
1169 rtx insn;
1170 rtx *opvec;
1171 int noperands;
1172{
1173 /* BODY will hold the body of INSN. */
1174 register rtx body = PATTERN (insn);
1175
1176 /* This will be 1 if trying to repeat the trick, and things need to be
1177 reversed if it appears to fail. */
1178 int reverse = 0;
1179
1180 /* START_INSN will hold the insn from where we start looking. This is the
1181 first insn after the following code_label if REVERSE is true. */
1182 rtx start_insn = insn;
1183
1184 /* If in state 4, check if the target branch is reached, in order to
1185 change back to state 0. */
1186 if (arm_ccfsm_state == 4)
1187 {
1188 if (insn == arm_target_insn)
1189 arm_ccfsm_state = 0;
1190 return;
1191 }
1192
1193 /* If in state 3, it is possible to repeat the trick, if this insn is an
1194 unconditional branch to a label, and immediately following this branch
1195 is the previous target label which is only used once, and the label this
1196 branch jumps to is not too far off. */
1197 if (arm_ccfsm_state == 3)
1198 {
1199 if (simplejump_p (insn))
1200 {
1201 start_insn = next_nonnote_insn (start_insn);
1202 if (GET_CODE (start_insn) == BARRIER)
1203 {
1204 /* XXX Isn't this always a barrier? */
1205 start_insn = next_nonnote_insn (start_insn);
1206 }
1207 if (GET_CODE (start_insn) == CODE_LABEL
1208 && CODE_LABEL_NUMBER (start_insn) == arm_target_label
1209 && LABEL_NUSES (start_insn) == 1)
1210 reverse = TRUE;
1211 else
1212 return;
1213 }
1214 else
1215 return;
1216 }
1217
1218 if (arm_ccfsm_state != 0 && !reverse)
1219 abort ();
1220 if (GET_CODE (insn) != JUMP_INSN)
1221 return;
1222
1223 if (reverse
1224 || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
1225 && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
1226 {
1227 int insns_skipped = 0, fail = FALSE, succeed = FALSE;
1228 /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */
1229 int then_not_else = TRUE;
1230 rtx this_insn = start_insn, label;
1231
1232 /* Register the insn jumped to. */
1233 if (reverse)
1234 label = XEXP (SET_SRC (body), 0);
1235 else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
1236 label = XEXP (XEXP (SET_SRC (body), 1), 0);
1237 else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
1238 {
1239 label = XEXP (XEXP (SET_SRC (body), 2), 0);
1240 then_not_else = FALSE;
1241 }
1242 else
1243 abort ();
1244
1245 /* See how many insns this branch skips, and what kind of insns. If all
1246 insns are okay, and the label or unconditional branch to the same
1247 label is not too far away, succeed. */
1248 for (insns_skipped = 0;
1249 !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
1250 insns_skipped++)
1251 {
1252 rtx scanbody;
1253
1254 this_insn = next_nonnote_insn (this_insn);
1255 if (!this_insn)
1256 break;
1257
1258 scanbody = PATTERN (this_insn);
1259
1260 switch (GET_CODE (this_insn))
1261 {
1262 case CODE_LABEL:
1263 /* Succeed if it is the target label, otherwise fail since
1264 control falls in from somewhere else. */
1265 if (this_insn == label)
1266 {
1267 arm_ccfsm_state = 1;
1268 succeed = TRUE;
1269 }
1270 else
1271 fail = TRUE;
1272 break;
1273
1274 case BARRIER: /* XXX Is this case necessary? */
1275 /* Succeed if the following insn is the target label.
1276 Otherwise fail. */
1277 this_insn = next_nonnote_insn (this_insn);
1278 if (this_insn == label)
1279 {
1280 arm_ccfsm_state = 1;
1281 succeed = TRUE;
1282 }
1283 else
1284 fail = TRUE;
1285 break;
1286
1287 case JUMP_INSN:
1288 /* If this is an unconditional branch to the same label, succeed.
1289 If it is to another label, do nothing. If it is conditional,
1290 fail. */
1291 /* XXX Probably, the test for the SET and the PC are unnecessary. */
1292
1293 if (GET_CODE (scanbody) == SET && GET_CODE (SET_DEST (scanbody)) == PC)
1294 {
1295 if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
1296 && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
1297 {
1298 arm_ccfsm_state = 2;
1299 succeed = TRUE;
1300 }
1301 else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
1302 fail = TRUE;
1303 }
1304 break;
1305
1306 case INSN:
1307 /* Instructions affecting the condition codes make it fail. */
1308 if (sets_cc0_p (scanbody))
1309 fail = TRUE;
1310 break;
1311
1312 default:
1313 break;
1314 }
1315 }
1316 if (succeed)
1317 {
1318 if (arm_ccfsm_state == 1 || reverse)
1319 arm_target_label = CODE_LABEL_NUMBER (label);
1320 else if (arm_ccfsm_state == 2)
1321 arm_target_insn = this_insn;
1322 else
1323 abort ();
1324
1325 /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from what
1326 it was. */
1327 if (!reverse)
1328 arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 0));
1329 if (reverse || then_not_else)
1330 arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
1331 }
1332 }
1333} /* final_prescan_insn */
1334
1335/* EOF */
This page took 0.194981 seconds and 5 git commands to generate.