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