]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/config/microblaze/microblaze.c
IPA C++ refactoring 4/N
[thirdparty/gcc.git] / gcc / config / microblaze / microblaze.c
CommitLineData
d34b0d1e 1/* Subroutines used for code generation on Xilinx MicroBlaze.
3aea1f79 2 Copyright (C) 2009-2014 Free Software Foundation, Inc.
d34b0d1e 3
4 Contributed by Michael Eager <eager@eagercon.com>.
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published
10 by the Free Software Foundation; either version 3, or (at your
11 option) any later version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3. If not see
20 <http://www.gnu.org/licenses/>. */
21
22#include "config.h"
23#include "system.h"
24#include "coretypes.h"
d34b0d1e 25#include "tm.h"
26#include "rtl.h"
27#include "regs.h"
28#include "hard-reg-set.h"
29#include "real.h"
30#include "insn-config.h"
31#include "conditions.h"
32#include "insn-flags.h"
33#include "insn-attr.h"
d34b0d1e 34#include "recog.h"
d34b0d1e 35#include "tree.h"
9ed99284 36#include "varasm.h"
37#include "stor-layout.h"
38#include "calls.h"
d34b0d1e 39#include "function.h"
40#include "expr.h"
41#include "flags.h"
42#include "reload.h"
43#include "output.h"
44#include "ggc.h"
45#include "hashtab.h"
46#include "target.h"
47#include "target-def.h"
48#include "tm_p.h"
49#include "gstab.h"
50#include "df.h"
51#include "optabs.h"
52#include "diagnostic-core.h"
367b1459 53#include "cgraph.h"
f7715905 54#include "builtins.h"
d34b0d1e 55
56#define MICROBLAZE_VERSION_COMPARE(VA,VB) strcasecmp (VA, VB)
57
58/* Classifies an address.
59
60ADDRESS_INVALID
61An invalid address.
62
63ADDRESS_REG
64
65A natural register or a register + const_int offset address.
66The register satisfies microblaze_valid_base_register_p and the
67offset is a const_arith_operand.
68
69ADDRESS_REG_INDEX
70
71A natural register offset by the index contained in an index register. The base
72register satisfies microblaze_valid_base_register_p and the index register
73satisfies microblaze_valid_index_register_p
74
75ADDRESS_CONST_INT
76
77A signed 16/32-bit constant address.
78
79ADDRESS_SYMBOLIC:
80
81A constant symbolic address or a (register + symbol). */
82
83enum microblaze_address_type
84{
85 ADDRESS_INVALID,
86 ADDRESS_REG,
87 ADDRESS_REG_INDEX,
88 ADDRESS_CONST_INT,
89 ADDRESS_SYMBOLIC,
90 ADDRESS_GOTOFF,
4eae25aa 91 ADDRESS_PLT,
92 ADDRESS_TLS
d34b0d1e 93};
94
95/* Classifies symbols
96
97SYMBOL_TYPE_GENERAL
98
99A general symbol. */
100enum microblaze_symbol_type
101{
102 SYMBOL_TYPE_INVALID,
103 SYMBOL_TYPE_GENERAL
104};
105
4eae25aa 106/* TLS Address Type. */
107enum tls_reloc {
108 TLS_GD,
109 TLS_LDM,
110 TLS_DTPREL,
111 TLS_IE,
112 TLS_LE
113};
114
d34b0d1e 115/* Classification of a MicroBlaze address. */
116struct microblaze_address_info
117{
118 enum microblaze_address_type type;
119 rtx regA; /* Contains valid values on ADDRESS_REG, ADDRESS_REG_INDEX,
120 ADDRESS_SYMBOLIC. */
121 rtx regB; /* Contains valid values on ADDRESS_REG_INDEX. */
122 rtx offset; /* Contains valid values on ADDRESS_CONST_INT and ADDRESS_REG. */
123 rtx symbol; /* Contains valid values on ADDRESS_SYMBOLIC. */
124 enum microblaze_symbol_type symbol_type;
4eae25aa 125 enum tls_reloc tls_type;
d34b0d1e 126};
127
128/* Structure to be filled in by compute_frame_size with register
129 save masks, and offsets for the current function. */
130
131struct GTY(()) microblaze_frame_info {
132 long total_size; /* # bytes that the entire frame takes up. */
133 long var_size; /* # bytes that variables take up. */
134 long args_size; /* # bytes that outgoing arguments take up. */
135 int link_debug_size; /* # bytes for the link reg and back pointer. */
136 int gp_reg_size; /* # bytes needed to store gp regs. */
137 long gp_offset; /* offset from new sp to store gp registers. */
138 long mask; /* mask of saved gp registers. */
139 int initialized; /* != 0 if frame size already calculated. */
140 int num_gp; /* number of gp registers saved. */
141 long insns_len; /* length of insns. */
142 int alloc_stack; /* Flag to indicate if the current function
143 must not create stack space. (As an optimization). */
144};
145
146/* Global variables for machine-dependent things. */
147
148/* Toggle which pipleline interface to use. */
149static GTY(()) int microblaze_sched_use_dfa = 0;
150
151/* Threshold for data being put into the small data/bss area, instead
152 of the normal data area (references to the small data/bss area take
153 1 instruction, and use the global pointer, references to the normal
154 data area takes 2 instructions). */
155int microblaze_section_threshold = -1;
156
157/* Prevent scheduling potentially exception causing instructions in
158 delay slots. -mcpu=v3.00.a or v4.00.a turns this on. */
159int microblaze_no_unsafe_delay;
160
f8a5cd80 161/* Set to one if the targeted core has the CLZ insn. */
162int microblaze_has_clz = 0;
163
d34b0d1e 164/* Which CPU pipeline do we use. We haven't really standardized on a CPU
165 version having only a particular type of pipeline. There can still be
166 options on the CPU to scale pipeline features up or down. :(
167 Bad Presentation (??), so we let the MD file rely on the value of
168 this variable instead Making PIPE_5 the default. It should be backward
169 optimal with PIPE_3 MicroBlazes. */
170enum pipeline_type microblaze_pipe = MICROBLAZE_PIPE_5;
171
172/* High and low marks for floating point values which we will accept
ca316360 173 as legitimate constants for TARGET_LEGITIMATE_CONSTANT_P. These are
d34b0d1e 174 initialized in override_options. */
175REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow;
176
177/* Array giving truth value on whether or not a given hard register
178 can support a given mode. */
179char microblaze_hard_regno_mode_ok[(int)MAX_MACHINE_MODE]
180 [FIRST_PSEUDO_REGISTER];
181
182/* Current frame information calculated by compute_frame_size. */
183struct microblaze_frame_info current_frame_info;
184
185/* Zero structure to initialize current_frame_info. */
186struct microblaze_frame_info zero_frame_info;
187
188/* List of all MICROBLAZE punctuation characters used by print_operand. */
189char microblaze_print_operand_punct[256];
190
191/* Map GCC register number to debugger register number. */
192int microblaze_dbx_regno[FIRST_PSEUDO_REGISTER];
193
194/* Map hard register number to register class. */
195enum reg_class microblaze_regno_to_class[] =
196{
197 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
198 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
199 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
200 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
201 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
202 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
203 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
204 GR_REGS, GR_REGS, GR_REGS, GR_REGS,
205 ST_REGS, GR_REGS, GR_REGS, GR_REGS
206};
207
208/* MicroBlaze specific machine attributes.
209 interrupt_handler - Interrupt handler attribute to add interrupt prologue
210 and epilogue and use appropriate interrupt return.
9d75589a 211 save_volatiles - Similar to interrupt handler, but use normal return. */
d34b0d1e 212int interrupt_handler;
0f718e59 213int break_handler;
d9c1b601 214int fast_interrupt;
d34b0d1e 215int save_volatiles;
216
217const struct attribute_spec microblaze_attribute_table[] = {
ac86af5d 218 /* name min_len, max_len, decl_req, type_req, fn_type, req_handler,
219 affects_type_identity */
220 {"interrupt_handler", 0, 0, true, false, false, NULL,
221 false },
0f718e59 222 {"break_handler", 0, 0, true, false, false, NULL,
223 false },
d9c1b601 224 {"fast_interrupt", 0, 0, true, false, false, NULL,
225 false },
ac86af5d 226 {"save_volatiles" , 0, 0, true, false, false, NULL,
227 false },
228 { NULL, 0, 0, false, false, false, NULL,
229 false }
d34b0d1e 230};
231
232static int microblaze_interrupt_function_p (tree);
233
9dcfd437 234static void microblaze_elf_asm_constructor (rtx, int) ATTRIBUTE_UNUSED;
235static void microblaze_elf_asm_destructor (rtx, int) ATTRIBUTE_UNUSED;
236
d34b0d1e 237section *sdata2_section;
238
4eae25aa 239#ifdef HAVE_AS_TLS
240#undef TARGET_HAVE_TLS
241#define TARGET_HAVE_TLS true
242#endif
243
d34b0d1e 244/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */
ca316360 245static bool
d34b0d1e 246microblaze_const_double_ok (rtx op, enum machine_mode mode)
247{
248 REAL_VALUE_TYPE d;
249
250 if (GET_CODE (op) != CONST_DOUBLE)
251 return 0;
252
ca316360 253 if (GET_MODE (op) == VOIDmode)
d34b0d1e 254 return 1;
255
256 if (mode != SFmode && mode != DFmode)
257 return 0;
258
259 if (op == CONST0_RTX (mode))
260 return 1;
261
262 REAL_VALUE_FROM_CONST_DOUBLE (d, op);
263
264 if (REAL_VALUE_ISNAN (d))
265 return FALSE;
266
267 if (REAL_VALUE_NEGATIVE (d))
268 d = real_value_negate (&d);
269
270 if (mode == DFmode)
271 {
272 if (REAL_VALUES_LESS (d, dfhigh) && REAL_VALUES_LESS (dflow, d))
273 return 1;
274 }
275 else
276 {
277 if (REAL_VALUES_LESS (d, sfhigh) && REAL_VALUES_LESS (sflow, d))
278 return 1;
279 }
280
281 return 0;
282}
283
284/* Return truth value if a memory operand fits in a single instruction
285 (ie, register + small offset) or (register + register). */
286
287int
288simple_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
289{
290 rtx addr, plus0, plus1;
291
292 /* Eliminate non-memory operations. */
293 if (GET_CODE (op) != MEM)
294 return 0;
295
296 /* dword operations really put out 2 instructions, so eliminate them. */
297 /* ??? This isn't strictly correct. It is OK to accept multiword modes
298 here, since the length attributes are being set correctly, but only
299 if the address is offsettable. */
300 if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD)
301 return 0;
302
303
304 /* Decode the address now. */
305 addr = XEXP (op, 0);
306 switch (GET_CODE (addr))
307
308 {
309 case REG:
310 return 1;
311
312 case PLUS:
313 plus0 = XEXP (addr, 0);
314 plus1 = XEXP (addr, 1);
315
4eae25aa 316 if (GET_CODE (plus0) != REG)
317 return 0;
318
d34b0d1e 319 if (GET_CODE (plus0) == REG && GET_CODE (plus1) == CONST_INT
320 && SMALL_INT (plus1))
321 {
322 return 1;
323 }
324 else if (GET_CODE (plus1) == REG && GET_CODE (plus0) == CONST_INT)
325 {
326 return 1;
327 }
328 else if (GET_CODE (plus0) == REG && GET_CODE (plus1) == REG)
329 {
330 return 1;
331 }
332 else
333 return 0;
334
335 case SYMBOL_REF:
336 return 0;
337
338 default:
339 break;
340 }
341
342 return 0;
343}
344
345/* Return nonzero for a memory address that can be used to load or store
346 a doubleword. */
347
348int
349double_memory_operand (rtx op, enum machine_mode mode)
350{
351 rtx addr;
352
353 if (GET_CODE (op) != MEM || !memory_operand (op, mode))
354 {
355 /* During reload, we accept a pseudo register if it has an
356 appropriate memory address. If we don't do this, we will
357 wind up reloading into a register, and then reloading that
358 register from memory, when we could just reload directly from
359 memory. */
360 if (reload_in_progress
361 && GET_CODE (op) == REG
362 && REGNO (op) >= FIRST_PSEUDO_REGISTER
363 && reg_renumber[REGNO (op)] < 0
1c654ff1 364 && reg_equiv_mem (REGNO (op)) != 0
365 && double_memory_operand (reg_equiv_mem (REGNO (op)), mode))
d34b0d1e 366 return 1;
367 return 0;
368 }
369
370 /* Make sure that 4 added to the address is a valid memory address.
371 This essentially just checks for overflow in an added constant. */
372
373 addr = XEXP (op, 0);
374
375 if (CONSTANT_ADDRESS_P (addr))
376 return 1;
377
378 return memory_address_p ((GET_MODE_CLASS (mode) == MODE_INT
29c05e22 379 ? SImode : SFmode),
380 plus_constant (Pmode, addr, 4));
d34b0d1e 381}
382
383/* Implement REG_OK_FOR_BASE_P -and- REG_OK_FOR_INDEX_P. */
384int
385microblaze_regno_ok_for_base_p (int regno, int strict)
386{
387 if (regno >= FIRST_PSEUDO_REGISTER)
388 {
389 if (!strict)
390 return true;
391 regno = reg_renumber[regno];
392 }
393
394 /* These fake registers will be eliminated to either the stack or
395 hard frame pointer, both of which are usually valid base registers.
396 Reload deals with the cases where the eliminated form isn't valid. */
397 if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
398 return true;
399
400 return GP_REG_P (regno);
401}
402
403/* Return true if X is a valid base register for the given mode.
404 Allow only hard registers if STRICT. */
405
406static bool
407microblaze_valid_base_register_p (rtx x,
408 enum machine_mode mode ATTRIBUTE_UNUSED,
409 int strict)
410{
411 if (!strict && GET_CODE (x) == SUBREG)
412 x = SUBREG_REG (x);
413
414 return (GET_CODE (x) == REG
415 && microblaze_regno_ok_for_base_p (REGNO (x), strict));
416}
417
4eae25aa 418/* Build the SYMBOL_REF for __tls_get_addr. */
419
420static GTY(()) rtx tls_get_addr_libfunc;
421
422static rtx
423get_tls_get_addr (void)
424{
425 if (!tls_get_addr_libfunc)
426 tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr");
427 return tls_get_addr_libfunc;
428}
429
430/* Return TRUE if X is a thread-local symbol. */
431bool
432microblaze_tls_symbol_p (rtx x)
433{
434 if (!TARGET_HAVE_TLS)
435 return false;
436
437 if (GET_CODE (x) != SYMBOL_REF)
438 return false;
439
440 return SYMBOL_REF_TLS_MODEL (x) != 0;
441}
442
443static int
444microblaze_tls_operand_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
445{
446 if (GET_CODE (*x) == SYMBOL_REF)
447 return SYMBOL_REF_TLS_MODEL (*x) != 0;
448
449 /* Don't recurse into UNSPEC_TLS looking for TLS symbols; these are
450 TLS offsets, not real symbol references. */
451 if (GET_CODE (*x) == UNSPEC && XINT (*x, 1) == UNSPEC_TLS)
452 return -1;
453
454 return 0;
455}
456
457/* Return TRUE if X contains any TLS symbol references. */
458
459bool
460microblaze_tls_referenced_p (rtx x)
461{
462 if (!TARGET_HAVE_TLS)
463 return false;
464
465 return for_each_rtx (&x, microblaze_tls_operand_p_1, NULL);
466}
467
468bool
469microblaze_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
470{
471 return microblaze_tls_referenced_p(x);
472}
473
474/* Return TRUE if X references a SYMBOL_REF. */
475int
476symbol_mentioned_p (rtx x)
477{
478 const char * fmt;
479 int i;
480
481 if (GET_CODE (x) == SYMBOL_REF)
482 return 1;
483
484 /* UNSPEC entries for a symbol include the SYMBOL_REF, but they
485 are constant offsets, not symbols. */
486 if (GET_CODE (x) == UNSPEC)
487 return 0;
488
489 fmt = GET_RTX_FORMAT (GET_CODE (x));
490
491 for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
492 {
493 if (fmt[i] == 'E')
494 {
495 int j;
496
497 for (j = XVECLEN (x, i) - 1; j >= 0; j--)
498 if (symbol_mentioned_p (XVECEXP (x, i, j)))
499 return 1;
500 }
501 else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i)))
502 return 1;
503 }
504
505 return 0;
506}
507
508/* Return TRUE if X references a LABEL_REF. */
509int
510label_mentioned_p (rtx x)
511{
512 const char * fmt;
513 int i;
514
515 if (GET_CODE (x) == LABEL_REF)
516 return 1;
517
518 /* UNSPEC entries for a symbol include a LABEL_REF for the referencing
519 instruction, but they are constant offsets, not symbols. */
520 if (GET_CODE (x) == UNSPEC)
521 return 0;
522
523 fmt = GET_RTX_FORMAT (GET_CODE (x));
524 for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
525 {
526 if (fmt[i] == 'E')
527 {
528 int j;
529
530 for (j = XVECLEN (x, i) - 1; j >= 0; j--)
531 if (label_mentioned_p (XVECEXP (x, i, j)))
532 return 1;
533 }
534 else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i)))
535 return 1;
536 }
537
538 return 0;
539}
540
541int
542tls_mentioned_p (rtx x)
543{
544 switch (GET_CODE (x))
545 {
546 case CONST:
547 return tls_mentioned_p (XEXP (x, 0));
548
549 case UNSPEC:
550 if (XINT (x, 1) == UNSPEC_TLS)
551 return 1;
552
553 default:
554 return 0;
555 }
556}
557
558static rtx
559load_tls_operand (rtx x, rtx reg)
560{
561 rtx tmp;
562
563 if (reg == NULL_RTX)
564 reg = gen_reg_rtx (Pmode);
565
566 tmp = gen_rtx_CONST (Pmode, x);
567
568 emit_insn (gen_rtx_SET (VOIDmode, reg,
569 gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp)));
570
571 return reg;
572}
573
574static rtx
575microblaze_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
576{
577 rtx insns, tls_entry;
578
579 df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
580
581 start_sequence ();
582
583 tls_entry = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
584 UNSPEC_TLS);
585
586 reg = load_tls_operand (tls_entry, reg);
587
588 *valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX,
589 LCT_PURE, /* LCT_CONST? */
590 Pmode, 1, reg, Pmode);
591
592 insns = get_insns ();
593 end_sequence ();
594
595 return insns;
596}
597
598rtx
599microblaze_legitimize_tls_address(rtx x, rtx reg)
600{
601 rtx dest, insns, ret, eqv, addend;
602 enum tls_model model;
603 model = SYMBOL_REF_TLS_MODEL (x);
604
605 switch (model)
606 {
607 case TLS_MODEL_LOCAL_DYNAMIC:
608 case TLS_MODEL_GLOBAL_DYNAMIC:
609 case TLS_MODEL_INITIAL_EXEC:
610 insns = microblaze_call_tls_get_addr (x, reg, &ret, TLS_GD);
611 dest = gen_reg_rtx (Pmode);
612 emit_libcall_block (insns, dest, ret, x);
613 break;
614
615 case TLS_MODEL_LOCAL_EXEC:
616 insns = microblaze_call_tls_get_addr (x, reg, &ret, TLS_LDM);
617
618 /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
619 share the LDM result with other LD model accesses. */
620 eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx), UNSPEC_TLS);
621 dest = gen_reg_rtx (Pmode);
622 emit_libcall_block (insns, dest, ret, eqv);
623
624 /* Load the addend. */
625 addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (TLS_DTPREL)),
626 UNSPEC_TLS);
627 addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
628 dest = gen_rtx_PLUS (Pmode, dest, addend);
629 break;
630
631 default:
632 gcc_unreachable ();
633 }
634 return dest;
635}
636
d34b0d1e 637static bool
638microblaze_classify_unspec (struct microblaze_address_info *info, rtx x)
639{
640 info->symbol_type = SYMBOL_TYPE_GENERAL;
641 info->symbol = XVECEXP (x, 0, 0);
642
643 if (XINT (x, 1) == UNSPEC_GOTOFF)
644 {
645 info->regA = gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM);
646 info->type = ADDRESS_GOTOFF;
647 }
648 else if (XINT (x, 1) == UNSPEC_PLT)
649 {
650 info->type = ADDRESS_PLT;
651 }
4eae25aa 652 else if (XINT (x, 1) == UNSPEC_TLS)
653 {
654 info->type = ADDRESS_TLS;
655 info->tls_type = tls_reloc INTVAL(XVECEXP(x, 0, 1));
656 }
d34b0d1e 657 else
658 {
659 return false;
660 }
661 return true;
662}
663
664
665/* Return true if X is a valid index register for the given mode.
666 Allow only hard registers if STRICT. */
667
668static bool
669microblaze_valid_index_register_p (rtx x,
670 enum machine_mode mode ATTRIBUTE_UNUSED,
671 int strict)
672{
673 if (!strict && GET_CODE (x) == SUBREG)
674 x = SUBREG_REG (x);
675
676 return (GET_CODE (x) == REG
677 /* A base register is good enough to be an index register on MicroBlaze. */
678 && microblaze_regno_ok_for_base_p (REGNO (x), strict));
679}
680
681/* Get the base register for accessing a value from the memory or
682 Symbol ref. Used for MicroBlaze Small Data Area Pointer Optimization. */
683static int
684get_base_reg (rtx x)
685{
686 tree decl;
4eae25aa 687 int base_reg;
688
689 if (!flag_pic || microblaze_tls_symbol_p(x))
690 base_reg = MB_ABI_BASE_REGNUM;
691 else if (flag_pic)
692 base_reg = MB_ABI_PIC_ADDR_REGNUM;
d34b0d1e 693
694 if (TARGET_XLGPOPT
695 && GET_CODE (x) == SYMBOL_REF
696 && SYMBOL_REF_SMALL_P (x) && (decl = SYMBOL_REF_DECL (x)) != NULL)
697 {
698 if (TREE_READONLY (decl))
699 base_reg = MB_ABI_GPRO_REGNUM;
700 else
701 base_reg = MB_ABI_GPRW_REGNUM;
702 }
703
704 return base_reg;
705}
706
707/* Return true if X is a valid address for machine mode MODE. If it is,
708 fill in INFO appropriately. STRICT is true if we should only accept
709 hard base registers.
710
711 type regA regB offset symbol
712
713 ADDRESS_INVALID NULL NULL NULL NULL
714
715 ADDRESS_REG %0 NULL const_0 / NULL
716 const_int
717 ADDRESS_REG_INDEX %0 %1 NULL NULL
718
719 ADDRESS_SYMBOLIC r0 / NULL NULL symbol
720 sda_base_reg
721
722 ADDRESS_CONST_INT r0 NULL const NULL
723
724 For modes spanning multiple registers (DFmode in 32-bit GPRs,
725 DImode, TImode), indexed addressing cannot be used because
726 adjacent memory cells are accessed by adding word-sized offsets
727 during assembly output. */
728
729static bool
730microblaze_classify_address (struct microblaze_address_info *info, rtx x,
731 enum machine_mode mode, int strict)
732{
733 rtx xplus0;
734 rtx xplus1;
735
736 info->type = ADDRESS_INVALID;
737 info->regA = NULL;
738 info->regB = NULL;
739 info->offset = NULL;
740 info->symbol = NULL;
741 info->symbol_type = SYMBOL_TYPE_INVALID;
742
743 switch (GET_CODE (x))
744 {
745 case REG:
746 case SUBREG:
747 {
748 info->type = ADDRESS_REG;
749 info->regA = x;
750 info->offset = const0_rtx;
751 return microblaze_valid_base_register_p (info->regA, mode, strict);
752 }
753 case PLUS:
754 {
755 xplus0 = XEXP (x, 0);
756 xplus1 = XEXP (x, 1);
757
758 if (microblaze_valid_base_register_p (xplus0, mode, strict))
759 {
760 info->type = ADDRESS_REG;
761 info->regA = xplus0;
762
763 if (GET_CODE (xplus1) == CONST_INT)
764 {
765 info->offset = xplus1;
766 return true;
767 }
768 else if (GET_CODE (xplus1) == UNSPEC)
769 {
4eae25aa 770 /* Need offsettable address. */
771 if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
772 return false;
773
d34b0d1e 774 return microblaze_classify_unspec (info, xplus1);
775 }
776 else if ((GET_CODE (xplus1) == SYMBOL_REF ||
4eae25aa 777 GET_CODE (xplus1) == LABEL_REF))
d34b0d1e 778 {
4eae25aa 779 if (flag_pic == 2 || microblaze_tls_symbol_p(xplus1))
780 return false;
d34b0d1e 781 info->type = ADDRESS_SYMBOLIC;
782 info->symbol = xplus1;
783 info->symbol_type = SYMBOL_TYPE_GENERAL;
784 return true;
785 }
4eae25aa 786 else if (GET_CODE (xplus1) == CONST)
787 {
788 rtx xconst0 = XEXP(xplus1, 0);
789
790 /* base + unspec. */
791 if (GET_CODE (xconst0) == UNSPEC)
792 {
793 /* Need offsettable address. */
794 if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
795 return false;
796 return microblaze_classify_unspec(info, xconst0);
797 }
798
799 /* for (plus x const_int) just look at x. */
800 if (GET_CODE (xconst0) == PLUS
801 && GET_CODE (XEXP (xconst0, 1)) == CONST_INT
802 && SMALL_INT (XEXP (xconst0, 1)))
803 {
804 /* This is ok as info->symbol is set to xplus1 the full
805 const-expression below. */
806 xconst0 = XEXP (xconst0, 0);
807 }
808
809 if (GET_CODE (xconst0) == SYMBOL_REF
810 || GET_CODE (xconst0) == LABEL_REF)
811 {
812 if (flag_pic == 2 || microblaze_tls_symbol_p(xconst0))
813 return false;
814
815 info->type = ADDRESS_SYMBOLIC;
816 info->symbol = xplus1;
817 info->symbol_type = SYMBOL_TYPE_GENERAL;
818 return true;
819 }
820
821 /* Not base + symbol || base + UNSPEC. */
822 return false;
823
824 }
d34b0d1e 825 else if (GET_CODE (xplus1) == REG
826 && microblaze_valid_index_register_p (xplus1, mode,
827 strict)
828 && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD))
829 {
830 /* Restrict larger than word-width modes from using an index register. */
831 info->type = ADDRESS_REG_INDEX;
832 info->regB = xplus1;
833 return true;
834 }
835 }
836 break;
837 }
838 case CONST_INT:
839 {
840 info->regA = gen_rtx_raw_REG (mode, 0);
841 info->type = ADDRESS_CONST_INT;
842 info->offset = x;
843 return true;
844 }
845 case CONST:
846 case LABEL_REF:
847 case SYMBOL_REF:
848 {
849 info->type = ADDRESS_SYMBOLIC;
850 info->symbol_type = SYMBOL_TYPE_GENERAL;
851 info->symbol = x;
852 info->regA = gen_rtx_raw_REG (mode, get_base_reg (x));
853
854 if (GET_CODE (x) == CONST)
855 {
4eae25aa 856 if (GET_CODE (XEXP (x, 0)) == UNSPEC)
857 {
858 info->regA = gen_rtx_raw_REG (mode,
859 get_base_reg (XVECEXP (XEXP (x,0), 0, 0)));
860 return microblaze_classify_unspec (info, XEXP (x, 0));
861 }
862 return !(flag_pic && pic_address_needs_scratch (x));
d34b0d1e 863 }
864
4eae25aa 865 if (flag_pic == 2)
866 return false;
867 else if (microblaze_tls_symbol_p(x))
868 return false;
869
d34b0d1e 870 return true;
871 }
872
873 case UNSPEC:
874 {
875 if (reload_in_progress)
876 df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
877 return microblaze_classify_unspec (info, x);
878 }
879
880 default:
881 return false;
882 }
883
884 return false;
885}
886
887/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It
888 returns a nonzero value if X is a legitimate address for a memory
889 operand of the indicated MODE. STRICT is nonzero if this function
890 is called during reload. */
891
892bool
893microblaze_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
894{
895 struct microblaze_address_info addr;
896
897 return microblaze_classify_address (&addr, x, mode, strict);
898}
899
4f12cc20 900int
901microblaze_valid_pic_const (rtx x)
902{
903 switch (GET_CODE (x))
904 {
905 case CONST:
906 case CONST_INT:
907 case CONST_DOUBLE:
908 return true;
909 default:
910 return false;
911 }
912}
913
914int
915microblaze_legitimate_pic_operand (rtx x)
916{
4eae25aa 917 if (flag_pic == 2 && (symbol_mentioned_p(x) || label_mentioned_p(x)))
4f12cc20 918 return 0;
4eae25aa 919
920 if (microblaze_tls_referenced_p(x))
4f12cc20 921 return 0;
922
923 return 1;
924}
d34b0d1e 925
926/* Try machine-dependent ways of modifying an illegitimate address
927 to be legitimate. If we find one, return the new, valid address.
928 This is used from only one place: `memory_address' in explow.c.
929
930 OLDX is the address as it was before break_out_memory_refs was
931 called. In some cases it is useful to look at this to decide what
932 needs to be done.
933
934 It is always safe for this function to do nothing. It exists to
935 recognize opportunities to optimize the output.
936
937 For the MicroBlaze, transform:
938
939 memory(X + <large int>)
940
941 into:
942
943 Y = <large int> & ~0x7fff;
944 Z = X + Y
945 memory (Z + (<large int> & 0x7fff));
946
947 This is for CSE to find several similar references, and only use one Z.
948
949 When PIC, convert addresses of the form memory (symbol+large int) to
950 memory (reg+large int). */
951
952static rtx
953microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
954 enum machine_mode mode ATTRIBUTE_UNUSED)
955{
956 register rtx xinsn = x, result;
957
958 if (GET_CODE (xinsn) == CONST
959 && flag_pic && pic_address_needs_scratch (xinsn))
960 {
961 rtx ptr_reg = gen_reg_rtx (Pmode);
962 rtx constant = XEXP (XEXP (xinsn, 0), 1);
963
964 emit_move_insn (ptr_reg, XEXP (XEXP (xinsn, 0), 0));
965
966 result = gen_rtx_PLUS (Pmode, ptr_reg, constant);
967 if (SMALL_INT (constant))
968 return result;
969 /* Otherwise we fall through so the code below will fix the
970 constant. */
971 xinsn = result;
972 }
973
974 if (GET_CODE (xinsn) == PLUS)
975 {
976 register rtx xplus0 = XEXP (xinsn, 0);
977 register rtx xplus1 = XEXP (xinsn, 1);
978 register enum rtx_code code0 = GET_CODE (xplus0);
979 register enum rtx_code code1 = GET_CODE (xplus1);
980
981 if (code0 != REG && code1 == REG)
982 {
983 xplus0 = XEXP (xinsn, 1);
984 xplus1 = XEXP (xinsn, 0);
985 code0 = GET_CODE (xplus0);
986 code1 = GET_CODE (xplus1);
987 }
988
989 if (code0 == REG && REG_OK_FOR_BASE_P (xplus0)
990 && code1 == CONST_INT && !SMALL_INT (xplus1))
991 {
992 rtx int_reg = gen_reg_rtx (Pmode);
993 rtx ptr_reg = gen_reg_rtx (Pmode);
994
995 emit_move_insn (int_reg, GEN_INT (INTVAL (xplus1) & ~0x7fff));
996
997 emit_insn (gen_rtx_SET (VOIDmode,
998 ptr_reg,
999 gen_rtx_PLUS (Pmode, xplus0, int_reg)));
1000
1001 result = gen_rtx_PLUS (Pmode, ptr_reg,
1002 GEN_INT (INTVAL (xplus1) & 0x7fff));
1003 return result;
1004 }
1005
4eae25aa 1006 if (code0 == REG && REG_OK_FOR_BASE_P (xplus0))
d34b0d1e 1007 {
1008 if (reload_in_progress)
1009 df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
1010 if (code1 == CONST)
1011 {
1012 xplus1 = XEXP (xplus1, 0);
1013 code1 = GET_CODE (xplus1);
1014 }
1015 if (code1 == SYMBOL_REF)
1016 {
4eae25aa 1017 if (microblaze_tls_symbol_p(xplus1))
1018 {
1019 rtx tls_ref, reg;
1020 reg = gen_reg_rtx (Pmode);
1021
1022 tls_ref = microblaze_legitimize_tls_address (xplus1,
1023 NULL_RTX);
1024 emit_move_insn (reg, tls_ref);
1025
1026 result = gen_rtx_PLUS (Pmode, xplus0, reg);
1027
1028 return result;
1029 }
1030 else if (flag_pic == 2)
1031 {
1032 rtx pic_ref, reg;
1033 reg = gen_reg_rtx (Pmode);
1034
1035 pic_ref = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xplus1),
1036 UNSPEC_GOTOFF);
1037 pic_ref = gen_rtx_CONST (Pmode, pic_ref);
1038 pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pic_ref);
1039 pic_ref = gen_const_mem (Pmode, pic_ref);
1040 emit_move_insn (reg, pic_ref);
1041 result = gen_rtx_PLUS (Pmode, xplus0, reg);
1042 return result;
1043 }
d34b0d1e 1044 }
1045 }
1046 }
1047
1048 if (GET_CODE (xinsn) == SYMBOL_REF)
1049 {
4eae25aa 1050 rtx reg;
1051 if (microblaze_tls_symbol_p(xinsn))
1052 {
1053 reg = microblaze_legitimize_tls_address (xinsn, NULL_RTX);
1054 }
1055 else
1056 {
1057 rtx pic_ref;
1058
1059 if (reload_in_progress)
1060 df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
1061
1062 pic_ref = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xinsn), UNSPEC_GOTOFF);
1063 pic_ref = gen_rtx_CONST (Pmode, pic_ref);
1064 pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pic_ref);
1065 pic_ref = gen_const_mem (Pmode, pic_ref);
1066 reg = pic_ref;
1067 }
1068 return reg;
d34b0d1e 1069 }
1070
1071 return x;
1072}
1073
1074/* Block Moves. */
1075
1076#define MAX_MOVE_REGS 8
1077#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
1078
1079/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
1080 Assume that the areas do not overlap. */
1081
1082static void
1083microblaze_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
1084{
1085 HOST_WIDE_INT offset, delta;
1086 unsigned HOST_WIDE_INT bits;
1087 int i;
1088 enum machine_mode mode;
1089 rtx *regs;
1090
1091 bits = BITS_PER_WORD;
1092 mode = mode_for_size (bits, MODE_INT, 0);
1093 delta = bits / BITS_PER_UNIT;
1094
1095 /* Allocate a buffer for the temporary registers. */
037176b6 1096 regs = XALLOCAVEC (rtx, length / delta);
d34b0d1e 1097
1098 /* Load as many BITS-sized chunks as possible. Use a normal load if
1099 the source has enough alignment, otherwise use left/right pairs. */
1100 for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
1101 {
1102 regs[i] = gen_reg_rtx (mode);
1103 emit_move_insn (regs[i], adjust_address (src, mode, offset));
1104 }
1105
1106 /* Copy the chunks to the destination. */
1107 for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
1108 emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
1109
1110 /* Mop up any left-over bytes. */
1111 if (offset < length)
1112 {
1113 src = adjust_address (src, BLKmode, offset);
1114 dest = adjust_address (dest, BLKmode, offset);
1115 move_by_pieces (dest, src, length - offset,
1116 MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
1117 }
1118}
1119
1120/* Helper function for doing a loop-based block operation on memory
1121 reference MEM. Each iteration of the loop will operate on LENGTH
1122 bytes of MEM.
1123
1124 Create a new base register for use within the loop and point it to
1125 the start of MEM. Create a new memory reference that uses this
1126 register. Store them in *LOOP_REG and *LOOP_MEM respectively. */
1127
1128static void
1129microblaze_adjust_block_mem (rtx mem, HOST_WIDE_INT length,
1130 rtx * loop_reg, rtx * loop_mem)
1131{
1132 *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
1133
1134 /* Although the new mem does not refer to a known location,
1135 it does keep up to LENGTH bytes of alignment. */
1136 *loop_mem = change_address (mem, BLKmode, *loop_reg);
1137 set_mem_align (*loop_mem,
1138 MIN ((HOST_WIDE_INT) MEM_ALIGN (mem),
1139 length * BITS_PER_UNIT));
1140}
1141
1142
1143/* Move LENGTH bytes from SRC to DEST using a loop that moves MAX_MOVE_BYTES
1144 per iteration. LENGTH must be at least MAX_MOVE_BYTES. Assume that the
1145 memory regions do not overlap. */
1146
1147static void
1148microblaze_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length)
1149{
1150 rtx label, src_reg, dest_reg, final_src;
1151 HOST_WIDE_INT leftover;
1152
1153 leftover = length % MAX_MOVE_BYTES;
1154 length -= leftover;
1155
1156 /* Create registers and memory references for use within the loop. */
1157 microblaze_adjust_block_mem (src, MAX_MOVE_BYTES, &src_reg, &src);
1158 microblaze_adjust_block_mem (dest, MAX_MOVE_BYTES, &dest_reg, &dest);
1159
1160 /* Calculate the value that SRC_REG should have after the last iteration
1161 of the loop. */
1162 final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length),
1163 0, 0, OPTAB_WIDEN);
1164
1165 /* Emit the start of the loop. */
1166 label = gen_label_rtx ();
1167 emit_label (label);
1168
1169 /* Emit the loop body. */
1170 microblaze_block_move_straight (dest, src, MAX_MOVE_BYTES);
1171
1172 /* Move on to the next block. */
29c05e22 1173 emit_move_insn (src_reg, plus_constant (Pmode, src_reg, MAX_MOVE_BYTES));
1174 emit_move_insn (dest_reg, plus_constant (Pmode, dest_reg, MAX_MOVE_BYTES));
d34b0d1e 1175
1176 /* Emit the test & branch. */
1177 emit_insn (gen_cbranchsi4 (gen_rtx_NE (SImode, src_reg, final_src),
1178 src_reg, final_src, label));
1179
1180 /* Mop up any left-over bytes. */
1181 if (leftover)
1182 microblaze_block_move_straight (dest, src, leftover);
1183}
1184
1185/* Expand a movmemsi instruction. */
1186
1187bool
1188microblaze_expand_block_move (rtx dest, rtx src, rtx length, rtx align_rtx)
1189{
1190
1191 if (GET_CODE (length) == CONST_INT)
1192 {
1193 HOST_WIDE_INT bytes = INTVAL (length);
1194 int align = INTVAL (align_rtx);
1195
1196 if (align > UNITS_PER_WORD)
1197 {
1198 align = UNITS_PER_WORD; /* We can't do any better. */
1199 }
1200 else if (align < UNITS_PER_WORD)
1201 {
1202 if (INTVAL (length) <= MAX_MOVE_BYTES)
1203 {
1204 move_by_pieces (dest, src, bytes, align, 0);
1205 return true;
1206 }
1207 else
1208 return false;
1209 }
1210
1211 if (INTVAL (length) <= 2 * MAX_MOVE_BYTES)
1212 {
1213 microblaze_block_move_straight (dest, src, INTVAL (length));
1214 return true;
1215 }
1216 else if (optimize)
1217 {
1218 microblaze_block_move_loop (dest, src, INTVAL (length));
1219 return true;
1220 }
1221 }
1222 return false;
1223}
1224
1225static bool
20d892d1 1226microblaze_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED,
1227 int opno ATTRIBUTE_UNUSED, int *total,
d34b0d1e 1228 bool speed ATTRIBUTE_UNUSED)
1229{
1230 enum machine_mode mode = GET_MODE (x);
1231
1232 switch (code)
1233 {
1234 case MEM:
1235 {
1236 int num_words = (GET_MODE_SIZE (mode) > UNITS_PER_WORD) ? 2 : 1;
1237 if (simple_memory_operand (x, mode))
1238 *total = COSTS_N_INSNS (2 * num_words);
1239 else
1240 *total = COSTS_N_INSNS (2 * (2 * num_words));
1241
1242 return true;
1243 }
1244 case NOT:
1245 {
1246 if (mode == DImode)
1247 {
1248 *total = COSTS_N_INSNS (2);
1249 }
1250 else
1251 *total = COSTS_N_INSNS (1);
1252 return false;
1253 }
1254 case AND:
1255 case IOR:
1256 case XOR:
1257 {
1258 if (mode == DImode)
1259 {
1260 *total = COSTS_N_INSNS (2);
1261 }
1262 else
1263 *total = COSTS_N_INSNS (1);
1264
1265 return false;
1266 }
1267 case ASHIFT:
1268 case ASHIFTRT:
1269 case LSHIFTRT:
1270 {
1271 if (TARGET_BARREL_SHIFT)
1272 {
1273 if (MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v5.00.a")
1274 >= 0)
1275 *total = COSTS_N_INSNS (1);
1276 else
1277 *total = COSTS_N_INSNS (2);
1278 }
1279 else if (!TARGET_SOFT_MUL)
1280 *total = COSTS_N_INSNS (1);
1281 else if (GET_CODE (XEXP (x, 1)) == CONST_INT)
1282 {
1283 /* Add 1 to make shift slightly more expensive than add. */
1284 *total = COSTS_N_INSNS (INTVAL (XEXP (x, 1))) + 1;
e66d204f 1285 /* Reduce shift costs for special circumstances. */
d34b0d1e 1286 if (optimize_size && INTVAL (XEXP (x, 1)) > 5)
1287 *total -= 2;
1288 if (!optimize_size && INTVAL (XEXP (x, 1)) > 17)
1289 *total -= 2;
1290 }
1291 else
1292 /* Double the worst cost of shifts when there is no barrel shifter and
1293 the shift amount is in a reg. */
1294 *total = COSTS_N_INSNS (32 * 4);
1295 return true;
1296 }
1297 case PLUS:
1298 case MINUS:
1299 {
1300 if (mode == SFmode || mode == DFmode)
1301 {
1302 if (TARGET_HARD_FLOAT)
1303 *total = COSTS_N_INSNS (6);
1304 return true;
1305 }
1306 else if (mode == DImode)
1307 {
1308 *total = COSTS_N_INSNS (4);
1309 return true;
1310 }
1311 else
1312 {
1313 *total = COSTS_N_INSNS (1);
1314 return true;
1315 }
1316
1317 return false;
1318 }
1319 case NEG:
1320 {
1321 if (mode == DImode)
1322 *total = COSTS_N_INSNS (4);
1323
1324 return false;
1325 }
1326 case MULT:
1327 {
1328 if (mode == SFmode)
1329 {
1330 if (TARGET_HARD_FLOAT)
1331 *total = COSTS_N_INSNS (6);
1332 }
1333 else if (!TARGET_SOFT_MUL)
1334 {
1335 if (MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v5.00.a")
1336 >= 0)
1337 *total = COSTS_N_INSNS (1);
1338 else
1339 *total = COSTS_N_INSNS (3);
1340 }
1341 else
1342 *total = COSTS_N_INSNS (10);
1343 return true;
1344 }
1345 case DIV:
1346 case UDIV:
1347 {
1348 if (mode == SFmode)
1349 {
1350 if (TARGET_HARD_FLOAT)
1351 *total = COSTS_N_INSNS (23);
1352 }
1353 return false;
1354 }
1355 case SIGN_EXTEND:
1356 {
1357 *total = COSTS_N_INSNS (1);
1358 return false;
1359 }
1360 case ZERO_EXTEND:
1361 {
1362 *total = COSTS_N_INSNS (1);
1363 return false;
1364 }
1365 }
1366
1367 return false;
1368}
1369
1370/* Return the number of instructions needed to load or store a value
1371 of mode MODE at X. Return 0 if X isn't valid for MODE. */
1372
1373static int
1374microblaze_address_insns (rtx x, enum machine_mode mode)
1375{
1376 struct microblaze_address_info addr;
1377
1378 if (microblaze_classify_address (&addr, x, mode, false))
1379 {
1380 switch (addr.type)
1381 {
1382 case ADDRESS_REG:
1383 if (SMALL_INT (addr.offset))
1384 return 1;
1385 else
1386 return 2;
1387 case ADDRESS_CONST_INT:
1388 if (SMALL_INT (x))
1389 return 1;
1390 else
1391 return 2;
1392 case ADDRESS_REG_INDEX:
d34b0d1e 1393 return 1;
4eae25aa 1394 case ADDRESS_SYMBOLIC:
d34b0d1e 1395 case ADDRESS_GOTOFF:
1396 return 2;
4eae25aa 1397 case ADDRESS_TLS:
1398 switch (addr.tls_type)
1399 {
1400 case TLS_GD:
1401 return 2;
1402 case TLS_LDM:
1403 return 2;
1404 case TLS_DTPREL:
1405 return 1;
1406 default :
1407 abort();
1408 }
d34b0d1e 1409 default:
1410 break;
1411 }
1412 }
1413 return 0;
1414}
1415
1416/* Provide the costs of an addressing mode that contains ADDR.
1417 If ADDR is not a valid address, its cost is irrelevant. */
1418static int
d9c5e5f4 1419microblaze_address_cost (rtx addr, enum machine_mode mode ATTRIBUTE_UNUSED,
1420 addr_space_t as ATTRIBUTE_UNUSED,
1421 bool speed ATTRIBUTE_UNUSED)
d34b0d1e 1422{
1423 return COSTS_N_INSNS (microblaze_address_insns (addr, GET_MODE (addr)));
1424}
1425
1426/* Return nonzero if X is an address which needs a temporary register when
1427 reloaded while generating PIC code. */
1428
1429int
1430pic_address_needs_scratch (rtx x)
1431{
4eae25aa 1432 if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
1433 {
1434 rtx p0, p1;
d34b0d1e 1435
4eae25aa 1436 p0 = XEXP (XEXP (x, 0), 0);
1437 p1 = XEXP (XEXP (x, 0), 1);
1438
1439 if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
1440 && (GET_CODE (p1) == CONST_INT)
1441 && (flag_pic == 2 || microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
1442 return 1;
1443 }
d34b0d1e 1444 return 0;
1445}
1446
1447/* Argument support functions. */
1448/* Initialize CUMULATIVE_ARGS for a function. */
1449
1450void
1451init_cumulative_args (CUMULATIVE_ARGS * cum, tree fntype,
1452 rtx libname ATTRIBUTE_UNUSED)
1453{
1454 static CUMULATIVE_ARGS zero_cum;
1455 tree param, next_param;
1456
1457 *cum = zero_cum;
1458
1459 /* Determine if this function has variable arguments. This is
1460 indicated by the last argument being 'void_type_mode' if there
1461 are no variable arguments. The standard MicroBlaze calling sequence
1462 passes all arguments in the general purpose registers in this case. */
1463
1464 for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
1465 param != 0; param = next_param)
1466 {
1467 next_param = TREE_CHAIN (param);
1468 if (next_param == 0 && TREE_VALUE (param) != void_type_node)
1469 cum->gp_reg_found = 1;
1470 }
1471}
1472
1473/* Advance the argument to the next argument position. */
1474
9a51523d 1475static void
39cba157 1476microblaze_function_arg_advance (cumulative_args_t cum_v,
1477 enum machine_mode mode,
9a51523d 1478 const_tree type, bool named ATTRIBUTE_UNUSED)
d34b0d1e 1479{
39cba157 1480 CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1481
d34b0d1e 1482 cum->arg_number++;
1483 switch (mode)
1484 {
1485 case VOIDmode:
1486 break;
1487
1488 default:
1489 gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
1490 || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
1491
1492 cum->gp_reg_found = 1;
1493 cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
1494 / UNITS_PER_WORD);
1495 break;
1496
1497 case BLKmode:
1498 cum->gp_reg_found = 1;
1499 cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
1500 / UNITS_PER_WORD);
1501 break;
1502
1503 case SFmode:
1504 cum->arg_words++;
1505 if (!cum->gp_reg_found && cum->arg_number <= 2)
1506 cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
1507 break;
1508
1509 case DFmode:
1510 cum->arg_words += 2;
1511 if (!cum->gp_reg_found && cum->arg_number <= 2)
1512 cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
1513 break;
1514
1515 case DImode:
1516 cum->gp_reg_found = 1;
1517 cum->arg_words += 2;
1518 break;
1519
1520 case QImode:
1521 case HImode:
1522 case SImode:
1523 case TImode:
1524 cum->gp_reg_found = 1;
1525 cum->arg_words++;
1526 break;
1527 }
1528}
1529
1530/* Return an RTL expression containing the register for the given mode,
1531 or 0 if the argument is to be passed on the stack. */
1532
9a51523d 1533static rtx
39cba157 1534microblaze_function_arg (cumulative_args_t cum_v, enum machine_mode mode,
9a51523d 1535 const_tree type ATTRIBUTE_UNUSED,
1536 bool named ATTRIBUTE_UNUSED)
d34b0d1e 1537{
39cba157 1538 CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1539
d34b0d1e 1540 rtx ret;
1541 int regbase = -1;
1542 int *arg_words = &cum->arg_words;
1543
1544 cum->last_arg_fp = 0;
1545 switch (mode)
1546 {
1547 case SFmode:
1548 case DFmode:
1549 case VOIDmode:
1550 case QImode:
1551 case HImode:
1552 case SImode:
1553 case DImode:
1554 case TImode:
1555 regbase = GP_ARG_FIRST;
1556 break;
1557 default:
1558 gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
1559 || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
1560 /* Drops through. */
1561 case BLKmode:
1562 regbase = GP_ARG_FIRST;
1563 break;
1564 }
1565
1566 if (*arg_words >= MAX_ARGS_IN_REGISTERS)
1567 ret = 0;
1568 else
1569 {
1570 gcc_assert (regbase != -1);
1571
1572 ret = gen_rtx_REG (mode, regbase + *arg_words);
1573 }
1574
1575 if (mode == VOIDmode)
1576 {
1577 if (cum->num_adjusts > 0)
1578 ret = gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
1579 gen_rtvec_v (cum->num_adjusts, cum->adjust));
1580 }
1581
1582 return ret;
1583}
1584
1585/* Return number of bytes of argument to put in registers. */
1586static int
39cba157 1587function_arg_partial_bytes (cumulative_args_t cum_v, enum machine_mode mode,
d34b0d1e 1588 tree type, bool named ATTRIBUTE_UNUSED)
1589{
39cba157 1590 CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1591
d34b0d1e 1592 if ((mode == BLKmode
1593 || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
1594 || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
1595 && cum->arg_words < MAX_ARGS_IN_REGISTERS)
1596 {
1597 int words;
1598 if (mode == BLKmode)
1599 words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
1600 / UNITS_PER_WORD);
1601 else
1602 words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
1603
1604 if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS)
1605 return 0; /* structure fits in registers */
1606
1607 return (MAX_ARGS_IN_REGISTERS - cum->arg_words) * UNITS_PER_WORD;
1608 }
1609
1610 else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS - 1)
1611 return UNITS_PER_WORD;
1612
1613 return 0;
1614}
1615
1616/* Convert a version number of the form "vX.YY.Z" to an integer encoding
1617 for easier range comparison. */
1618static int
1619microblaze_version_to_int (const char *version)
1620{
1621 const char *p, *v;
24b9f6cb 1622 const char *tmpl = "vXX.YY.Z";
d34b0d1e 1623 int iver = 0;
1624
1625 p = version;
1626 v = tmpl;
1627
24b9f6cb 1628 while (*p)
d34b0d1e 1629 {
1630 if (*v == 'X')
1631 { /* Looking for major */
24b9f6cb 1632 if (*p == '.')
1633 {
1634 *v++;
1635 }
1636 else
1637 {
1638 if (!(*p >= '0' && *p <= '9'))
1639 return -1;
1640 iver += (int) (*p - '0');
1641 iver *= 10;
1642 }
1643 }
d34b0d1e 1644 else if (*v == 'Y')
1645 { /* Looking for minor */
1646 if (!(*p >= '0' && *p <= '9'))
1647 return -1;
1648 iver += (int) (*p - '0');
1649 iver *= 10;
1650 }
1651 else if (*v == 'Z')
1652 { /* Looking for compat */
1653 if (!(*p >= 'a' && *p <= 'z'))
1654 return -1;
1655 iver *= 10;
1656 iver += (int) (*p - 'a');
1657 }
1658 else
1659 {
1660 if (*p != *v)
1661 return -1;
1662 }
1663
1664 v++;
1665 p++;
1666 }
1667
1668 if (*p)
1669 return -1;
1670
1671 return iver;
1672}
1673
d34b0d1e 1674
1675static void
1676microblaze_option_override (void)
1677{
1678 register int i, start;
1679 register int regno;
1680 register enum machine_mode mode;
1681 int ver;
1682
13a54dd9 1683 microblaze_section_threshold = (global_options_set.x_g_switch_value
1684 ? g_switch_value
1685 : MICROBLAZE_DEFAULT_GVALUE);
d34b0d1e 1686
f9ca9e46 1687 if (flag_pic)
1688 {
1689 /* Make sure it's 2, we only support one kind of PIC. */
1690 flag_pic = 2;
1691 if (!TARGET_SUPPORTS_PIC)
1692 {
1693 error ("-fPIC/-fpic not supported for this target");
1694 /* Clear it to avoid further errors. */
1695 flag_pic = 0;
1696 }
1697 }
1698
d34b0d1e 1699 /* Check the MicroBlaze CPU version for any special action to be done. */
1700 if (microblaze_select_cpu == NULL)
1701 microblaze_select_cpu = MICROBLAZE_DEFAULT_CPU;
1702 ver = microblaze_version_to_int (microblaze_select_cpu);
1703 if (ver == -1)
1704 {
1705 error ("%qs is an invalid argument to -mcpu=", microblaze_select_cpu);
1706 }
1707
1708 ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v3.00.a");
1709 if (ver < 0)
1710 {
1711 /* No hardware exceptions in earlier versions. So no worries. */
037176b6 1712#if 0
1713 microblaze_select_flags &= ~(MICROBLAZE_MASK_NO_UNSAFE_DELAY);
1714#endif
d34b0d1e 1715 microblaze_no_unsafe_delay = 0;
1716 microblaze_pipe = MICROBLAZE_PIPE_3;
1717 }
1718 else if (ver == 0
1719 || (MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v4.00.b")
1720 == 0))
1721 {
037176b6 1722#if 0
1723 microblaze_select_flags |= (MICROBLAZE_MASK_NO_UNSAFE_DELAY);
1724#endif
d34b0d1e 1725 microblaze_no_unsafe_delay = 1;
1726 microblaze_pipe = MICROBLAZE_PIPE_3;
1727 }
1728 else
1729 {
1730 /* We agree to use 5 pipe-stage model even on area optimized 3
1731 pipe-stage variants. */
037176b6 1732#if 0
1733 microblaze_select_flags &= ~(MICROBLAZE_MASK_NO_UNSAFE_DELAY);
1734#endif
d34b0d1e 1735 microblaze_no_unsafe_delay = 0;
1736 microblaze_pipe = MICROBLAZE_PIPE_5;
1737 if (MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v5.00.a") == 0
1738 || MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu,
1739 "v5.00.b") == 0
1740 || MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu,
1741 "v5.00.c") == 0)
1742 {
1743 /* Pattern compares are to be turned on by default only when
1744 compiling for MB v5.00.'z'. */
1745 target_flags |= MASK_PATTERN_COMPARE;
1746 }
1747 }
1748
1749 ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v6.00.a");
1750 if (ver < 0)
1751 {
1752 if (TARGET_MULTIPLY_HIGH)
1753 warning (0,
1754 "-mxl-multiply-high can be used only with -mcpu=v6.00.a or greater");
1755 }
1756
f8a5cd80 1757 ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v8.10.a");
1758 microblaze_has_clz = 1;
1759 if (ver < 0)
1760 {
1761 /* MicroBlaze prior to 8.10.a didn't have clz. */
1762 microblaze_has_clz = 0;
1763 }
1764
db38a061 1765 /* TARGET_REORDER defaults to 2 if -mxl-reorder not specified. */
1766 ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v8.30.a");
1767 if (ver < 0)
1768 {
1769 if (TARGET_REORDER == 1)
1770 warning (0, "-mxl-reorder can be used only with -mcpu=v8.30.a or greater");
1771 TARGET_REORDER = 0;
1772 }
1773 else if ((ver == 0) && !TARGET_PATTERN_COMPARE)
1774 {
1775 if (TARGET_REORDER == 1)
1776 warning (0, "-mxl-reorder requires -mxl-pattern-compare for -mcpu=v8.30.a");
1777 TARGET_REORDER = 0;
1778 }
1779
d34b0d1e 1780 if (TARGET_MULTIPLY_HIGH && TARGET_SOFT_MUL)
1781 error ("-mxl-multiply-high requires -mno-xl-soft-mul");
1782
1783 /* Always use DFA scheduler. */
1784 microblaze_sched_use_dfa = 1;
1785
037176b6 1786#if 0
1787 microblaze_abicalls = MICROBLAZE_ABICALLS_NO;
1788#endif
d34b0d1e 1789
1790 /* Initialize the high, low values for legit floating point constants. */
1791 real_maxval (&dfhigh, 0, DFmode);
1792 real_maxval (&dflow, 1, DFmode);
1793 real_maxval (&sfhigh, 0, SFmode);
1794 real_maxval (&sflow, 1, SFmode);
1795
1796 microblaze_print_operand_punct['?'] = 1;
1797 microblaze_print_operand_punct['#'] = 1;
1798 microblaze_print_operand_punct['&'] = 1;
1799 microblaze_print_operand_punct['!'] = 1;
1800 microblaze_print_operand_punct['*'] = 1;
1801 microblaze_print_operand_punct['@'] = 1;
1802 microblaze_print_operand_punct['.'] = 1;
1803 microblaze_print_operand_punct['('] = 1;
1804 microblaze_print_operand_punct[')'] = 1;
1805 microblaze_print_operand_punct['['] = 1;
1806 microblaze_print_operand_punct[']'] = 1;
1807 microblaze_print_operand_punct['<'] = 1;
1808 microblaze_print_operand_punct['>'] = 1;
1809 microblaze_print_operand_punct['{'] = 1;
1810 microblaze_print_operand_punct['}'] = 1;
1811 microblaze_print_operand_punct['^'] = 1;
1812 microblaze_print_operand_punct['$'] = 1;
1813 microblaze_print_operand_punct['+'] = 1;
1814
1815 /* Set up array to map GCC register number to debug register number.
1816 Ignore the special purpose register numbers. */
1817
1818 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
1819 microblaze_dbx_regno[i] = -1;
1820
1821 start = GP_DBX_FIRST - GP_REG_FIRST;
1822 for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
1823 microblaze_dbx_regno[i] = i + start;
1824
1825 /* Set up array giving whether a given register can hold a given mode. */
1826
1827 for (mode = VOIDmode;
1828 mode != MAX_MACHINE_MODE; mode = (enum machine_mode) ((int) mode + 1))
1829 {
1830 register int size = GET_MODE_SIZE (mode);
1831
1832 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
1833 {
1834 register int ok;
1835
1836 if (mode == CCmode)
1837 {
1838 ok = (ST_REG_P (regno) || GP_REG_P (regno));
1839 }
1840 else if (GP_REG_P (regno))
1841 ok = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
1842 else
1843 ok = 0;
1844
1845 microblaze_hard_regno_mode_ok[(int) mode][regno] = ok;
1846 }
1847 }
1848}
1849
1850/* Return true if FUNC is an interrupt function as specified
1851 by the "interrupt_handler" attribute. */
1852
1853static int
1854microblaze_interrupt_function_p (tree func)
1855{
1856 tree a;
1857
1858 if (TREE_CODE (func) != FUNCTION_DECL)
1859 return 0;
1860
1861 a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
1862 return a != NULL_TREE;
1863}
1864
d9c1b601 1865static int
1866microblaze_fast_interrupt_function_p (tree func)
1867{
1868 tree a;
1869
1870 if (TREE_CODE (func) != FUNCTION_DECL)
1871 return 0;
1872
1873 a = lookup_attribute ("fast_interrupt", DECL_ATTRIBUTES (func));
1874 return a != NULL_TREE;
1875}
0f718e59 1876int
1877microblaze_break_function_p (tree func)
1878{
1879 tree a;
1880 if (!func)
1881 return 0;
1882 if (TREE_CODE (func) != FUNCTION_DECL)
1883 return 0;
d9c1b601 1884
0f718e59 1885 a = lookup_attribute ("break_handler", DECL_ATTRIBUTES (func));
1886 return a != NULL_TREE;
1887}
d34b0d1e 1888/* Return true if FUNC is an interrupt function which uses
1889 normal return, indicated by the "save_volatiles" attribute. */
1890
1891static int
1892microblaze_save_volatiles (tree func)
1893{
1894 tree a;
1895
1896 if (TREE_CODE (func) != FUNCTION_DECL)
1897 return 0;
1898
1899 a = lookup_attribute ("save_volatiles", DECL_ATTRIBUTES (func));
1900 return a != NULL_TREE;
1901}
1902
1903/* Return whether function is tagged with 'interrupt_handler'
d9c1b601 1904 or 'fast_interrupt' attribute. Return true if function
1905 should use return from interrupt rather than normal
1906 function return. */
d34b0d1e 1907int
d9c1b601 1908microblaze_is_interrupt_variant (void)
d34b0d1e 1909{
d9c1b601 1910 return (interrupt_handler || fast_interrupt);
d34b0d1e 1911}
0f718e59 1912int
1913microblaze_is_break_handler (void)
1914{
1915 return break_handler;
1916}
d34b0d1e 1917
1918/* Determine of register must be saved/restored in call. */
1919static int
1920microblaze_must_save_register (int regno)
1921{
1922 if (pic_offset_table_rtx &&
1923 (regno == MB_ABI_PIC_ADDR_REGNUM) && df_regs_ever_live_p (regno))
1924 return 1;
1925
1926 if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
1927 return 1;
1928
1929 if (frame_pointer_needed && (regno == HARD_FRAME_POINTER_REGNUM))
1930 return 1;
1931
d5bf7b64 1932 if (!crtl->is_leaf)
d34b0d1e 1933 {
1934 if (regno == MB_ABI_SUB_RETURN_ADDR_REGNUM)
1935 return 1;
d9c1b601 1936 if ((microblaze_is_interrupt_variant () || save_volatiles) &&
d34b0d1e 1937 (regno >= 3 && regno <= 12))
1938 return 1;
1939 }
1940
d9c1b601 1941 if (microblaze_is_interrupt_variant ())
d34b0d1e 1942 {
1943 if (df_regs_ever_live_p (regno)
1944 || regno == MB_ABI_MSR_SAVE_REG
d9c1b601 1945 || (interrupt_handler
1946 && (regno == MB_ABI_ASM_TEMP_REGNUM
1947 || regno == MB_ABI_EXCEPTION_RETURN_ADDR_REGNUM)))
d34b0d1e 1948 return 1;
1949 }
1950
1951 if (save_volatiles)
1952 {
1953 if (df_regs_ever_live_p (regno)
1954 || regno == MB_ABI_ASM_TEMP_REGNUM
1955 || regno == MB_ABI_EXCEPTION_RETURN_ADDR_REGNUM)
1956 return 1;
1957 }
1958
1959 return 0;
1960}
1961
1962/* Return the bytes needed to compute the frame pointer from the current
1963 stack pointer.
1964
1965 MicroBlaze stack frames look like:
1966
1967
1968
1969 Before call After call
1970 +-----------------------+ +-----------------------+
1971 high | | | |
1972 mem. | local variables, | | local variables, |
1973 | callee saved and | | callee saved and |
1974 | temps | | temps |
1975 +-----------------------+ +-----------------------+
1976 | arguments for called | | arguments for called |
1977 | subroutines | | subroutines |
1978 | (optional) | | (optional) |
1979 +-----------------------+ +-----------------------+
1980 | Link register | | Link register |
1981 SP->| | | |
1982 +-----------------------+ +-----------------------+
1983 | |
1984 | local variables, |
1985 | callee saved and |
1986 | temps |
1987 +-----------------------+
1988 | MSR (optional if, |
1989 | interrupt handler) |
1990 +-----------------------+
1991 | |
1992 | alloca allocations |
1993 | |
1994 +-----------------------+
1995 | |
1996 | arguments for called |
1997 | subroutines |
1998 | (optional) |
1999 | |
2000 +-----------------------+
2001 | Link register |
2002 low FP,SP->| |
2003 memory +-----------------------+
2004
2005*/
2006
2007static HOST_WIDE_INT
2008compute_frame_size (HOST_WIDE_INT size)
2009{
2010 int regno;
2011 HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up. */
2012 HOST_WIDE_INT var_size; /* # bytes that local variables take up. */
2013 HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up. */
2014 int link_debug_size; /* # bytes for link register. */
2015 HOST_WIDE_INT gp_reg_size; /* # bytes needed to store calle-saved gp regs. */
2016 long mask; /* mask of saved gp registers. */
2017
2018 interrupt_handler =
2019 microblaze_interrupt_function_p (current_function_decl);
0f718e59 2020 break_handler =
2021 microblaze_break_function_p (current_function_decl);
2022
d9c1b601 2023 fast_interrupt =
2024 microblaze_fast_interrupt_function_p (current_function_decl);
d34b0d1e 2025 save_volatiles = microblaze_save_volatiles (current_function_decl);
0f718e59 2026 if (break_handler)
2027 interrupt_handler = break_handler;
d34b0d1e 2028
2029 gp_reg_size = 0;
2030 mask = 0;
2031 var_size = size;
2032 args_size = crtl->outgoing_args_size;
2033
2034 if ((args_size == 0) && cfun->calls_alloca)
2035 args_size = NUM_OF_ARGS * UNITS_PER_WORD;
2036
2037 total_size = var_size + args_size;
2038
2039 if (flag_pic == 2)
2040 /* force setting GOT. */
2041 df_set_regs_ever_live (MB_ABI_PIC_ADDR_REGNUM, true);
2042
2043 /* Calculate space needed for gp registers. */
2044 for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
2045 {
2046 if (microblaze_must_save_register (regno))
2047 {
2048
2049 if (regno != MB_ABI_SUB_RETURN_ADDR_REGNUM)
2050 /* Don't account for link register. It is accounted specially below. */
2051 gp_reg_size += GET_MODE_SIZE (SImode);
2052
2053 mask |= (1L << (regno - GP_REG_FIRST));
2054 }
2055 }
2056
2057 total_size += gp_reg_size;
2058
2059 /* Add 4 bytes for MSR. */
d9c1b601 2060 if (microblaze_is_interrupt_variant ())
d34b0d1e 2061 total_size += 4;
2062
2063 /* No space to be allocated for link register in leaf functions with no other
2064 stack requirements. */
d5bf7b64 2065 if (total_size == 0 && crtl->is_leaf)
d34b0d1e 2066 link_debug_size = 0;
2067 else
2068 link_debug_size = UNITS_PER_WORD;
2069
2070 total_size += link_debug_size;
2071
2072 /* Save other computed information. */
2073 current_frame_info.total_size = total_size;
2074 current_frame_info.var_size = var_size;
2075 current_frame_info.args_size = args_size;
2076 current_frame_info.gp_reg_size = gp_reg_size;
2077 current_frame_info.mask = mask;
2078 current_frame_info.initialized = reload_completed;
2079 current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
2080 current_frame_info.link_debug_size = link_debug_size;
2081
2082 if (mask)
2083 /* Offset from which to callee-save GP regs. */
2084 current_frame_info.gp_offset = (total_size - gp_reg_size);
2085 else
2086 current_frame_info.gp_offset = 0;
2087
2088 /* Ok, we're done. */
2089 return total_size;
2090}
2091
2092/* Make sure that we're not trying to eliminate to the wrong hard frame
2093 pointer. */
2094
2095static bool
2096microblaze_can_eliminate (const int from, const int to)
2097{
2098 return ((from == RETURN_ADDRESS_POINTER_REGNUM && !leaf_function_p())
2099 || (to == MB_ABI_SUB_RETURN_ADDR_REGNUM && leaf_function_p())
2100 || (from != RETURN_ADDRESS_POINTER_REGNUM
2101 && (to == HARD_FRAME_POINTER_REGNUM
2102 || (to == STACK_POINTER_REGNUM && !frame_pointer_needed))));
2103}
2104
2105/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame
2106 pointer or argument pointer or the return address pointer. TO is either
2107 the stack pointer or hard frame pointer. */
2108
2109HOST_WIDE_INT
2110microblaze_initial_elimination_offset (int from, int to)
2111{
2112 HOST_WIDE_INT offset;
2113
2114 switch (from)
2115 {
2116 case FRAME_POINTER_REGNUM:
2117 offset = 0;
2118 break;
2119 case ARG_POINTER_REGNUM:
2120 if (to == STACK_POINTER_REGNUM || to == HARD_FRAME_POINTER_REGNUM)
2121 offset = compute_frame_size (get_frame_size ());
2122 else
2123 gcc_unreachable ();
2124 break;
2125 case RETURN_ADDRESS_POINTER_REGNUM:
d5bf7b64 2126 if (crtl->is_leaf)
d34b0d1e 2127 offset = 0;
2128 else
2129 offset = current_frame_info.gp_offset +
2130 ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT)));
2131 break;
2132 default:
2133 gcc_unreachable ();
2134 }
2135 return offset;
2136}
2137
2138/* Print operands using format code.
2139
2140 The MicroBlaze specific codes are:
2141
2142 'X' X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
2143 'x' X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
2144 'F' op is CONST_DOUBLE, print 32 bits in hex,
2145 'd' output integer constant in decimal,
2146 'z' if the operand is 0, use $0 instead of normal operand.
2147 'D' print second register of double-word register operand.
2148 'L' print low-order register of double-word register operand.
2149 'M' print high-order register of double-word register operand.
2150 'C' print part of opcode for a branch condition.
2151 'N' print part of opcode for a branch condition, inverted.
2152 'S' X is CODE_LABEL, print with prefix of "LS" (for embedded switch).
2153 'B' print 'z' for EQ, 'n' for NE
2154 'b' print 'n' for EQ, 'z' for NE
2155 'T' print 'f' for EQ, 't' for NE
2156 't' print 't' for EQ, 'f' for NE
2157 'm' Print 1<<operand.
2158 'i' Print 'i' if MEM operand has immediate value
c4fcbf4c 2159 'y' Print 'y' if MEM operand is single register
d34b0d1e 2160 'o' Print operand address+4
2161 '?' Print 'd' if we use a branch with delay slot instead of normal branch.
2162 'h' Print high word of const_double (int or float) value as hex
2163 'j' Print low word of const_double (int or float) value as hex
2164 's' Print -1 if operand is negative, 0 if positive (sign extend)
2165 '@' Print the name of the temporary register (rMB_ABI_ASM_TEMP_REGNUM).
2166 '#' Print nop if the delay slot of a branch is not filled.
2167*/
2168
2169void
2170print_operand (FILE * file, rtx op, int letter)
2171{
2172 register enum rtx_code code;
2173
2174 if (PRINT_OPERAND_PUNCT_VALID_P (letter))
2175 {
2176 switch (letter)
2177 {
2178 case '?':
2179 /* Conditionally add a 'd' to indicate filled delay slot. */
2180 if (final_sequence != NULL)
2181 fputs ("d", file);
2182 break;
2183
2184 case '#':
2185 /* Conditionally add a nop in unfilled delay slot. */
2186 if (final_sequence == NULL)
2187 fputs ("nop\t\t# Unfilled delay slot\n", file);
2188 break;
2189
2190 case '@':
2191 fputs (reg_names[GP_REG_FIRST + MB_ABI_ASM_TEMP_REGNUM], file);
2192 break;
2193
2194 default:
2195 output_operand_lossage ("unknown punctuation '%c'", letter);
2196 break;
2197 }
2198
2199 return;
2200 }
2201
2202 if (!op)
2203 {
2204 output_operand_lossage ("null pointer");
2205 return;
2206 }
2207
2208 code = GET_CODE (op);
2209
2210 if (code == SIGN_EXTEND)
2211 op = XEXP (op, 0), code = GET_CODE (op);
2212
2213 if (letter == 'C')
2214 switch (code)
2215 {
2216 case EQ:
2217 fputs ("eq", file);
2218 break;
2219 case NE:
2220 fputs ("ne", file);
2221 break;
2222 case GT:
2223 case GTU:
2224 fputs ("gt", file);
2225 break;
2226 case GE:
2227 case GEU:
2228 fputs ("ge", file);
2229 break;
2230 case LT:
2231 case LTU:
2232 fputs ("lt", file);
2233 break;
2234 case LE:
2235 case LEU:
2236 fputs ("le", file);
2237 break;
2238 default:
2239 fatal_insn ("PRINT_OPERAND, invalid insn for %%C", op);
2240 }
2241
2242 else if (letter == 'N')
2243 switch (code)
2244 {
2245 case EQ:
2246 fputs ("ne", file);
2247 break;
2248 case NE:
2249 fputs ("eq", file);
2250 break;
2251 case GT:
2252 case GTU:
2253 fputs ("le", file);
2254 break;
2255 case GE:
2256 case GEU:
2257 fputs ("lt", file);
2258 break;
2259 case LT:
2260 case LTU:
2261 fputs ("ge", file);
2262 break;
2263 case LE:
2264 case LEU:
2265 fputs ("gt", file);
2266 break;
2267 default:
2268 fatal_insn ("PRINT_OPERAND, invalid insn for %%N", op);
2269 }
2270
2271 else if (letter == 'S')
2272 {
2273 char buffer[100];
2274
2275 ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
2276 assemble_name (file, buffer);
2277 }
2278
2279 /* Print 'i' for memory operands which have immediate values. */
2280 else if (letter == 'i')
2281 {
2282 if (code == MEM)
2283 {
2284 struct microblaze_address_info info;
2285
2286 if (!microblaze_classify_address
2287 (&info, XEXP (op, 0), GET_MODE (op), 1))
2288 fatal_insn ("insn contains an invalid address !", op);
2289
2290 switch (info.type)
2291 {
2292 case ADDRESS_REG:
2293 case ADDRESS_CONST_INT:
2294 case ADDRESS_SYMBOLIC:
2295 case ADDRESS_GOTOFF:
4eae25aa 2296 case ADDRESS_TLS:
d34b0d1e 2297 fputs ("i", file);
2298 break;
2299 case ADDRESS_REG_INDEX:
2300 break;
2301 case ADDRESS_INVALID:
2302 case ADDRESS_PLT:
bf776685 2303 fatal_insn ("invalid address", op);
d34b0d1e 2304 }
2305 }
2306 }
2307
2308 else if (code == REG || code == SUBREG)
2309 {
2310 register int regnum;
2311
2312 if (code == REG)
2313 regnum = REGNO (op);
2314 else
2315 regnum = true_regnum (op);
2316
2317 if ((letter == 'M' && !WORDS_BIG_ENDIAN)
2318 || (letter == 'L' && WORDS_BIG_ENDIAN) || letter == 'D')
2319 regnum++;
2320
2321 fprintf (file, "%s", reg_names[regnum]);
2322 }
2323
2324 else if (code == MEM)
2325 if (letter == 'o')
2326 {
2327 rtx op4 = adjust_address (op, GET_MODE (op), 4);
2328 output_address (XEXP (op4, 0));
2329 }
c4fcbf4c 2330 else if (letter == 'y')
2331 {
2332 rtx mem_reg = XEXP (op, 0);
2333 if (GET_CODE (mem_reg) == REG)
2334 {
2335 register int regnum = REGNO (mem_reg);
2336 fprintf (file, "%s", reg_names[regnum]);
2337 }
2338 }
d34b0d1e 2339 else
2340 output_address (XEXP (op, 0));
2341
2342 else if (letter == 'h' || letter == 'j')
2343 {
2344 long val[2];
2345 if (code == CONST_DOUBLE)
2346 {
2347 if (GET_MODE (op) == DFmode)
2348 {
2349 REAL_VALUE_TYPE value;
2350 REAL_VALUE_FROM_CONST_DOUBLE (value, op);
2351 REAL_VALUE_TO_TARGET_DOUBLE (value, val);
2352 }
2353 else
2354 {
2355 val[0] = CONST_DOUBLE_HIGH (op);
2356 val[1] = CONST_DOUBLE_LOW (op);
2357 }
2358 }
2359 else if (code == CONST_INT)
2360 {
2361 val[0] = (INTVAL (op) & 0xffffffff00000000LL) >> 32;
2362 val[1] = INTVAL (op) & 0x00000000ffffffffLL;
2363 if (val[0] == 0 && val[1] < 0)
2364 val[0] = -1;
2365
2366 }
2367 fprintf (file, "0x%8.8lx", (letter == 'h') ? val[0] : val[1]);
2368 }
2369 else if (code == CONST_DOUBLE)
2370 {
2371 if (letter == 'F')
2372 {
2373 unsigned long value_long;
2374 REAL_VALUE_TYPE value;
2375 REAL_VALUE_FROM_CONST_DOUBLE (value, op);
2376 REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
2377 fprintf (file, HOST_WIDE_INT_PRINT_HEX, value_long);
2378 }
2379 else
2380 {
2381 char s[60];
2382 real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
2383 fputs (s, file);
2384 }
2385 }
2386
2387 else if (code == UNSPEC)
2388 {
2389 print_operand_address (file, op);
2390 }
2391
2392 else if (letter == 'x' && GET_CODE (op) == CONST_INT)
2393 fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL (op));
2394
2395 else if (letter == 'X' && GET_CODE (op) == CONST_INT)
2396 fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
2397
2398 else if (letter == 'd' && GET_CODE (op) == CONST_INT)
2399 fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL (op)));
2400
2401 else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
2402 fputs (reg_names[GP_REG_FIRST], file);
2403
2404 else if (letter == 's' && GET_CODE (op) == CONST_INT)
2405 if (INTVAL (op) < 0)
2406 fputs ("-1", file);
2407 else
2408 fputs ("0", file);
2409
2410 else if (letter == 'd' || letter == 'x' || letter == 'X' || letter == 's')
2411 output_operand_lossage ("letter %c was found & insn was not CONST_INT", letter);
2412
2413 else if (letter == 'B')
2414 fputs (code == EQ ? "z" : "n", file);
2415 else if (letter == 'b')
2416 fputs (code == EQ ? "n" : "z", file);
2417 else if (letter == 'T')
2418 fputs (code == EQ ? "f" : "t", file);
2419 else if (letter == 't')
2420 fputs (code == EQ ? "t" : "f", file);
2421
4eae25aa 2422 else if (code == CONST
2423 && ((GET_CODE (XEXP (op, 0)) == REG)
2424 || (GET_CODE (XEXP (op, 0)) == UNSPEC)))
d34b0d1e 2425 {
2426 print_operand (file, XEXP (op, 0), letter);
2427 }
4eae25aa 2428 else if (code == CONST
2429 && (GET_CODE (XEXP (op, 0)) == PLUS)
2430 && (GET_CODE (XEXP (XEXP (op, 0), 0)) == REG)
2431 && (GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST))
2432 {
2433 print_operand_address (file, XEXP (op, 0));
2434 }
d34b0d1e 2435 else if (letter == 'm')
2436 fprintf (file, HOST_WIDE_INT_PRINT_DEC, (1L << INTVAL (op)));
2437 else
2438 output_addr_const (file, op);
2439}
2440
2441/* A C compound statement to output to stdio stream STREAM the
2442 assembler syntax for an instruction operand that is a memory
2443 reference whose address is ADDR. ADDR is an RTL expression.
2444
2445 Possible address classifications and output formats are,
2446
2447 ADDRESS_REG "%0, r0"
2448
2449 ADDRESS_REG with non-zero "%0, <addr_const>"
2450 offset
2451
2452 ADDRESS_REG_INDEX "rA, RB"
2453 (if rA is r0, rA and rB are swapped)
2454
2455 ADDRESS_CONST_INT "r0, <addr_const>"
2456
2457 ADDRESS_SYMBOLIC "rBase, <addr_const>"
2458 (rBase is a base register suitable for the
2459 symbol's type)
2460*/
2461
2462void
2463print_operand_address (FILE * file, rtx addr)
2464{
2465 struct microblaze_address_info info;
2466 enum microblaze_address_type type;
2467 if (!microblaze_classify_address (&info, addr, GET_MODE (addr), 1))
2468 fatal_insn ("insn contains an invalid address !", addr);
2469
2470 type = info.type;
2471 switch (info.type)
2472 {
2473 case ADDRESS_REG:
2474 fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
2475 output_addr_const (file, info.offset);
2476 break;
2477 case ADDRESS_REG_INDEX:
2478 if (REGNO (info.regA) == 0)
2479 /* Make rB == r0 instead of rA == r0. This helps reduce read port
2480 congestion. */
2481 fprintf (file, "%s,%s", reg_names[REGNO (info.regB)],
2482 reg_names[REGNO (info.regA)]);
2483 else if (REGNO (info.regB) != 0)
2484 /* This is a silly swap to help Dhrystone. */
2485 fprintf (file, "%s,%s", reg_names[REGNO (info.regB)],
2486 reg_names[REGNO (info.regA)]);
2487 break;
2488 case ADDRESS_CONST_INT:
2489 fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
2490 output_addr_const (file, info.offset);
2491 break;
2492 case ADDRESS_SYMBOLIC:
2493 case ADDRESS_GOTOFF:
2494 case ADDRESS_PLT:
4eae25aa 2495 case ADDRESS_TLS:
d34b0d1e 2496 if (info.regA)
2497 fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
2498 output_addr_const (file, info.symbol);
2499 if (type == ADDRESS_GOTOFF)
2500 {
2501 fputs ("@GOT", file);
2502 }
2503 else if (type == ADDRESS_PLT)
2504 {
2505 fputs ("@PLT", file);
2506 }
4eae25aa 2507 else if (type == ADDRESS_TLS)
2508 {
2509 switch (info.tls_type)
2510 {
2511 case TLS_GD:
2512 fputs ("@TLSGD", file);
2513 break;
2514 case TLS_LDM:
2515 fputs ("@TLSLDM", file);
2516 break;
2517 case TLS_DTPREL:
2518 fputs ("@TLSDTPREL", file);
2519 break;
2520 default :
2521 abort();
2522 break;
2523 }
2524 }
d34b0d1e 2525 break;
2526 case ADDRESS_INVALID:
2527 fatal_insn ("invalid address", addr);
2528 break;
2529 }
2530}
2531
2532/* Emit either a label, .comm, or .lcomm directive, and mark that the symbol
2533 is used, so that we don't emit an .extern for it in
2534 microblaze_asm_file_end. */
2535
2536void
2537microblaze_declare_object (FILE * stream, const char *name,
2538 const char *section, const char *fmt, int size)
2539{
2540
2541 fputs (section, stream);
2542 assemble_name (stream, name);
2543 fprintf (stream, fmt, size);
2544}
2545
2546/* Common code to emit the insns (or to write the instructions to a file)
2547 to save/restore registers.
2548
2549 Other parts of the code assume that MICROBLAZE_TEMP1_REGNUM (aka large_reg)
2550 is not modified within save_restore_insns. */
2551
2552#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
2553
2554/* Save or restore instructions based on whether this is the prologue or
2555 epilogue. prologue is 1 for the prologue. */
2556static void
2557save_restore_insns (int prologue)
2558{
2559 rtx base_reg_rtx, reg_rtx, mem_rtx, /* msr_rtx, */ isr_reg_rtx =
2560 0, isr_mem_rtx = 0;
2561 rtx isr_msr_rtx = 0, insn;
2562 long mask = current_frame_info.mask;
037176b6 2563 HOST_WIDE_INT gp_offset;
d34b0d1e 2564 int regno;
2565
2566 if (frame_pointer_needed
2567 && !BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
2568 gcc_unreachable ();
2569
2570 if (mask == 0)
2571 return;
2572
2573 /* Save registers starting from high to low. The debuggers prefer at least
2574 the return register be stored at func+4, and also it allows us not to
2575 need a nop in the epilog if at least one register is reloaded in
2576 addition to return address. */
2577
2578 /* Pick which pointer to use as a base register. For small frames, just
2579 use the stack pointer. Otherwise, use a temporary register. Save 2
2580 cycles if the save area is near the end of a large frame, by reusing
2581 the constant created in the prologue/epilogue to adjust the stack
2582 frame. */
2583
2584 gp_offset = current_frame_info.gp_offset;
2585
2586 gcc_assert (gp_offset > 0);
2587
2588 base_reg_rtx = stack_pointer_rtx;
d34b0d1e 2589
2590 /* For interrupt_handlers, need to save/restore the MSR. */
d9c1b601 2591 if (microblaze_is_interrupt_variant ())
d34b0d1e 2592 {
2593 isr_mem_rtx = gen_rtx_MEM (SImode,
2594 gen_rtx_PLUS (Pmode, base_reg_rtx,
2595 GEN_INT (current_frame_info.
2596 gp_offset -
2597 UNITS_PER_WORD)));
2598
2599 /* Do not optimize in flow analysis. */
2600 MEM_VOLATILE_P (isr_mem_rtx) = 1;
2601 isr_reg_rtx = gen_rtx_REG (SImode, MB_ABI_MSR_SAVE_REG);
2602 isr_msr_rtx = gen_rtx_REG (SImode, ST_REG);
2603 }
2604
d9c1b601 2605 if (microblaze_is_interrupt_variant () && !prologue)
d34b0d1e 2606 {
2607 emit_move_insn (isr_reg_rtx, isr_mem_rtx);
2608 emit_move_insn (isr_msr_rtx, isr_reg_rtx);
2609 /* Do not optimize in flow analysis. */
2610 emit_insn (gen_rtx_USE (SImode, isr_reg_rtx));
2611 emit_insn (gen_rtx_USE (SImode, isr_msr_rtx));
2612 }
2613
2614 for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
2615 {
2616 if (BITSET_P (mask, regno - GP_REG_FIRST))
2617 {
2618 if (regno == MB_ABI_SUB_RETURN_ADDR_REGNUM)
2619 /* Don't handle here. Already handled as the first register. */
2620 continue;
2621
2622 reg_rtx = gen_rtx_REG (SImode, regno);
2623 insn = gen_rtx_PLUS (Pmode, base_reg_rtx, GEN_INT (gp_offset));
2624 mem_rtx = gen_rtx_MEM (SImode, insn);
d9c1b601 2625 if (microblaze_is_interrupt_variant () || save_volatiles)
d34b0d1e 2626 /* Do not optimize in flow analysis. */
2627 MEM_VOLATILE_P (mem_rtx) = 1;
2628
2629 if (prologue)
2630 {
2631 insn = emit_move_insn (mem_rtx, reg_rtx);
2632 RTX_FRAME_RELATED_P (insn) = 1;
2633 }
2634 else
2635 {
2636 insn = emit_move_insn (reg_rtx, mem_rtx);
2637 }
2638
2639 gp_offset += GET_MODE_SIZE (SImode);
2640 }
2641 }
2642
d9c1b601 2643 if (microblaze_is_interrupt_variant () && prologue)
d34b0d1e 2644 {
2645 emit_move_insn (isr_reg_rtx, isr_msr_rtx);
2646 emit_move_insn (isr_mem_rtx, isr_reg_rtx);
2647
2648 /* Do not optimize in flow analysis. */
2649 emit_insn (gen_rtx_USE (SImode, isr_reg_rtx));
2650 emit_insn (gen_rtx_USE (SImode, isr_msr_rtx));
2651 }
2652
2653 /* Done saving and restoring */
2654}
2655
2656
2657/* Set up the stack and frame (if desired) for the function. */
2658static void
2659microblaze_function_prologue (FILE * file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
2660{
2661 const char *fnname;
2662 long fsiz = current_frame_info.total_size;
2663
2664 /* Get the function name the same way that toplev.c does before calling
2665 assemble_start_function. This is needed so that the name used here
2666 exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
2667 fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
2668 if (!flag_inhibit_size_directive)
2669 {
2670 fputs ("\t.ent\t", file);
2671 if (interrupt_handler && strcmp (INTERRUPT_HANDLER_NAME, fnname))
0f718e59 2672 fputs ("_interrupt_handler", file);
2673 else if (break_handler && strcmp (BREAK_HANDLER_NAME, fnname))
2674 fputs ("_break_handler", file);
d9c1b601 2675 else if (fast_interrupt && strcmp (FAST_INTERRUPT_NAME, fnname))
0f718e59 2676 fputs ("_fast_interrupt", file);
d34b0d1e 2677 else
2678 assemble_name (file, fnname);
2679 fputs ("\n", file);
d9c1b601 2680 if (!microblaze_is_interrupt_variant ())
d34b0d1e 2681 ASM_OUTPUT_TYPE_DIRECTIVE (file, fnname, "function");
2682 }
2683
2684 assemble_name (file, fnname);
2685 fputs (":\n", file);
2686
2687 if (interrupt_handler && strcmp (INTERRUPT_HANDLER_NAME, fnname))
2688 fputs ("_interrupt_handler:\n", file);
0f718e59 2689 if (break_handler && strcmp (BREAK_HANDLER_NAME, fnname))
2690 fputs ("_break_handler:\n", file);
d34b0d1e 2691 if (!flag_inhibit_size_directive)
2692 {
2693 /* .frame FRAMEREG, FRAMESIZE, RETREG. */
2694 fprintf (file,
2695 "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d, args= %d\n",
2696 (reg_names[(frame_pointer_needed)
2697 ? HARD_FRAME_POINTER_REGNUM :
2698 STACK_POINTER_REGNUM]), fsiz,
2699 reg_names[MB_ABI_SUB_RETURN_ADDR_REGNUM + GP_REG_FIRST],
2700 current_frame_info.var_size, current_frame_info.num_gp,
2701 crtl->outgoing_args_size);
2702 fprintf (file, "\t.mask\t0x%08lx\n", current_frame_info.mask);
2703 }
2704}
2705
2706/* Output extra assembler code at the end of a prologue. */
2707static void
2708microblaze_function_end_prologue (FILE * file)
2709{
2710 if (TARGET_STACK_CHECK)
2711 {
2712 fprintf (file, "\t# Stack Check Stub -- Start.\n\t");
2713 fprintf (file, "ori\tr18,r0,_stack_end\n\t");
2714 fprintf (file, "cmpu\tr18,r1,r18\n\t");
2715 fprintf (file, "bgei\tr18,_stack_overflow_exit\n\t");
2716 fprintf (file, "# Stack Check Stub -- End.\n");
2717 }
2718}
2719
9dcfd437 2720static void
2721microblaze_elf_asm_cdtor (rtx symbol, int priority, bool is_ctor)
2722{
2723 section *s;
2724
2725 if (priority != DEFAULT_INIT_PRIORITY)
2726 {
2727 char buf[18];
2728 sprintf (buf, "%s.%.5u",
2729 is_ctor ? ".ctors" : ".dtors",
2730 MAX_INIT_PRIORITY - priority);
2731 s = get_section (buf, SECTION_WRITE, NULL_TREE);
2732 }
2733 else if (is_ctor)
2734 s = ctors_section;
2735 else
2736 s = dtors_section;
2737
2738 switch_to_section (s);
2739 assemble_align (POINTER_SIZE);
2740 fputs ("\t.word\t", asm_out_file);
2741 output_addr_const (asm_out_file, symbol);
2742 fputs ("\n", asm_out_file);
2743}
2744
2745/* Add a function to the list of static constructors. */
2746
2747static void
2748microblaze_elf_asm_constructor (rtx symbol, int priority)
2749{
2750 microblaze_elf_asm_cdtor (symbol, priority, /*is_ctor=*/true);
2751}
2752
2753/* Add a function to the list of static destructors. */
2754
2755static void
2756microblaze_elf_asm_destructor (rtx symbol, int priority)
2757{
2758 microblaze_elf_asm_cdtor (symbol, priority, /*is_ctor=*/false);
2759}
2760
d34b0d1e 2761/* Expand the prologue into a bunch of separate insns. */
2762
2763void
2764microblaze_expand_prologue (void)
2765{
2766 int regno;
2767 HOST_WIDE_INT fsiz;
2768 const char *arg_name = 0;
2769 tree fndecl = current_function_decl;
2770 tree fntype = TREE_TYPE (fndecl);
2771 tree fnargs = DECL_ARGUMENTS (fndecl);
2772 rtx next_arg_reg;
2773 int i;
2774 tree next_arg;
2775 tree cur_arg;
39cba157 2776 CUMULATIVE_ARGS args_so_far_v;
2777 cumulative_args_t args_so_far;
d34b0d1e 2778 rtx mem_rtx, reg_rtx;
2779
2780 /* If struct value address is treated as the first argument, make it so. */
2781 if (aggregate_value_p (DECL_RESULT (fndecl), fntype)
2782 && !cfun->returns_pcc_struct)
2783 {
2784 tree type = build_pointer_type (fntype);
2785 tree function_result_decl = build_decl (BUILTINS_LOCATION, PARM_DECL,
2786 NULL_TREE, type);
2787
2788 DECL_ARG_TYPE (function_result_decl) = type;
2789 TREE_CHAIN (function_result_decl) = fnargs;
2790 fnargs = function_result_decl;
2791 }
2792
2793 /* Determine the last argument, and get its name. */
2794
39cba157 2795 INIT_CUMULATIVE_ARGS (args_so_far_v, fntype, NULL_RTX, 0, 0);
2796 args_so_far = pack_cumulative_args (&args_so_far_v);
d34b0d1e 2797 regno = GP_ARG_FIRST;
2798
2799 for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
2800 {
2801 tree passed_type = DECL_ARG_TYPE (cur_arg);
2802 enum machine_mode passed_mode = TYPE_MODE (passed_type);
2803 rtx entry_parm;
2804
2805 if (TREE_ADDRESSABLE (passed_type))
2806 {
2807 passed_type = build_pointer_type (passed_type);
2808 passed_mode = Pmode;
2809 }
2810
39cba157 2811 entry_parm = targetm.calls.function_arg (args_so_far, passed_mode,
9a51523d 2812 passed_type, true);
d34b0d1e 2813
2814 if (entry_parm)
2815 {
2816 int words;
2817
2818 /* passed in a register, so will get homed automatically. */
2819 if (GET_MODE (entry_parm) == BLKmode)
2820 words = (int_size_in_bytes (passed_type) + 3) / 4;
2821 else
2822 words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
2823
2824 regno = REGNO (entry_parm) + words - 1;
2825 }
2826 else
2827 {
2828 regno = GP_ARG_LAST + 1;
2829 break;
2830 }
2831
39cba157 2832 targetm.calls.function_arg_advance (args_so_far, passed_mode,
9a51523d 2833 passed_type, true);
d34b0d1e 2834
2835 next_arg = TREE_CHAIN (cur_arg);
2836 if (next_arg == 0)
2837 {
2838 if (DECL_NAME (cur_arg))
2839 arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg));
2840
2841 break;
2842 }
2843 }
2844
2845 /* Split parallel insn into a sequence of insns. */
2846
39cba157 2847 next_arg_reg = targetm.calls.function_arg (args_so_far, VOIDmode,
9a51523d 2848 void_type_node, true);
d34b0d1e 2849 if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
2850 {
2851 rtvec adjust = XVEC (next_arg_reg, 0);
2852 int num = GET_NUM_ELEM (adjust);
2853
2854 for (i = 0; i < num; i++)
2855 {
2856 rtx pattern = RTVEC_ELT (adjust, i);
2857 emit_insn (pattern);
2858 }
2859 }
2860
2861 fsiz = compute_frame_size (get_frame_size ());
2862
a86bc377 2863 if (flag_stack_usage_info)
db58342c 2864 current_function_static_stack_size = fsiz;
2865
0f718e59 2866
d34b0d1e 2867 /* If this function is a varargs function, store any registers that
2868 would normally hold arguments ($5 - $10) on the stack. */
2869 if (((TYPE_ARG_TYPES (fntype) != 0
2870 && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
2871 != void_type_node))
2872 || (arg_name != 0
2873 && ((arg_name[0] == '_'
2874 && strcmp (arg_name, "__builtin_va_alist") == 0)
2875 || (arg_name[0] == 'v'
2876 && strcmp (arg_name, "va_alist") == 0)))))
2877 {
2878 int offset = (regno - GP_ARG_FIRST + 1) * UNITS_PER_WORD;
2879 rtx ptr = stack_pointer_rtx;
2880
2881 /* If we are doing svr4-abi, sp has already been decremented by fsiz. */
2882 for (; regno <= GP_ARG_LAST; regno++)
2883 {
2884 if (offset != 0)
2885 ptr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
2886 emit_move_insn (gen_rtx_MEM (SImode, ptr),
2887 gen_rtx_REG (SImode, regno));
2888
2889 offset += GET_MODE_SIZE (SImode);
2890 }
2891
2892 }
2893
2894 if (fsiz > 0)
2895 {
2896 rtx fsiz_rtx = GEN_INT (fsiz);
2897
2898 rtx insn = NULL;
2899 insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
2900 fsiz_rtx));
2901 if (insn)
2902 RTX_FRAME_RELATED_P (insn) = 1;
2903
2904 /* Handle SUB_RETURN_ADDR_REGNUM specially at first. */
d5bf7b64 2905 if (!crtl->is_leaf || interrupt_handler)
d34b0d1e 2906 {
2907 mem_rtx = gen_rtx_MEM (SImode,
2908 gen_rtx_PLUS (Pmode, stack_pointer_rtx,
2909 const0_rtx));
2910
2911 if (interrupt_handler)
2912 /* Do not optimize in flow analysis. */
2913 MEM_VOLATILE_P (mem_rtx) = 1;
2914
2915 reg_rtx = gen_rtx_REG (SImode, MB_ABI_SUB_RETURN_ADDR_REGNUM);
2916 insn = emit_move_insn (mem_rtx, reg_rtx);
2917 RTX_FRAME_RELATED_P (insn) = 1;
2918 }
2919
2920 /* _save_ registers for prologue. */
2921 save_restore_insns (1);
2922
2923 if (frame_pointer_needed)
2924 {
2925 rtx insn = 0;
2926
2927 insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
2928 stack_pointer_rtx));
2929
2930 if (insn)
2931 RTX_FRAME_RELATED_P (insn) = 1;
2932 }
2933 }
2934
4eae25aa 2935 if ((flag_pic == 2 || TLS_NEEDS_GOT )
2936 && df_regs_ever_live_p (MB_ABI_PIC_ADDR_REGNUM))
d34b0d1e 2937 {
d34b0d1e 2938 SET_REGNO (pic_offset_table_rtx, MB_ABI_PIC_ADDR_REGNUM);
037176b6 2939 emit_insn (gen_set_got (pic_offset_table_rtx)); /* setting GOT. */
d34b0d1e 2940 }
2941
2942 /* If we are profiling, make sure no instructions are scheduled before
2943 the call to mcount. */
2944
2945 if (profile_flag)
2946 emit_insn (gen_blockage ());
2947}
2948
2949/* Do necessary cleanup after a function to restore stack, frame, and regs. */
2950
2951#define RA_MASK ((long) 0x80000000) /* 1 << 31 */
2952#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
2953
2954static void
2955microblaze_function_epilogue (FILE * file ATTRIBUTE_UNUSED,
2956 HOST_WIDE_INT size ATTRIBUTE_UNUSED)
2957{
2958 const char *fnname;
2959
2960 /* Get the function name the same way that toplev.c does before calling
2961 assemble_start_function. This is needed so that the name used here
2962 exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
2963 fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
2964
2965 if (!flag_inhibit_size_directive)
2966 {
2967 fputs ("\t.end\t", file);
0f718e59 2968 if (interrupt_handler && !break_handler)
d34b0d1e 2969 fputs ("_interrupt_handler", file);
0f718e59 2970 else if (break_handler)
2971 fputs ("_break_handler", file);
d34b0d1e 2972 else
2973 assemble_name (file, fnname);
2974 fputs ("\n", file);
2975 }
2976
2977 /* Reset state info for each function. */
2978 current_frame_info = zero_frame_info;
2979
2980 /* Restore the output file if optimizing the GP (optimizing the GP causes
2981 the text to be diverted to a tempfile, so that data decls come before
2982 references to the data). */
2983}
2984
2985/* Expand the epilogue into a bunch of separate insns. */
2986
2987void
2988microblaze_expand_epilogue (void)
2989{
2990 HOST_WIDE_INT fsiz = current_frame_info.total_size;
2991 rtx fsiz_rtx = GEN_INT (fsiz);
2992 rtx reg_rtx;
2993 rtx mem_rtx;
2994
2995 /* In case of interrupt handlers use addki instead of addi for changing the
2996 stack pointer value. */
2997
2998 if (microblaze_can_use_return_insn ())
2999 {
3000 emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
3001 GP_REG_FIRST +
3002 MB_ABI_SUB_RETURN_ADDR_REGNUM)));
3003 return;
3004 }
3005
3006 if (fsiz > 0)
3007 {
3008 /* Restore SUB_RETURN_ADDR_REGNUM at first. This is to prevent the
3009 sequence of load-followed by a use (in rtsd) in every prologue. Saves
3010 a load-use stall cycle :) This is also important to handle alloca.
3011 (See comments for if (frame_pointer_needed) below. */
3012
d5bf7b64 3013 if (!crtl->is_leaf || interrupt_handler)
d34b0d1e 3014 {
3015 mem_rtx =
3016 gen_rtx_MEM (SImode,
3017 gen_rtx_PLUS (Pmode, stack_pointer_rtx, const0_rtx));
3018 if (interrupt_handler)
3019 /* Do not optimize in flow analysis. */
3020 MEM_VOLATILE_P (mem_rtx) = 1;
3021 reg_rtx = gen_rtx_REG (SImode, MB_ABI_SUB_RETURN_ADDR_REGNUM);
3022 emit_move_insn (reg_rtx, mem_rtx);
3023 }
3024
3025 /* It is important that this is done after we restore the return address
3026 register (above). When alloca is used, we want to restore the
3027 sub-routine return address only from the current stack top and not
3028 from the frame pointer (which we restore below). (frame_pointer + 0)
3029 might have been over-written since alloca allocates memory on the
3030 current stack. */
3031 if (frame_pointer_needed)
3032 emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
3033
3034 /* _restore_ registers for epilogue. */
3035 save_restore_insns (0);
3036 emit_insn (gen_blockage ());
3037 emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, fsiz_rtx));
3038 }
3039
3040 emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, GP_REG_FIRST +
3041 MB_ABI_SUB_RETURN_ADDR_REGNUM)));
3042}
3043
3044
3045/* Return nonzero if this function is known to have a null epilogue.
3046 This allows the optimizer to omit jumps to jumps if no stack
3047 was created. */
3048
3049int
3050microblaze_can_use_return_insn (void)
3051{
3052 if (!reload_completed)
3053 return 0;
3054
3055 if (df_regs_ever_live_p (MB_ABI_SUB_RETURN_ADDR_REGNUM) || profile_flag)
3056 return 0;
3057
3058 if (current_frame_info.initialized)
3059 return current_frame_info.total_size == 0;
3060
3061 return compute_frame_size (get_frame_size ()) == 0;
3062}
3063
3064/* Implement TARGET_SECONDARY_RELOAD. */
3065
037176b6 3066static reg_class_t
d34b0d1e 3067microblaze_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x ATTRIBUTE_UNUSED,
037176b6 3068 reg_class_t rclass, enum machine_mode mode ATTRIBUTE_UNUSED,
d34b0d1e 3069 secondary_reload_info *sri ATTRIBUTE_UNUSED)
3070{
3071 if (rclass == ST_REGS)
3072 return GR_REGS;
3073
3074 return NO_REGS;
3075}
3076
3077static void
3078microblaze_globalize_label (FILE * stream, const char *name)
3079{
3080 fputs ("\t.globl\t", stream);
d9c1b601 3081 if (microblaze_is_interrupt_variant ())
d34b0d1e 3082 {
d9c1b601 3083 if (interrupt_handler && strcmp (name, INTERRUPT_HANDLER_NAME))
3084 fputs (INTERRUPT_HANDLER_NAME, stream);
0f718e59 3085 else if (break_handler && strcmp (name, BREAK_HANDLER_NAME))
3086 fputs (BREAK_HANDLER_NAME, stream);
d9c1b601 3087 else if (fast_interrupt && strcmp (name, FAST_INTERRUPT_NAME))
3088 fputs (FAST_INTERRUPT_NAME, stream);
d34b0d1e 3089 fputs ("\n\t.globl\t", stream);
3090 }
3091 assemble_name (stream, name);
3092 fputs ("\n", stream);
3093}
3094
3095/* Returns true if decl should be placed into a "small data" section. */
3096static bool
3097microblaze_elf_in_small_data_p (const_tree decl)
3098{
037176b6 3099 HOST_WIDE_INT size;
3100
d34b0d1e 3101 if (!TARGET_XLGPOPT)
3102 return false;
3103
3104 /* We want to merge strings, so we never consider them small data. */
3105 if (TREE_CODE (decl) == STRING_CST)
3106 return false;
3107
3108 /* Functions are never in the small data area. */
3109 if (TREE_CODE (decl) == FUNCTION_DECL)
3110 return false;
3111
3112 if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl))
3113 {
738a6bda 3114 const char *section = DECL_SECTION_NAME (decl);
d34b0d1e 3115 if (strcmp (section, ".sdata") == 0
3116 || strcmp (section, ".sdata2") == 0
3117 || strcmp (section, ".sbss") == 0
3118 || strcmp (section, ".sbss2") == 0)
3119 return true;
3120 }
3121
037176b6 3122 size = int_size_in_bytes (TREE_TYPE (decl));
d34b0d1e 3123
3124 return (size > 0 && size <= microblaze_section_threshold);
3125}
3126
3127
3128static section *
3129microblaze_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
3130{
3131 switch (categorize_decl_for_section (decl, reloc))
3132 {
3133 case SECCAT_RODATA_MERGE_STR:
3134 case SECCAT_RODATA_MERGE_STR_INIT:
3135 /* MB binutils have various issues with mergeable string sections and
3136 relaxation/relocation. Currently, turning mergeable sections
3137 into regular readonly sections. */
3138
3139 return readonly_data_section;
3140 default:
3141 return default_elf_select_section (decl, reloc, align);
3142 }
3143}
3144
3145/*
3146 Encode info about sections into the RTL based on a symbol's declaration.
3147 The default definition of this hook, default_encode_section_info in
3148 `varasm.c', sets a number of commonly-useful bits in SYMBOL_REF_FLAGS. */
3149
3150static void
3151microblaze_encode_section_info (tree decl, rtx rtl, int first)
3152{
3153 default_encode_section_info (decl, rtl, first);
3154}
3155
3156static rtx
3157expand_pic_symbol_ref (enum machine_mode mode ATTRIBUTE_UNUSED, rtx op)
3158{
3159 rtx result;
3160 result = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), UNSPEC_GOTOFF);
3161 result = gen_rtx_CONST (Pmode, result);
3162 result = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result);
3163 result = gen_const_mem (Pmode, result);
3164 return result;
3165}
3166
0462b5fb 3167static void
3168microblaze_asm_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
3169 HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
3170 tree function)
3171{
3172 rtx this_rtx, insn, funexp;
3173
3174 reload_completed = 1;
3175 epilogue_completed = 1;
3176
3177 /* Mark the end of the (empty) prologue. */
3178 emit_note (NOTE_INSN_PROLOGUE_END);
3179
3180 /* Find the "this" pointer. If the function returns a structure,
3181 the structure return pointer is in MB_ABI_FIRST_ARG_REGNUM. */
3182 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
3183 this_rtx = gen_rtx_REG (Pmode, (MB_ABI_FIRST_ARG_REGNUM + 1));
3184 else
3185 this_rtx = gen_rtx_REG (Pmode, MB_ABI_FIRST_ARG_REGNUM);
3186
3187 /* Apply the constant offset, if required. */
3188 if (delta)
3189 emit_insn (gen_addsi3 (this_rtx, this_rtx, GEN_INT (delta)));
3190
3191 /* Apply the offset from the vtable, if required. */
3192 if (vcall_offset)
3193 {
3194 rtx vcall_offset_rtx = GEN_INT (vcall_offset);
3195 rtx temp1 = gen_rtx_REG (Pmode, MB_ABI_TEMP1_REGNUM);
3196
3197 emit_move_insn (temp1, gen_rtx_MEM (Pmode, this_rtx));
3198
3199 rtx loc = gen_rtx_PLUS (Pmode, temp1, vcall_offset_rtx);
3200 emit_move_insn (temp1, gen_rtx_MEM (Pmode, loc));
3201
3202 emit_insn (gen_addsi3 (this_rtx, this_rtx, temp1));
3203 }
3204
3205 /* Generate a tail call to the target function. */
3206 if (!TREE_USED (function))
3207 {
3208 assemble_external (function);
3209 TREE_USED (function) = 1;
3210 }
3211
3212 funexp = XEXP (DECL_RTL (function), 0);
3213 rtx temp2 = gen_rtx_REG (Pmode, MB_ABI_TEMP2_REGNUM);
3214
3215 if (flag_pic)
3216 emit_move_insn (temp2, expand_pic_symbol_ref (Pmode, funexp));
3217 else
3218 emit_move_insn (temp2, funexp);
3219
3220 emit_insn (gen_indirect_jump (temp2));
3221
3222 /* Run just enough of rest_of_compilation. This sequence was
3223 "borrowed" from rs6000.c. */
3224 insn = get_insns ();
3225 shorten_branches (insn);
3226 final_start_function (insn, file, 1);
3227 final (insn, file, 1);
3228 final_end_function ();
3229
3230 reload_completed = 0;
3231 epilogue_completed = 0;
3232}
3233
d34b0d1e 3234bool
3235microblaze_expand_move (enum machine_mode mode, rtx operands[])
3236{
4eae25aa 3237 rtx op0, op1;
3238
3239 op0 = operands[0];
3240 op1 = operands[1];
3241
3242 if (!register_operand (op0, SImode)
3243 && !register_operand (op1, SImode)
3244 && (GET_CODE (op1) != CONST_INT || INTVAL (op1) != 0))
3245 {
3246 rtx temp = force_reg (SImode, op1);
3247 emit_move_insn (op0, temp);
3248 return true;
3249 }
d34b0d1e 3250 /* If operands[1] is a constant address invalid for pic, then we need to
3251 handle it just like LEGITIMIZE_ADDRESS does. */
4eae25aa 3252 if (GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF)
d34b0d1e 3253 {
4eae25aa 3254 rtx result;
3255 if (microblaze_tls_symbol_p(op1))
d34b0d1e 3256 {
4eae25aa 3257 result = microblaze_legitimize_tls_address (op1, NULL_RTX);
3258 emit_move_insn (op0, result);
3259 return true;
d34b0d1e 3260 }
4eae25aa 3261 else if (flag_pic)
d34b0d1e 3262 {
d34b0d1e 3263 if (reload_in_progress)
3264 df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
4eae25aa 3265 result = expand_pic_symbol_ref (mode, op1);
3266 emit_move_insn (op0, result);
d34b0d1e 3267 return true;
3268 }
4eae25aa 3269 }
3270 /* Handle Case of (const (plus symbol const_int)). */
3271 if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1,0)) == PLUS)
3272 {
3273 rtx p0, p1;
d34b0d1e 3274
4eae25aa 3275 p0 = XEXP (XEXP (op1, 0), 0);
3276 p1 = XEXP (XEXP (op1, 0), 1);
d34b0d1e 3277
4eae25aa 3278 if ((GET_CODE (p1) == CONST_INT)
3279 && ((GET_CODE (p0) == UNSPEC)
3280 || ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
3281 && (flag_pic == 2 || microblaze_tls_symbol_p (p0)
3282 || !SMALL_INT (p1)))))
d34b0d1e 3283 {
4eae25aa 3284 rtx temp = force_reg (SImode, p0);
3285 rtx temp2 = p1;
d34b0d1e 3286
4eae25aa 3287 if (flag_pic && reload_in_progress)
d34b0d1e 3288 df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
4eae25aa 3289 emit_move_insn (op0, gen_rtx_PLUS (SImode, temp, temp2));
d34b0d1e 3290 return true;
3291 }
3292 }
d34b0d1e 3293 return false;
3294}
3295
3296/* Expand shift operations. */
3297int
3298microblaze_expand_shift (rtx operands[])
3299{
3300 gcc_assert ((GET_CODE (operands[2]) == CONST_INT)
3301 || (GET_CODE (operands[2]) == REG)
3302 || (GET_CODE (operands[2]) == SUBREG));
3303
3304 /* Shift by one -- generate pattern. */
3305 if ((GET_CODE (operands[2]) == CONST_INT) && (INTVAL (operands[2]) == 1))
3306 return 0;
3307
3308 /* Have barrel shifter and shift > 1: use it. */
3309 if (TARGET_BARREL_SHIFT)
3310 return 0;
3311
3312 gcc_assert ((GET_CODE (operands[0]) == REG)
3313 || (GET_CODE (operands[0]) == SUBREG)
3314 || (GET_CODE (operands[1]) == REG)
3315 || (GET_CODE (operands[1]) == SUBREG));
3316
3317 /* Shift by zero -- copy regs if necessary. */
3318 if ((GET_CODE (operands[2]) == CONST_INT) && (INTVAL (operands[2]) == 0))
3319 {
3320 if (REGNO (operands[0]) != REGNO (operands[1]))
3321 emit_insn (gen_movsi (operands[0], operands[1]));
3322 return 1;
3323 }
3324
3325 return 0;
3326}
3327
3328/* Return an RTX indicating where the return address to the
3329 calling function can be found. */
3330rtx
3331microblaze_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
3332{
3333 if (count != 0)
3334 return NULL_RTX;
3335
3336 return gen_rtx_PLUS (Pmode,
3337 get_hard_reg_initial_val (Pmode,
3338 MB_ABI_SUB_RETURN_ADDR_REGNUM),
3339 GEN_INT (8));
3340}
3341
367b1459 3342/* Queue an .ident string in the queue of top-level asm statements.
3343 If the string size is below the threshold, put it into .sdata2.
3344 If the front-end is done, we must be being called from toplev.c.
3345 In that case, do nothing. */
d34b0d1e 3346void
367b1459 3347microblaze_asm_output_ident (const char *string)
d34b0d1e 3348{
367b1459 3349 const char *section_asm_op;
3350 int size;
3351 char *buf;
3352
35ee1c66 3353 if (symtab->state != PARSING)
367b1459 3354 return;
3355
3356 size = strlen (string) + 1;
d34b0d1e 3357 if (size <= microblaze_section_threshold)
367b1459 3358 section_asm_op = SDATA2_SECTION_ASM_OP;
d34b0d1e 3359 else
367b1459 3360 section_asm_op = READONLY_DATA_SECTION_ASM_OP;
3361
3362 buf = ACONCAT ((section_asm_op, "\n\t.ascii \"", string, "\\0\"\n", NULL));
35ee1c66 3363 symtab->finalize_toplevel_asm (build_string (strlen (buf), buf));
d34b0d1e 3364}
3365
3366static void
3367microblaze_elf_asm_init_sections (void)
3368{
3369 sdata2_section
3370 = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
3371 SDATA2_SECTION_ASM_OP);
3372}
3373
3374/* Generate assembler code for constant parts of a trampoline. */
3375
3376static void
3377microblaze_asm_trampoline_template (FILE *f)
3378{
10baa3f3 3379 fprintf (f, "\tmfs r18, rpc\n");
3380 fprintf (f, "\tlwi r3, r18, 16\n");
3381 fprintf (f, "\tlwi r18, r18, 20\n");
3382 fprintf (f, "\tbra r18\n");
d34b0d1e 3383 /* fprintf (f, "\t.word\t0x00000000\t\t# <function address>\n"); */
3384 /* fprintf (f, "\t.word\t0x00000000\t\t# <static chain value>\n"); */
3385}
3386
3387/* Implement TARGET_TRAMPOLINE_INIT. */
3388
3389static void
3390microblaze_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
3391{
3392 rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
3393 rtx mem;
3394
3395 emit_block_move (m_tramp, assemble_trampoline_template (),
10baa3f3 3396 GEN_INT (6*UNITS_PER_WORD), BLOCK_OP_NORMAL);
d34b0d1e 3397
10baa3f3 3398 mem = adjust_address (m_tramp, SImode, 16);
d34b0d1e 3399 emit_move_insn (mem, chain_value);
10baa3f3 3400 mem = adjust_address (m_tramp, SImode, 20);
d34b0d1e 3401 emit_move_insn (mem, fnaddr);
3402}
3403\f
96a9db22 3404/* Generate conditional branch -- first, generate test condition,
3405 second, generate correct branch instruction. */
3406
3407void
3408microblaze_expand_conditional_branch (enum machine_mode mode, rtx operands[])
d34b0d1e 3409{
96a9db22 3410 enum rtx_code code = GET_CODE (operands[0]);
3411 rtx cmp_op0 = operands[1];
3412 rtx cmp_op1 = operands[2];
3413 rtx label1 = operands[3];
d34b0d1e 3414 rtx comp_reg = gen_reg_rtx (SImode);
96a9db22 3415 rtx condition;
3416
d34b0d1e 3417 gcc_assert ((GET_CODE (cmp_op0) == REG) || (GET_CODE (cmp_op0) == SUBREG));
3418
3419 /* If comparing against zero, just test source reg. */
96a9db22 3420 if (cmp_op1 == const0_rtx)
3421 {
3422 comp_reg = cmp_op0;
3423 condition = gen_rtx_fmt_ee (signed_condition (code), SImode, comp_reg, const0_rtx);
3424 emit_jump_insn (gen_condjump (condition, label1));
3425 }
d34b0d1e 3426
96a9db22 3427 else if (code == EQ || code == NE)
d34b0d1e 3428 {
e249ec67 3429 /* Use xor for equal/not-equal comparison. */
3430 emit_insn (gen_xorsi3 (comp_reg, cmp_op0, cmp_op1));
96a9db22 3431 condition = gen_rtx_fmt_ee (signed_condition (code), SImode, comp_reg, const0_rtx);
3432 emit_jump_insn (gen_condjump (condition, label1));
d34b0d1e 3433 }
96a9db22 3434 else
d34b0d1e 3435 {
96a9db22 3436 /* Generate compare and branch in single instruction. */
d34b0d1e 3437 cmp_op1 = force_reg (mode, cmp_op1);
96a9db22 3438 condition = gen_rtx_fmt_ee (code, mode, cmp_op0, cmp_op1);
3439 emit_jump_insn (gen_branch_compare(condition, cmp_op0, cmp_op1, label1));
d34b0d1e 3440 }
d34b0d1e 3441}
3442
d34b0d1e 3443
3444void
3445microblaze_expand_conditional_branch_sf (rtx operands[])
3446{
3447 rtx condition;
3448 rtx cmp_op0 = XEXP (operands[0], 0);
3449 rtx cmp_op1 = XEXP (operands[0], 1);
3450 rtx comp_reg = gen_reg_rtx (SImode);
3451
3452 emit_insn (gen_cstoresf4 (comp_reg, operands[0], cmp_op0, cmp_op1));
3453 condition = gen_rtx_NE (SImode, comp_reg, const0_rtx);
3454 emit_jump_insn (gen_condjump (condition, operands[3]));
3455}
3456
3457/* Implement TARGET_FRAME_POINTER_REQUIRED. */
3458
3459static bool
3460microblaze_frame_pointer_required (void)
3461{
3462 /* If the function contains dynamic stack allocations, we need to
3463 use the frame pointer to access the static parts of the frame. */
3464 if (cfun->calls_alloca)
3465 return true;
3466 return false;
3467}
3468
3469void
3470microblaze_expand_divide (rtx operands[])
3471{
3472 /* Table lookup software divides. Works for all (nr/dr) where (0 <= nr,dr <= 15). */
3473
3474 rtx regt1 = gen_reg_rtx (SImode);
3475 rtx reg18 = gen_rtx_REG (SImode, R_TMP);
3476 rtx regqi = gen_reg_rtx (QImode);
3477 rtx div_label = gen_label_rtx ();
3478 rtx div_end_label = gen_label_rtx ();
3479 rtx div_table_rtx = gen_rtx_SYMBOL_REF (QImode,"_divsi3_table");
3480 rtx mem_rtx;
3481 rtx ret;
3482 rtx jump, cjump, insn;
3483
3484 insn = emit_insn (gen_iorsi3 (regt1, operands[1], operands[2]));
3485 cjump = emit_jump_insn_after (gen_cbranchsi4 (
3486 gen_rtx_GTU (SImode, regt1, GEN_INT (15)),
3487 regt1, GEN_INT (15), div_label), insn);
3488 LABEL_NUSES (div_label) = 1;
3489 JUMP_LABEL (cjump) = div_label;
3490 emit_insn (gen_rtx_CLOBBER (SImode, reg18));
3491
3492 emit_insn (gen_ashlsi3_bshift (regt1, operands[1], GEN_INT(4)));
3493 emit_insn (gen_addsi3 (regt1, regt1, operands[2]));
3494 mem_rtx = gen_rtx_MEM (QImode,
3495 gen_rtx_PLUS (Pmode, regt1, div_table_rtx));
3496
3497 insn = emit_insn (gen_movqi (regqi, mem_rtx));
3498 insn = emit_insn (gen_movsi (operands[0], gen_rtx_SUBREG (SImode, regqi, 0)));
3499 jump = emit_jump_insn_after (gen_jump (div_end_label), insn);
3500 JUMP_LABEL (jump) = div_end_label;
3501 LABEL_NUSES (div_end_label) = 1;
3502 emit_barrier ();
3503
3504 emit_label (div_label);
3505 ret = emit_library_call_value (gen_rtx_SYMBOL_REF (Pmode, "__divsi3"),
3506 operands[0], LCT_NORMAL,
3507 GET_MODE (operands[0]), 2, operands[1],
3508 GET_MODE (operands[1]), operands[2],
3509 GET_MODE (operands[2]));
3510 if (ret != operands[0])
3511 emit_move_insn (operands[0], ret);
3512
3513 emit_label (div_end_label);
3514 emit_insn (gen_blockage ());
3515}
3516
3517/* Implement TARGET_FUNCTION_VALUE. */
3518static rtx
3519microblaze_function_value (const_tree valtype,
3520 const_tree func ATTRIBUTE_UNUSED,
3521 bool outgoing ATTRIBUTE_UNUSED)
3522{
3523 return LIBCALL_VALUE (TYPE_MODE (valtype));
3524}
3525
3526/* Implement TARGET_SCHED_ADJUST_COST. */
3527static int
3528microblaze_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
3529 rtx dep ATTRIBUTE_UNUSED, int cost)
3530{
3531 if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
3532 return cost;
3533 if (REG_NOTE_KIND (link) != 0)
3534 return 0;
3535 return cost;
3536}
ca316360 3537
3538/* Implement TARGET_LEGITIMATE_CONSTANT_P.
3539
3540 At present, GAS doesn't understand li.[sd], so don't allow it
3541 to be generated at present. */
3542static bool
4eae25aa 3543microblaze_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
ca316360 3544{
4eae25aa 3545
3546 if (microblaze_cannot_force_const_mem(mode, x))
3547 return false;
3548
3549 if (GET_CODE (x) == CONST_DOUBLE)
3550 {
3551 return microblaze_const_double_ok (x, GET_MODE (x));
3552 }
3553
3554 /* Handle Case of (const (plus unspec const_int)). */
3555 if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
3556 {
3557 rtx p0, p1;
3558
3559 p0 = XEXP (XEXP (x, 0), 0);
3560 p1 = XEXP (XEXP (x, 0), 1);
3561
3562 if (GET_CODE(p1) == CONST_INT)
3563 {
3564 /* Const offset from UNSPEC is not supported. */
3565 if ((GET_CODE (p0) == UNSPEC))
3566 return false;
3567
3568 if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
3569 && (microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
3570 return false;
3571 }
3572 }
3573
3574 return true;
ca316360 3575}
4eae25aa 3576
d34b0d1e 3577\f
3578#undef TARGET_ENCODE_SECTION_INFO
3579#define TARGET_ENCODE_SECTION_INFO microblaze_encode_section_info
3580
3581#undef TARGET_ASM_GLOBALIZE_LABEL
3582#define TARGET_ASM_GLOBALIZE_LABEL microblaze_globalize_label
3583
3584#undef TARGET_ASM_FUNCTION_PROLOGUE
3585#define TARGET_ASM_FUNCTION_PROLOGUE microblaze_function_prologue
3586
3587#undef TARGET_ASM_FUNCTION_EPILOGUE
3588#define TARGET_ASM_FUNCTION_EPILOGUE microblaze_function_epilogue
3589
3590#undef TARGET_RTX_COSTS
3591#define TARGET_RTX_COSTS microblaze_rtx_costs
3592
4eae25aa 3593#undef TARGET_CANNOT_FORCE_CONST_MEM
3594#define TARGET_CANNOT_FORCE_CONST_MEM microblaze_cannot_force_const_mem
3595
d34b0d1e 3596#undef TARGET_ADDRESS_COST
3597#define TARGET_ADDRESS_COST microblaze_address_cost
3598
3599#undef TARGET_ATTRIBUTE_TABLE
3600#define TARGET_ATTRIBUTE_TABLE microblaze_attribute_table
3601
3602#undef TARGET_IN_SMALL_DATA_P
3603#define TARGET_IN_SMALL_DATA_P microblaze_elf_in_small_data_p
3604
3605#undef TARGET_ASM_SELECT_SECTION
3606#define TARGET_ASM_SELECT_SECTION microblaze_select_section
3607
3608#undef TARGET_HAVE_SRODATA_SECTION
3609#define TARGET_HAVE_SRODATA_SECTION true
3610
3611#undef TARGET_ASM_FUNCTION_END_PROLOGUE
3612#define TARGET_ASM_FUNCTION_END_PROLOGUE \
3613 microblaze_function_end_prologue
3614
d34b0d1e 3615#undef TARGET_ARG_PARTIAL_BYTES
3616#define TARGET_ARG_PARTIAL_BYTES function_arg_partial_bytes
3617
9a51523d 3618#undef TARGET_FUNCTION_ARG
3619#define TARGET_FUNCTION_ARG microblaze_function_arg
3620
3621#undef TARGET_FUNCTION_ARG_ADVANCE
3622#define TARGET_FUNCTION_ARG_ADVANCE microblaze_function_arg_advance
3623
d34b0d1e 3624#undef TARGET_CAN_ELIMINATE
3625#define TARGET_CAN_ELIMINATE microblaze_can_eliminate
3626
3627#undef TARGET_LEGITIMIZE_ADDRESS
3628#define TARGET_LEGITIMIZE_ADDRESS microblaze_legitimize_address
3629
3630#undef TARGET_LEGITIMATE_ADDRESS_P
3631#define TARGET_LEGITIMATE_ADDRESS_P microblaze_legitimate_address_p
3632
3633#undef TARGET_FRAME_POINTER_REQUIRED
3634#define TARGET_FRAME_POINTER_REQUIRED microblaze_frame_pointer_required
3635
3636#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
3637#define TARGET_ASM_TRAMPOLINE_TEMPLATE microblaze_asm_trampoline_template
3638
3639#undef TARGET_TRAMPOLINE_INIT
3640#define TARGET_TRAMPOLINE_INIT microblaze_trampoline_init
3641
3642#undef TARGET_PROMOTE_FUNCTION_MODE
3643#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
3644
3645#undef TARGET_FUNCTION_VALUE
3646#define TARGET_FUNCTION_VALUE microblaze_function_value
3647
3648#undef TARGET_SECONDARY_RELOAD
3649#define TARGET_SECONDARY_RELOAD microblaze_secondary_reload
3650
0462b5fb 3651#undef TARGET_ASM_OUTPUT_MI_THUNK
3652#define TARGET_ASM_OUTPUT_MI_THUNK microblaze_asm_output_mi_thunk
3653
3654#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
3655#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
3656
d34b0d1e 3657#undef TARGET_SCHED_ADJUST_COST
3658#define TARGET_SCHED_ADJUST_COST microblaze_adjust_cost
3659
3660#undef TARGET_ASM_INIT_SECTIONS
3661#define TARGET_ASM_INIT_SECTIONS microblaze_elf_asm_init_sections
3662
3663#undef TARGET_OPTION_OVERRIDE
3664#define TARGET_OPTION_OVERRIDE microblaze_option_override
3665
ca316360 3666#undef TARGET_LEGITIMATE_CONSTANT_P
3667#define TARGET_LEGITIMATE_CONSTANT_P microblaze_legitimate_constant_p
3668
d34b0d1e 3669struct gcc_target targetm = TARGET_INITIALIZER;
3670\f
3671#include "gt-microblaze.h"