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