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