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