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