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