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