1 /* Subroutines used for code generation on Xilinx MicroBlaze.
2    Copyright (C) 2009-2019 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 movmemsi 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,machine_mode mode,const_tree type,bool named ATTRIBUTE_UNUSED)1545 microblaze_function_arg_advance (cumulative_args_t cum_v,
1546 				 machine_mode mode,
1547 				 const_tree type, bool named ATTRIBUTE_UNUSED)
1548 {
1549   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1550 
1551   cum->arg_number++;
1552   switch (mode)
1553     {
1554     case E_VOIDmode:
1555       break;
1556 
1557     default:
1558       gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
1559 	  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
1560 
1561       cum->gp_reg_found = 1;
1562       cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
1563 			 / UNITS_PER_WORD);
1564       break;
1565 
1566     case E_BLKmode:
1567       cum->gp_reg_found = 1;
1568       cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
1569 			 / UNITS_PER_WORD);
1570       break;
1571 
1572     case E_SFmode:
1573       cum->arg_words++;
1574       if (!cum->gp_reg_found && cum->arg_number <= 2)
1575 	cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
1576       break;
1577 
1578     case E_DFmode:
1579       cum->arg_words += 2;
1580       if (!cum->gp_reg_found && cum->arg_number <= 2)
1581 	cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
1582       break;
1583 
1584     case E_DImode:
1585       cum->gp_reg_found = 1;
1586       cum->arg_words += 2;
1587       break;
1588 
1589     case E_QImode:
1590     case E_HImode:
1591     case E_SImode:
1592     case E_TImode:
1593       cum->gp_reg_found = 1;
1594       cum->arg_words++;
1595       break;
1596     }
1597 }
1598 
1599 /* Return an RTL expression containing the register for the given mode,
1600    or 0 if the argument is to be passed on the stack.  */
1601 
1602 static rtx
microblaze_function_arg(cumulative_args_t cum_v,machine_mode mode,const_tree type ATTRIBUTE_UNUSED,bool named ATTRIBUTE_UNUSED)1603 microblaze_function_arg (cumulative_args_t cum_v, machine_mode mode,
1604 			 const_tree type ATTRIBUTE_UNUSED,
1605 			 bool named ATTRIBUTE_UNUSED)
1606 {
1607   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1608 
1609   rtx ret;
1610   int regbase = -1;
1611   int *arg_words = &cum->arg_words;
1612 
1613   cum->last_arg_fp = 0;
1614   switch (mode)
1615     {
1616     case E_SFmode:
1617     case E_DFmode:
1618     case E_VOIDmode:
1619     case E_QImode:
1620     case E_HImode:
1621     case E_SImode:
1622     case E_DImode:
1623     case E_TImode:
1624       regbase = GP_ARG_FIRST;
1625       break;
1626     default:
1627       gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
1628 	  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
1629       /* FALLTHRU */
1630     case E_BLKmode:
1631       regbase = GP_ARG_FIRST;
1632       break;
1633     }
1634 
1635   if (*arg_words >= MAX_ARGS_IN_REGISTERS)
1636     ret = 0;
1637   else
1638     {
1639       gcc_assert (regbase != -1);
1640 
1641       ret = gen_rtx_REG (mode, regbase + *arg_words);
1642     }
1643 
1644   if (mode == VOIDmode)
1645     {
1646       if (cum->num_adjusts > 0)
1647 	ret = gen_rtx_PARALLEL ((machine_mode) cum->fp_code,
1648 				gen_rtvec_v (cum->num_adjusts, cum->adjust));
1649     }
1650 
1651   return ret;
1652 }
1653 
1654 /* Return number of bytes of argument to put in registers. */
1655 static int
function_arg_partial_bytes(cumulative_args_t cum_v,machine_mode mode,tree type,bool named ATTRIBUTE_UNUSED)1656 function_arg_partial_bytes (cumulative_args_t cum_v, machine_mode mode,
1657 			    tree type, bool named ATTRIBUTE_UNUSED)
1658 {
1659   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1660 
1661   if ((mode == BLKmode
1662        || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
1663        || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
1664       && cum->arg_words < MAX_ARGS_IN_REGISTERS)
1665     {
1666       int words;
1667       if (mode == BLKmode)
1668 	words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
1669 		 / UNITS_PER_WORD);
1670       else
1671 	words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
1672 
1673       if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS)
1674 	return 0;		/* structure fits in registers */
1675 
1676       return (MAX_ARGS_IN_REGISTERS - cum->arg_words) * UNITS_PER_WORD;
1677     }
1678 
1679   else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS - 1)
1680     return UNITS_PER_WORD;
1681 
1682   return 0;
1683 }
1684 
1685 /*  Convert a version number of the form "vX.YY.Z" to an integer encoding
1686     for easier range comparison.  */
1687 static int
microblaze_version_to_int(const char * version)1688 microblaze_version_to_int (const char *version)
1689 {
1690   const char *p, *v;
1691   const char *tmpl = "vXX.YY.Z";
1692   int iver = 0;
1693 
1694   p = version;
1695   v = tmpl;
1696 
1697   while (*p)
1698     {
1699       if (*v == 'X')
1700 	{			/* Looking for major  */
1701           if (*p == '.')
1702             {
1703               v++;
1704             }
1705           else
1706             {
1707 	      if (!(*p >= '0' && *p <= '9'))
1708 	        return -1;
1709 	      iver += (int) (*p - '0');
1710               iver *= 10;
1711 	     }
1712         }
1713       else if (*v == 'Y')
1714 	{			/* Looking for minor  */
1715 	  if (!(*p >= '0' && *p <= '9'))
1716 	    return -1;
1717 	  iver += (int) (*p - '0');
1718 	  iver *= 10;
1719 	}
1720       else if (*v == 'Z')
1721 	{			/* Looking for compat  */
1722 	  if (!(*p >= 'a' && *p <= 'z'))
1723 	    return -1;
1724 	  iver *= 10;
1725 	  iver += (int) (*p - 'a');
1726 	}
1727       else
1728 	{
1729 	  if (*p != *v)
1730 	    return -1;
1731 	}
1732 
1733       v++;
1734       p++;
1735     }
1736 
1737   if (*p)
1738     return -1;
1739 
1740   return iver;
1741 }
1742 
1743 
1744 static void
microblaze_option_override(void)1745 microblaze_option_override (void)
1746 {
1747   register int i, start;
1748   register int regno;
1749   register machine_mode mode;
1750   int ver;
1751 
1752   microblaze_section_threshold = (global_options_set.x_g_switch_value
1753 				  ? g_switch_value
1754 				  : MICROBLAZE_DEFAULT_GVALUE);
1755 
1756   if (flag_pic)
1757     {
1758       /* Make sure it's 2, we only support one kind of PIC.  */
1759       flag_pic = 2;
1760       if (!TARGET_SUPPORTS_PIC)
1761         {
1762 	  error ("%<-fPIC%>/%<-fpic%> not supported for this target");
1763           /* Clear it to avoid further errors.  */
1764           flag_pic = 0;
1765         }
1766     }
1767 
1768   /* Check the MicroBlaze CPU version for any special action to be done.  */
1769   if (microblaze_select_cpu == NULL)
1770     microblaze_select_cpu = MICROBLAZE_DEFAULT_CPU;
1771   ver = microblaze_version_to_int (microblaze_select_cpu);
1772   if (ver == -1)
1773     {
1774       error ("%qs is an invalid argument to %<-mcpu=%>", microblaze_select_cpu);
1775     }
1776 
1777   ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v3.00.a");
1778   if (ver < 0)
1779     {
1780       /* No hardware exceptions in earlier versions. So no worries.  */
1781 #if 0
1782       microblaze_select_flags &= ~(MICROBLAZE_MASK_NO_UNSAFE_DELAY);
1783 #endif
1784       microblaze_no_unsafe_delay = 0;
1785       microblaze_pipe = MICROBLAZE_PIPE_3;
1786     }
1787   else if (ver == 0
1788 	   || (MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v4.00.b")
1789 	       == 0))
1790     {
1791 #if 0
1792       microblaze_select_flags |= (MICROBLAZE_MASK_NO_UNSAFE_DELAY);
1793 #endif
1794       microblaze_no_unsafe_delay = 1;
1795       microblaze_pipe = MICROBLAZE_PIPE_3;
1796     }
1797   else
1798     {
1799       /* We agree to use 5 pipe-stage model even on area optimized 3
1800          pipe-stage variants.  */
1801 #if 0
1802       microblaze_select_flags &= ~(MICROBLAZE_MASK_NO_UNSAFE_DELAY);
1803 #endif
1804       microblaze_no_unsafe_delay = 0;
1805       microblaze_pipe = MICROBLAZE_PIPE_5;
1806       if (MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v5.00.a") == 0
1807 	  || MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu,
1808 					 "v5.00.b") == 0
1809 	  || MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu,
1810 					 "v5.00.c") == 0)
1811 	{
1812 	  /* Pattern compares are to be turned on by default only when
1813  	     compiling for MB v5.00.'z'.  */
1814 	  target_flags |= MASK_PATTERN_COMPARE;
1815 	}
1816     }
1817 
1818   ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v6.00.a");
1819   if (ver < 0)
1820     {
1821       if (TARGET_MULTIPLY_HIGH)
1822 	warning (0,
1823 		 "%<-mxl-multiply-high%> can be used only with "
1824 		 "%<-mcpu=v6.00.a%> or greater");
1825     }
1826 
1827   ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v8.10.a");
1828   microblaze_has_clz = 1;
1829   if (ver < 0)
1830     {
1831         /* MicroBlaze prior to 8.10.a didn't have clz.  */
1832         microblaze_has_clz = 0;
1833     }
1834 
1835   /* TARGET_REORDER defaults to 2 if -mxl-reorder not specified.  */
1836   ver = MICROBLAZE_VERSION_COMPARE (microblaze_select_cpu, "v8.30.a");
1837   if (ver < 0)
1838     {
1839         if (TARGET_REORDER == 1)
1840 	  warning (0, "%<-mxl-reorder%> can be used only with "
1841 		   "%<-mcpu=v8.30.a%> or greater");
1842         TARGET_REORDER = 0;
1843     }
1844   else if ((ver == 0) && !TARGET_PATTERN_COMPARE)
1845     {
1846         if (TARGET_REORDER == 1)
1847 	  warning (0, "%<-mxl-reorder%> requires %<-mxl-pattern-compare%> for "
1848 		   "%<-mcpu=v8.30.a%>");
1849         TARGET_REORDER = 0;
1850     }
1851 
1852   if (TARGET_MULTIPLY_HIGH && TARGET_SOFT_MUL)
1853     error ("%<-mxl-multiply-high%> requires %<-mno-xl-soft-mul%>");
1854 
1855   /* Always use DFA scheduler.  */
1856   microblaze_sched_use_dfa = 1;
1857 
1858 #if 0
1859   microblaze_abicalls = MICROBLAZE_ABICALLS_NO;
1860 #endif
1861 
1862   /* Initialize the high, low values for legit floating point constants.  */
1863   real_maxval (&dfhigh, 0, DFmode);
1864   real_maxval (&dflow, 1, DFmode);
1865   real_maxval (&sfhigh, 0, SFmode);
1866   real_maxval (&sflow, 1, SFmode);
1867 
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   microblaze_print_operand_punct[']'] = 1;
1879   microblaze_print_operand_punct['<'] = 1;
1880   microblaze_print_operand_punct['>'] = 1;
1881   microblaze_print_operand_punct['{'] = 1;
1882   microblaze_print_operand_punct['}'] = 1;
1883   microblaze_print_operand_punct['^'] = 1;
1884   microblaze_print_operand_punct['$'] = 1;
1885   microblaze_print_operand_punct['+'] = 1;
1886 
1887   /* Set up array to map GCC register number to debug register number.
1888      Ignore the special purpose register numbers.  */
1889 
1890   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
1891     microblaze_dbx_regno[i] = -1;
1892 
1893   start = GP_DBX_FIRST - GP_REG_FIRST;
1894   for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
1895     microblaze_dbx_regno[i] = i + start;
1896 
1897   /* Set up array giving whether a given register can hold a given mode.   */
1898 
1899   for (mode = VOIDmode;
1900        mode != MAX_MACHINE_MODE; mode = (machine_mode) ((int) mode + 1))
1901     {
1902       register int size = GET_MODE_SIZE (mode);
1903 
1904       for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
1905 	{
1906 	  register int ok;
1907 
1908 	  if (mode == CCmode)
1909 	    {
1910 	      ok = (ST_REG_P (regno) || GP_REG_P (regno));
1911 	    }
1912 	  else if (GP_REG_P (regno))
1913 	    ok = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
1914 	  else
1915 	    ok = 0;
1916 
1917 	  microblaze_hard_regno_mode_ok_p[(int) mode][regno] = ok;
1918 	}
1919     }
1920 }
1921 
1922 /* Implement TARGET_HARD_REGNO_MODE_OK.  In 32 bit mode, require that
1923    DImode and DFmode be in even registers.  For DImode, this makes some
1924    of the insns easier to write, since you don't have to worry about a
1925    DImode value in registers 3 & 4, producing a result in 4 & 5.
1926 
1927    To make the code simpler, the hook now just references an
1928    array built in override_options.  */
1929 
1930 static bool
microblaze_hard_regno_mode_ok(unsigned int regno,machine_mode mode)1931 microblaze_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
1932 {
1933   return microblaze_hard_regno_mode_ok_p[mode][regno];
1934 }
1935 
1936 /* Implement TARGET_MODES_TIEABLE_P.  */
1937 
1938 static bool
microblaze_modes_tieable_p(machine_mode mode1,machine_mode mode2)1939 microblaze_modes_tieable_p (machine_mode mode1, machine_mode mode2)
1940 {
1941   return ((GET_MODE_CLASS (mode1) == MODE_FLOAT
1942 	   || GET_MODE_CLASS (mode1) == MODE_COMPLEX_FLOAT)
1943 	  == (GET_MODE_CLASS (mode2) == MODE_FLOAT
1944 	      || GET_MODE_CLASS (mode2) == MODE_COMPLEX_FLOAT));
1945 }
1946 
1947 /* Return true if FUNC is an interrupt function as specified
1948    by the "interrupt_handler" attribute.  */
1949 
1950 static int
microblaze_interrupt_function_p(tree func)1951 microblaze_interrupt_function_p (tree func)
1952 {
1953   tree a;
1954 
1955   if (TREE_CODE (func) != FUNCTION_DECL)
1956     return 0;
1957 
1958   a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
1959   return a != NULL_TREE;
1960 }
1961 
1962 static int
microblaze_fast_interrupt_function_p(tree func)1963 microblaze_fast_interrupt_function_p (tree func)
1964 {
1965   tree a;
1966 
1967   if (TREE_CODE (func) != FUNCTION_DECL)
1968     return 0;
1969 
1970   a = lookup_attribute ("fast_interrupt", DECL_ATTRIBUTES (func));
1971   return a != NULL_TREE;
1972 }
1973 int
microblaze_break_function_p(tree func)1974 microblaze_break_function_p (tree func)
1975 {
1976   tree a;
1977   if (!func)
1978     return 0;
1979   if (TREE_CODE (func) != FUNCTION_DECL)
1980     return 0;
1981 
1982   a = lookup_attribute ("break_handler", DECL_ATTRIBUTES (func));
1983   return a != NULL_TREE;
1984 }
1985 /* Return true if FUNC is an interrupt function which uses
1986    normal return, indicated by the "save_volatiles" attribute.  */
1987 
1988 static int
microblaze_save_volatiles(tree func)1989 microblaze_save_volatiles (tree func)
1990 {
1991   tree a;
1992 
1993   if (TREE_CODE (func) != FUNCTION_DECL)
1994     return 0;
1995 
1996   a = lookup_attribute ("save_volatiles", DECL_ATTRIBUTES (func));
1997   return a != NULL_TREE;
1998 }
1999 
2000 /* Return whether function is tagged with 'interrupt_handler'
2001    or 'fast_interrupt' attribute.  Return true if function
2002    should use return from interrupt rather than normal
2003    function return.  */
2004 int
microblaze_is_interrupt_variant(void)2005 microblaze_is_interrupt_variant (void)
2006 {
2007   return (interrupt_handler || fast_interrupt);
2008 }
2009 int
microblaze_is_break_handler(void)2010 microblaze_is_break_handler (void)
2011 {
2012   return break_handler;
2013 }
2014 
2015 /* Determine of register must be saved/restored in call.  */
2016 static int
microblaze_must_save_register(int regno)2017 microblaze_must_save_register (int regno)
2018 {
2019   if (pic_offset_table_rtx &&
2020       (regno == MB_ABI_PIC_ADDR_REGNUM) && df_regs_ever_live_p (regno))
2021     return 1;
2022 
2023   if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
2024     return 1;
2025 
2026   if (frame_pointer_needed && (regno == HARD_FRAME_POINTER_REGNUM))
2027     return 1;
2028 
2029   if (crtl->calls_eh_return
2030       && regno == MB_ABI_SUB_RETURN_ADDR_REGNUM)
2031     return 1;
2032 
2033   if (!crtl->is_leaf)
2034     {
2035       if (regno == MB_ABI_SUB_RETURN_ADDR_REGNUM)
2036 	return 1;
2037       if ((microblaze_is_interrupt_variant () || save_volatiles) &&
2038 	  (regno >= 3 && regno <= 12))
2039 	return 1;
2040     }
2041 
2042   if (microblaze_is_interrupt_variant ())
2043     {
2044       if (df_regs_ever_live_p (regno)
2045 	  || regno == MB_ABI_MSR_SAVE_REG
2046 	  || (interrupt_handler
2047               && (regno == MB_ABI_ASM_TEMP_REGNUM
2048 	          || regno == MB_ABI_EXCEPTION_RETURN_ADDR_REGNUM)))
2049 	return 1;
2050     }
2051 
2052   if (save_volatiles)
2053     {
2054       if (df_regs_ever_live_p (regno)
2055 	  || regno == MB_ABI_ASM_TEMP_REGNUM
2056 	  || regno == MB_ABI_EXCEPTION_RETURN_ADDR_REGNUM)
2057 	return 1;
2058     }
2059 
2060   if (crtl->calls_eh_return
2061       && (regno == EH_RETURN_DATA_REGNO (0)
2062           || regno == EH_RETURN_DATA_REGNO (1)))
2063     return 1;
2064 
2065   return 0;
2066 }
2067 
2068 /* Return the bytes needed to compute the frame pointer from the current
2069    stack pointer.
2070 
2071    MicroBlaze stack frames look like:
2072 
2073 
2074 
2075              Before call		        After call
2076         +-----------------------+	+-----------------------+
2077    high |			|       |      			|
2078    mem. |  local variables,     |	|  local variables,	|
2079         |  callee saved and     |       |  callee saved and    	|
2080 	|  temps     		|       |  temps     	        |
2081         +-----------------------+	+-----------------------+
2082         |  arguments for called	|       |  arguments for called |
2083 	|  subroutines		|	|  subroutines  	|
2084         |  (optional)           |       |  (optional)           |
2085         +-----------------------+	+-----------------------+
2086 	|  Link register 	|	|  Link register        |
2087     SP->|                       |       |                       |
2088 	+-----------------------+       +-----------------------+
2089 					|		        |
2090                                         |  local variables,     |
2091                                         |  callee saved and     |
2092                                         |  temps                |
2093 					+-----------------------+
2094                                         |   MSR (optional if,   |
2095                                         |   interrupt handler)  |
2096 					+-----------------------+
2097 					|			|
2098                                         |  alloca allocations   |
2099         				|			|
2100 					+-----------------------+
2101 					|			|
2102                                         |  arguments for called |
2103                                         |  subroutines          |
2104                                         |  (optional)           |
2105         				|		        |
2106 					+-----------------------+
2107                                         |  Link register        |
2108    low                           FP,SP->|                       |
2109    memory        			+-----------------------+
2110 
2111 */
2112 
2113 static HOST_WIDE_INT
compute_frame_size(HOST_WIDE_INT size)2114 compute_frame_size (HOST_WIDE_INT size)
2115 {
2116   int regno;
2117   HOST_WIDE_INT total_size;	/* # bytes that the entire frame takes up.  */
2118   HOST_WIDE_INT var_size;	/* # bytes that local variables take up.  */
2119   HOST_WIDE_INT args_size;	/* # bytes that outgoing arguments take up.  */
2120   int link_debug_size;		/* # bytes for link register.  */
2121   HOST_WIDE_INT gp_reg_size;	/* # bytes needed to store calle-saved gp regs.  */
2122   long mask;			/* mask of saved gp registers.  */
2123 
2124   interrupt_handler =
2125     microblaze_interrupt_function_p (current_function_decl);
2126   break_handler =
2127     microblaze_break_function_p (current_function_decl);
2128 
2129   fast_interrupt =
2130     microblaze_fast_interrupt_function_p (current_function_decl);
2131   save_volatiles = microblaze_save_volatiles (current_function_decl);
2132   if (break_handler)
2133     interrupt_handler = break_handler;
2134 
2135   gp_reg_size = 0;
2136   mask = 0;
2137   var_size = size;
2138   args_size = crtl->outgoing_args_size;
2139 
2140   if ((args_size == 0) && cfun->calls_alloca)
2141     args_size = NUM_OF_ARGS * UNITS_PER_WORD;
2142 
2143   total_size = var_size + args_size;
2144 
2145   if (flag_pic == 2 && !TARGET_PIC_DATA_TEXT_REL)
2146     /* force setting GOT.  */
2147     df_set_regs_ever_live (MB_ABI_PIC_ADDR_REGNUM, true);
2148 
2149   /* Calculate space needed for gp registers.  */
2150   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
2151     {
2152       if (microblaze_must_save_register (regno))
2153 	{
2154 
2155 	  if (regno != MB_ABI_SUB_RETURN_ADDR_REGNUM)
2156 	    /* Don't account for link register. It is accounted specially below.  */
2157 	    gp_reg_size += GET_MODE_SIZE (SImode);
2158 
2159 	  mask |= (1L << (regno - GP_REG_FIRST));
2160 	}
2161     }
2162 
2163   total_size += gp_reg_size;
2164 
2165   /* Add 4 bytes for MSR.  */
2166   if (microblaze_is_interrupt_variant ())
2167     total_size += 4;
2168 
2169   /* No space to be allocated for link register in leaf functions with no other
2170      stack requirements.  */
2171   if (total_size == 0 && crtl->is_leaf)
2172     link_debug_size = 0;
2173   else
2174     link_debug_size = UNITS_PER_WORD;
2175 
2176   total_size += link_debug_size;
2177 
2178   /* Save other computed information.  */
2179   current_frame_info.total_size = total_size;
2180   current_frame_info.var_size = var_size;
2181   current_frame_info.args_size = args_size;
2182   current_frame_info.gp_reg_size = gp_reg_size;
2183   current_frame_info.mask = mask;
2184   current_frame_info.initialized = reload_completed;
2185   current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
2186   current_frame_info.link_debug_size = link_debug_size;
2187 
2188   if (mask)
2189     /* Offset from which to callee-save GP regs.  */
2190     current_frame_info.gp_offset = (total_size - gp_reg_size);
2191   else
2192     current_frame_info.gp_offset = 0;
2193 
2194   /* Ok, we're done.  */
2195   return total_size;
2196 }
2197 
2198 /* Make sure that we're not trying to eliminate to the wrong hard frame
2199    pointer.  */
2200 
2201 static bool
microblaze_can_eliminate(const int from,const int to)2202 microblaze_can_eliminate (const int from, const int to)
2203 {
2204   return ((from == RETURN_ADDRESS_POINTER_REGNUM && !leaf_function_p())
2205    	  || (to == MB_ABI_SUB_RETURN_ADDR_REGNUM && leaf_function_p())
2206   	  || (from != RETURN_ADDRESS_POINTER_REGNUM
2207    	      && (to == HARD_FRAME_POINTER_REGNUM
2208 		  || (to == STACK_POINTER_REGNUM && !frame_pointer_needed))));
2209 }
2210 
2211 /* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
2212    pointer or argument pointer or the return address pointer.  TO is either
2213    the stack pointer or hard frame pointer.  */
2214 
2215 HOST_WIDE_INT
microblaze_initial_elimination_offset(int from,int to)2216 microblaze_initial_elimination_offset (int from, int to)
2217 {
2218   HOST_WIDE_INT offset;
2219 
2220   switch (from)
2221     {
2222     case FRAME_POINTER_REGNUM:
2223       offset = 0;
2224       break;
2225     case ARG_POINTER_REGNUM:
2226       if (to == STACK_POINTER_REGNUM || to == HARD_FRAME_POINTER_REGNUM)
2227 	offset = compute_frame_size (get_frame_size ());
2228       else
2229 	gcc_unreachable ();
2230       break;
2231     case RETURN_ADDRESS_POINTER_REGNUM:
2232       if (crtl->is_leaf)
2233 	offset = 0;
2234       else
2235 	offset = current_frame_info.gp_offset +
2236 	  ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT)));
2237       break;
2238     default:
2239       gcc_unreachable ();
2240     }
2241   return offset;
2242 }
2243 
2244 /* Print operands using format code.
2245 
2246    The MicroBlaze specific codes are:
2247 
2248    'X'  X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
2249    'x'  X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
2250    'F'  op is CONST_DOUBLE, print 32 bits in hex,
2251    'd'  output integer constant in decimal,
2252    'z'	if the operand is 0, use $0 instead of normal operand.
2253    'D'  print second register of double-word register operand.
2254    'L'  print low-order register of double-word register operand.
2255    'M'  print high-order register of double-word register operand.
2256    'C'  print part of opcode for a branch condition.
2257    'N'  print part of opcode for a branch condition, inverted.
2258    'S'  X is CODE_LABEL, print with prefix of "LS" (for embedded switch).
2259    'B'  print 'z' for EQ, 'n' for NE
2260    'b'  print 'n' for EQ, 'z' for NE
2261    'T'  print 'f' for EQ, 't' for NE
2262    't'  print 't' for EQ, 'f' for NE
2263    'm'  Print 1<<operand.
2264    'i'  Print 'i' if MEM operand has immediate value
2265    'y'  Print 'y' if MEM operand is single register
2266    'o'	Print operand address+4
2267    '?'	Print 'd' if we use a branch with delay slot instead of normal branch.
2268    'h'  Print high word of const_double (int or float) value as hex
2269    'j'  Print low word of const_double (int or float) value as hex
2270    's'  Print -1 if operand is negative, 0 if positive (sign extend)
2271    '@'	Print the name of the temporary register (rMB_ABI_ASM_TEMP_REGNUM).
2272    '#'	Print nop if the delay slot of a branch is not filled.
2273 */
2274 
2275 void
print_operand(FILE * file,rtx op,int letter)2276 print_operand (FILE * file, rtx op, int letter)
2277 {
2278   register enum rtx_code code;
2279 
2280   if (PRINT_OPERAND_PUNCT_VALID_P (letter))
2281     {
2282       switch (letter)
2283 	{
2284 	case '?':
2285 	  /* Conditionally add a 'd' to indicate filled delay slot.  */
2286 	  if (final_sequence != NULL)
2287 	    fputs ("d", file);
2288 	  break;
2289 
2290 	case '#':
2291 	  /* Conditionally add a nop in unfilled delay slot.  */
2292 	  if (final_sequence == NULL)
2293 	    fputs ("nop\t\t# Unfilled delay slot\n", file);
2294 	  break;
2295 
2296 	case '@':
2297 	  fputs (reg_names[GP_REG_FIRST + MB_ABI_ASM_TEMP_REGNUM], file);
2298 	  break;
2299 
2300 	default:
2301 	  output_operand_lossage ("unknown punctuation '%c'", letter);
2302 	  break;
2303 	}
2304 
2305       return;
2306     }
2307 
2308   if (!op)
2309     {
2310       output_operand_lossage ("null pointer");
2311       return;
2312     }
2313 
2314   code = GET_CODE (op);
2315 
2316   if (code == SIGN_EXTEND)
2317     op = XEXP (op, 0), code = GET_CODE (op);
2318 
2319   if (letter == 'C')
2320     switch (code)
2321       {
2322       case EQ:
2323 	fputs ("eq", file);
2324 	break;
2325       case NE:
2326 	fputs ("ne", file);
2327 	break;
2328       case GT:
2329       case GTU:
2330 	fputs ("gt", file);
2331 	break;
2332       case GE:
2333       case GEU:
2334 	fputs ("ge", file);
2335 	break;
2336       case LT:
2337       case LTU:
2338 	fputs ("lt", file);
2339 	break;
2340       case LE:
2341       case LEU:
2342 	fputs ("le", file);
2343 	break;
2344       default:
2345 	fatal_insn ("PRINT_OPERAND, invalid insn for %%C", op);
2346       }
2347 
2348   else if (letter == 'N')
2349     switch (code)
2350       {
2351       case EQ:
2352 	fputs ("ne", file);
2353 	break;
2354       case NE:
2355 	fputs ("eq", file);
2356 	break;
2357       case GT:
2358       case GTU:
2359 	fputs ("le", file);
2360 	break;
2361       case GE:
2362       case GEU:
2363 	fputs ("lt", file);
2364 	break;
2365       case LT:
2366       case LTU:
2367 	fputs ("ge", file);
2368 	break;
2369       case LE:
2370       case LEU:
2371 	fputs ("gt", file);
2372 	break;
2373       default:
2374 	fatal_insn ("PRINT_OPERAND, invalid insn for %%N", op);
2375       }
2376 
2377   else if (letter == 'S')
2378     {
2379       char buffer[100];
2380 
2381       ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
2382       assemble_name (file, buffer);
2383     }
2384 
2385   /* Print 'i' for memory operands which have immediate values.  */
2386   else if (letter == 'i')
2387     {
2388       if (code == MEM)
2389 	{
2390 	  struct microblaze_address_info info;
2391 
2392 	  if (!microblaze_classify_address
2393 	      (&info, XEXP (op, 0), GET_MODE (op), 1))
2394 	    fatal_insn ("insn contains an invalid address !", op);
2395 
2396 	  switch (info.type)
2397 	    {
2398 	    case ADDRESS_REG:
2399 	    case ADDRESS_CONST_INT:
2400 	    case ADDRESS_SYMBOLIC:
2401 	    case ADDRESS_SYMBOLIC_TXT_REL:
2402 	    case ADDRESS_GOTOFF:
2403 	    case ADDRESS_TLS:
2404 	      fputs ("i", file);
2405 	      break;
2406 	    case ADDRESS_REG_INDEX:
2407 	      break;
2408 	    case ADDRESS_INVALID:
2409 	    case ADDRESS_PLT:
2410 	      fatal_insn ("invalid address", op);
2411 	    }
2412 	}
2413     }
2414 
2415   else if (code == REG || code == SUBREG)
2416     {
2417       register int regnum;
2418 
2419       if (code == REG)
2420 	regnum = REGNO (op);
2421       else
2422 	regnum = true_regnum (op);
2423 
2424       if ((letter == 'M' && !WORDS_BIG_ENDIAN)
2425 	  || (letter == 'L' && WORDS_BIG_ENDIAN) || letter == 'D')
2426 	regnum++;
2427 
2428       fprintf (file, "%s", reg_names[regnum]);
2429     }
2430 
2431   else if (code == MEM)
2432     if (letter == 'o')
2433       {
2434 	rtx op4 = adjust_address (op, GET_MODE (op), 4);
2435 	output_address (GET_MODE (op), XEXP (op4, 0));
2436       }
2437     else if (letter == 'y')
2438       {
2439         rtx mem_reg = XEXP (op, 0);
2440         if (GET_CODE (mem_reg) == REG)
2441         {
2442             register int regnum = REGNO (mem_reg);
2443             fprintf (file, "%s", reg_names[regnum]);
2444         }
2445       }
2446     else
2447       output_address (GET_MODE (op), XEXP (op, 0));
2448 
2449   else if (letter == 'h' || letter == 'j')
2450     {
2451       long val[2];
2452       if (code == CONST_DOUBLE)
2453 	{
2454 	  if (GET_MODE (op) == DFmode)
2455 	    REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (op), val);
2456 	  else
2457 	    {
2458 	      val[0] = CONST_DOUBLE_HIGH (op);
2459 	      val[1] = CONST_DOUBLE_LOW (op);
2460 	    }
2461 	}
2462       else if (code == CONST_INT)
2463         {
2464 	  val[0] = (INTVAL (op) & 0xffffffff00000000LL) >> 32;
2465 	  val[1] = INTVAL (op) & 0x00000000ffffffffLL;
2466 	  if (val[0] == 0 && val[1] < 0)
2467 	    val[0] = -1;
2468 
2469         }
2470       fprintf (file, "0x%8.8lx", (letter == 'h') ? val[0] : val[1]);
2471     }
2472   else if (code == CONST_DOUBLE)
2473     {
2474       if (letter == 'F')
2475 	{
2476 	  unsigned long value_long;
2477 	  REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (op),
2478 				       value_long);
2479 	  fprintf (file, HOST_WIDE_INT_PRINT_HEX, value_long);
2480 	}
2481       else
2482 	{
2483 	  char s[60];
2484 	  real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
2485 	  fputs (s, file);
2486 	}
2487     }
2488 
2489   else if (code == UNSPEC)
2490     {
2491       print_operand_address (file, op);
2492     }
2493 
2494   else if (letter == 'x' && GET_CODE (op) == CONST_INT)
2495     fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL (op));
2496 
2497   else if (letter == 'X' && GET_CODE (op) == CONST_INT)
2498     fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
2499 
2500   else if (letter == 'd' && GET_CODE (op) == CONST_INT)
2501     fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL (op)));
2502 
2503   else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
2504     fputs (reg_names[GP_REG_FIRST], file);
2505 
2506   else if (letter == 's' && GET_CODE (op) == CONST_INT)
2507     if (INTVAL (op) < 0)
2508       fputs ("-1", file);
2509     else
2510       fputs ("0", file);
2511 
2512   else if (letter == 'd' || letter == 'x' || letter == 'X' || letter == 's')
2513     output_operand_lossage ("letter %c was found & insn was not CONST_INT", letter);
2514 
2515   else if (letter == 'B')
2516     fputs (code == EQ ? "z" : "n", file);
2517   else if (letter == 'b')
2518     fputs (code == EQ ? "n" : "z", file);
2519   else if (letter == 'T')
2520     fputs (code == EQ ? "f" : "t", file);
2521   else if (letter == 't')
2522     fputs (code == EQ ? "t" : "f", file);
2523 
2524   else if (code == CONST
2525            && ((GET_CODE (XEXP (op, 0)) == REG)
2526                || (GET_CODE (XEXP (op, 0)) == UNSPEC)))
2527     {
2528       print_operand (file, XEXP (op, 0), letter);
2529     }
2530   else if (code == CONST
2531            && (GET_CODE (XEXP (op, 0)) == PLUS)
2532            && (GET_CODE (XEXP (XEXP (op, 0), 0)) == REG)
2533            && (GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST))
2534     {
2535       print_operand_address (file, XEXP (op, 0));
2536     }
2537   else if (letter == 'm')
2538     fprintf (file, HOST_WIDE_INT_PRINT_DEC, (1L << INTVAL (op)));
2539   else
2540     output_addr_const (file, op);
2541 }
2542 
2543 /* A C compound statement to output to stdio stream STREAM the
2544    assembler syntax for an instruction operand that is a memory
2545    reference whose address is ADDR.  ADDR is an RTL expression.
2546 
2547    Possible address classifications and output formats are,
2548 
2549    ADDRESS_REG                  "%0, r0"
2550 
2551    ADDRESS_REG with non-zero    "%0, <addr_const>"
2552    offset
2553 
2554    ADDRESS_REG_INDEX            "rA, RB"
2555                                 (if rA is r0, rA and rB are swapped)
2556 
2557    ADDRESS_CONST_INT            "r0, <addr_const>"
2558 
2559    ADDRESS_SYMBOLIC             "rBase, <addr_const>"
2560                                 (rBase is a base register suitable for the
2561 				 symbol's type)
2562 */
2563 
2564 void
print_operand_address(FILE * file,rtx addr)2565 print_operand_address (FILE * file, rtx addr)
2566 {
2567   struct microblaze_address_info info;
2568   enum microblaze_address_type type;
2569   if (!microblaze_classify_address (&info, addr, GET_MODE (addr), 2))
2570     fatal_insn ("insn contains an invalid address !", addr);
2571 
2572   type = info.type;
2573   switch (info.type)
2574     {
2575     case ADDRESS_REG:
2576       fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
2577       output_addr_const (file, info.offset);
2578       break;
2579     case ADDRESS_REG_INDEX:
2580       if (REGNO (info.regA) == 0)
2581 	/* Make rB == r0 instead of rA == r0. This helps reduce read port
2582            congestion.  */
2583 	fprintf (file, "%s,%s", reg_names[REGNO (info.regB)],
2584 		 reg_names[REGNO (info.regA)]);
2585       else if (REGNO (info.regB) != 0)
2586 	/* This is a silly swap to help Dhrystone.  */
2587 	fprintf (file, "%s,%s", reg_names[REGNO (info.regB)],
2588 		 reg_names[REGNO (info.regA)]);
2589       break;
2590     case ADDRESS_CONST_INT:
2591       fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
2592       output_addr_const (file, info.offset);
2593       break;
2594     case ADDRESS_SYMBOLIC:
2595     case ADDRESS_SYMBOLIC_TXT_REL:
2596     case ADDRESS_GOTOFF:
2597     case ADDRESS_PLT:
2598     case ADDRESS_TLS:
2599       if (info.regA)
2600 	fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
2601       output_addr_const (file, info.symbol);
2602       if (type == ADDRESS_GOTOFF)
2603 	{
2604 	  fputs ("@GOT", file);
2605 	}
2606       else if (type == ADDRESS_PLT)
2607 	{
2608 	  fputs ("@PLT", file);
2609 	}
2610       else if (type == ADDRESS_SYMBOLIC_TXT_REL)
2611     {
2612       if (info.offset != NULL && CONST_INT_P (info.offset)
2613 	  && INTVAL (info.offset) > 0)
2614 	{
2615 	  fprintf (file, "+");
2616 	  output_addr_const (file, info.offset);
2617 	}
2618       fputs ("@TXTREL", file);
2619     }
2620       else if (type == ADDRESS_TLS)
2621 	{
2622 	  switch (info.tls_type)
2623 	    {
2624 	      case TLS_GD:
2625 		fputs ("@TLSGD", file);
2626 		break;
2627 	      case TLS_LDM:
2628 		fputs ("@TLSLDM", file);
2629 		break;
2630 	      case TLS_DTPREL:
2631 		fputs ("@TLSDTPREL", file);
2632 		break;
2633 	      default :
2634 		abort();
2635 		break;
2636 	    }
2637 	}
2638       break;
2639     case ADDRESS_INVALID:
2640       fatal_insn ("invalid address", addr);
2641       break;
2642     }
2643 }
2644 
2645 /* Emit either a label, .comm, or .lcomm directive, and mark that the symbol
2646    is used, so that we don't emit an .extern for it in
2647    microblaze_asm_file_end.  */
2648 
2649 void
microblaze_declare_object(FILE * stream,const char * name,const char * section,const char * fmt,int size)2650 microblaze_declare_object (FILE * stream, const char *name,
2651 			   const char *section, const char *fmt, int size)
2652 {
2653 
2654   fputs (section, stream);
2655   assemble_name (stream, name);
2656   fprintf (stream, fmt, size);
2657 }
2658 
2659 /* Common code to emit the insns (or to write the instructions to a file)
2660    to save/restore registers.
2661 
2662    Other parts of the code assume that MICROBLAZE_TEMP1_REGNUM (aka large_reg)
2663    is not modified within save_restore_insns.  */
2664 
2665 #define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
2666 
2667 /* Save or restore instructions based on whether this is the prologue or
2668    epilogue.  prologue is 1 for the prologue.  */
2669 static void
save_restore_insns(int prologue)2670 save_restore_insns (int prologue)
2671 {
2672   rtx base_reg_rtx, reg_rtx, mem_rtx, /* msr_rtx, */ isr_reg_rtx =
2673     0, isr_mem_rtx = 0;
2674   rtx isr_msr_rtx = 0, insn;
2675   long mask = current_frame_info.mask;
2676   HOST_WIDE_INT gp_offset;
2677   int regno;
2678 
2679   if (frame_pointer_needed
2680       && !BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
2681     gcc_unreachable ();
2682 
2683   if (mask == 0)
2684     return;
2685 
2686   /* Save registers starting from high to low.  The debuggers prefer at least
2687      the return register be stored at func+4, and also it allows us not to
2688      need a nop in the epilog if at least one register is reloaded in
2689      addition to return address.  */
2690 
2691   /* Pick which pointer to use as a base register.  For small frames, just
2692      use the stack pointer.  Otherwise, use a temporary register.  Save 2
2693      cycles if the save area is near the end of a large frame, by reusing
2694      the constant created in the prologue/epilogue to adjust the stack
2695      frame.  */
2696 
2697   gp_offset = current_frame_info.gp_offset;
2698 
2699   gcc_assert (gp_offset > 0);
2700 
2701   base_reg_rtx = stack_pointer_rtx;
2702 
2703   /* For interrupt_handlers, need to save/restore the MSR.  */
2704   if (microblaze_is_interrupt_variant ())
2705     {
2706       isr_mem_rtx = gen_rtx_MEM (SImode,
2707 				 gen_rtx_PLUS (Pmode, base_reg_rtx,
2708 					       GEN_INT (current_frame_info.
2709 							gp_offset -
2710 							UNITS_PER_WORD)));
2711 
2712       /* Do not optimize in flow analysis.  */
2713       MEM_VOLATILE_P (isr_mem_rtx) = 1;
2714       isr_reg_rtx = gen_rtx_REG (SImode, MB_ABI_MSR_SAVE_REG);
2715       isr_msr_rtx = gen_rtx_REG (SImode, ST_REG);
2716     }
2717 
2718   if (microblaze_is_interrupt_variant () && !prologue)
2719     {
2720       emit_move_insn (isr_reg_rtx, isr_mem_rtx);
2721       emit_move_insn (isr_msr_rtx, isr_reg_rtx);
2722       /* Do not optimize in flow analysis.  */
2723       emit_insn (gen_rtx_USE (SImode, isr_reg_rtx));
2724       emit_insn (gen_rtx_USE (SImode, isr_msr_rtx));
2725     }
2726 
2727   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
2728     {
2729       if (BITSET_P (mask, regno - GP_REG_FIRST))
2730 	{
2731 	  if (regno == MB_ABI_SUB_RETURN_ADDR_REGNUM)
2732 	    /* Don't handle here. Already handled as the first register.  */
2733 	    continue;
2734 
2735 	  reg_rtx = gen_rtx_REG (SImode, regno);
2736 	  insn = gen_rtx_PLUS (Pmode, base_reg_rtx, GEN_INT (gp_offset));
2737 	  mem_rtx = gen_rtx_MEM (SImode, insn);
2738 	  if (microblaze_is_interrupt_variant () || save_volatiles)
2739 	    /* Do not optimize in flow analysis.  */
2740 	    MEM_VOLATILE_P (mem_rtx) = 1;
2741 
2742 	  if (prologue)
2743 	    {
2744 	      insn = emit_move_insn (mem_rtx, reg_rtx);
2745 	      RTX_FRAME_RELATED_P (insn) = 1;
2746 	    }
2747 	  else
2748 	    {
2749 	      insn = emit_move_insn (reg_rtx, mem_rtx);
2750 	    }
2751 
2752 	  gp_offset += GET_MODE_SIZE (SImode);
2753 	}
2754     }
2755 
2756   if (microblaze_is_interrupt_variant () && prologue)
2757     {
2758       emit_move_insn (isr_reg_rtx, isr_msr_rtx);
2759       emit_move_insn (isr_mem_rtx, isr_reg_rtx);
2760 
2761       /* Do not optimize in flow analysis.  */
2762       emit_insn (gen_rtx_USE (SImode, isr_reg_rtx));
2763       emit_insn (gen_rtx_USE (SImode, isr_msr_rtx));
2764     }
2765 
2766   /* Done saving and restoring */
2767 }
2768 
2769 
2770 /* Set up the stack and frame (if desired) for the function.  */
2771 static void
microblaze_function_prologue(FILE * file)2772 microblaze_function_prologue (FILE * file)
2773 {
2774   const char *fnname;
2775   long fsiz = current_frame_info.total_size;
2776 
2777   /* Get the function name the same way that toplev.c does before calling
2778      assemble_start_function.  This is needed so that the name used here
2779      exactly matches the name used in ASM_DECLARE_FUNCTION_NAME.  */
2780   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
2781   if (!flag_inhibit_size_directive)
2782     {
2783       fputs ("\t.ent\t", file);
2784       if (interrupt_handler && strcmp (INTERRUPT_HANDLER_NAME, fnname))
2785         fputs ("_interrupt_handler", file);
2786       else if (break_handler && strcmp (BREAK_HANDLER_NAME, fnname))
2787 	fputs ("_break_handler", file);
2788       else if (fast_interrupt && strcmp (FAST_INTERRUPT_NAME, fnname))
2789         fputs ("_fast_interrupt", file);
2790       else
2791 	assemble_name (file, fnname);
2792       fputs ("\n", file);
2793       if (!microblaze_is_interrupt_variant ())
2794 	ASM_OUTPUT_TYPE_DIRECTIVE (file, fnname, "function");
2795     }
2796 
2797   assemble_name (file, fnname);
2798   fputs (":\n", file);
2799 
2800   if (interrupt_handler && strcmp (INTERRUPT_HANDLER_NAME, fnname))
2801     fputs ("_interrupt_handler:\n", file);
2802   if (break_handler && strcmp (BREAK_HANDLER_NAME, fnname))
2803     fputs ("_break_handler:\n", file);
2804   if (!flag_inhibit_size_directive)
2805     {
2806       /* .frame FRAMEREG, FRAMESIZE, RETREG.  */
2807       fprintf (file,
2808 	       "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d, args= %d\n",
2809 	       (reg_names[(frame_pointer_needed)
2810 			  ? HARD_FRAME_POINTER_REGNUM :
2811 			  STACK_POINTER_REGNUM]), fsiz,
2812 	       reg_names[MB_ABI_SUB_RETURN_ADDR_REGNUM + GP_REG_FIRST],
2813 	       current_frame_info.var_size, current_frame_info.num_gp,
2814 	       (int) crtl->outgoing_args_size);
2815       fprintf (file, "\t.mask\t0x%08lx\n", current_frame_info.mask);
2816     }
2817 }
2818 
2819 /* Output extra assembler code at the end of a prologue.  */
2820 static void
microblaze_function_end_prologue(FILE * file)2821 microblaze_function_end_prologue (FILE * file)
2822 {
2823   if (TARGET_STACK_CHECK)
2824     {
2825       fprintf (file, "\t# Stack Check Stub -- Start.\n\t");
2826       fprintf (file, "ori\tr18,r0,_stack_end\n\t");
2827       fprintf (file, "cmpu\tr18,r1,r18\n\t");
2828       fprintf (file, "bgei\tr18,_stack_overflow_exit\n\t");
2829       fprintf (file, "# Stack Check Stub -- End.\n");
2830     }
2831 }
2832 
2833 static void
microblaze_elf_asm_cdtor(rtx symbol,int priority,bool is_ctor)2834 microblaze_elf_asm_cdtor (rtx symbol, int priority, bool is_ctor)
2835 {
2836   section *s;
2837 
2838   if (priority != DEFAULT_INIT_PRIORITY)
2839     {
2840       char buf[18];
2841       sprintf (buf, "%s.%.5u",
2842                is_ctor ? ".ctors" : ".dtors",
2843                MAX_INIT_PRIORITY - priority);
2844       s = get_section (buf, SECTION_WRITE, NULL_TREE);
2845     }
2846   else if (is_ctor)
2847     s = ctors_section;
2848   else
2849     s = dtors_section;
2850 
2851   switch_to_section (s);
2852   assemble_align (POINTER_SIZE);
2853   fputs ("\t.word\t", asm_out_file);
2854   output_addr_const (asm_out_file, symbol);
2855   fputs ("\n", asm_out_file);
2856 }
2857 
2858 /* Add a function to the list of static constructors.  */
2859 
2860 static void
microblaze_elf_asm_constructor(rtx symbol,int priority)2861 microblaze_elf_asm_constructor (rtx symbol, int priority)
2862 {
2863   microblaze_elf_asm_cdtor (symbol, priority, /*is_ctor=*/true);
2864 }
2865 
2866 /* Add a function to the list of static destructors.  */
2867 
2868 static void
microblaze_elf_asm_destructor(rtx symbol,int priority)2869 microblaze_elf_asm_destructor (rtx symbol, int priority)
2870 {
2871   microblaze_elf_asm_cdtor (symbol, priority, /*is_ctor=*/false);
2872 }
2873 
2874 /* Expand the prologue into a bunch of separate insns.  */
2875 
2876 void
microblaze_expand_prologue(void)2877 microblaze_expand_prologue (void)
2878 {
2879   int regno;
2880   HOST_WIDE_INT fsiz;
2881   const char *arg_name = 0;
2882   tree fndecl = current_function_decl;
2883   tree fntype = TREE_TYPE (fndecl);
2884   tree fnargs = DECL_ARGUMENTS (fndecl);
2885   rtx next_arg_reg;
2886   int i;
2887   tree next_arg;
2888   tree cur_arg;
2889   CUMULATIVE_ARGS args_so_far_v;
2890   cumulative_args_t args_so_far;
2891   rtx mem_rtx, reg_rtx;
2892 
2893   /* If struct value address is treated as the first argument, make it so.  */
2894   if (aggregate_value_p (DECL_RESULT (fndecl), fntype)
2895       && !cfun->returns_pcc_struct)
2896     {
2897       tree type = build_pointer_type (fntype);
2898       tree function_result_decl = build_decl (BUILTINS_LOCATION, PARM_DECL,
2899 					      NULL_TREE, type);
2900 
2901       DECL_ARG_TYPE (function_result_decl) = type;
2902       TREE_CHAIN (function_result_decl) = fnargs;
2903       fnargs = function_result_decl;
2904     }
2905 
2906   /* Determine the last argument, and get its name.  */
2907 
2908   INIT_CUMULATIVE_ARGS (args_so_far_v, fntype, NULL_RTX, 0, 0);
2909   args_so_far = pack_cumulative_args (&args_so_far_v);
2910   regno = GP_ARG_FIRST;
2911 
2912   for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
2913     {
2914       tree passed_type = DECL_ARG_TYPE (cur_arg);
2915       machine_mode passed_mode = TYPE_MODE (passed_type);
2916       rtx entry_parm;
2917 
2918       if (TREE_ADDRESSABLE (passed_type))
2919 	{
2920 	  passed_type = build_pointer_type (passed_type);
2921 	  passed_mode = Pmode;
2922 	}
2923 
2924       entry_parm = targetm.calls.function_arg (args_so_far, passed_mode,
2925 					       passed_type, true);
2926 
2927       if (entry_parm)
2928 	{
2929 	  int words;
2930 
2931 	  /* passed in a register, so will get homed automatically.  */
2932 	  if (GET_MODE (entry_parm) == BLKmode)
2933 	    words = (int_size_in_bytes (passed_type) + 3) / 4;
2934 	  else
2935 	    words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
2936 
2937 	  regno = REGNO (entry_parm) + words - 1;
2938 	}
2939       else
2940 	{
2941 	  regno = GP_ARG_LAST + 1;
2942 	  break;
2943 	}
2944 
2945       targetm.calls.function_arg_advance (args_so_far, passed_mode,
2946 					  passed_type, true);
2947 
2948       next_arg = TREE_CHAIN (cur_arg);
2949       if (next_arg == 0)
2950 	{
2951 	  if (DECL_NAME (cur_arg))
2952 	    arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg));
2953 
2954 	  break;
2955 	}
2956     }
2957 
2958   /* Split parallel insn into a sequence of insns.  */
2959 
2960   next_arg_reg = targetm.calls.function_arg (args_so_far, VOIDmode,
2961 					     void_type_node, true);
2962   if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
2963     {
2964       rtvec adjust = XVEC (next_arg_reg, 0);
2965       int num = GET_NUM_ELEM (adjust);
2966 
2967       for (i = 0; i < num; i++)
2968 	{
2969 	  rtx pattern = RTVEC_ELT (adjust, i);
2970 	  emit_insn (pattern);
2971 	}
2972     }
2973 
2974   fsiz = compute_frame_size (get_frame_size ());
2975 
2976   if (flag_stack_usage_info)
2977     current_function_static_stack_size = fsiz;
2978 
2979   /* If this function is a varargs function, store any registers that
2980      would normally hold arguments ($5 - $10) on the stack.  */
2981   if (((TYPE_ARG_TYPES (fntype) != 0
2982 	&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
2983 	    != void_type_node))
2984        || (arg_name != 0
2985 	   && ((arg_name[0] == '_'
2986 		&& strcmp (arg_name, "__builtin_va_alist") == 0)
2987 	       || (arg_name[0] == 'v'
2988 		   && strcmp (arg_name, "va_alist") == 0)))))
2989     {
2990       int offset = (regno - GP_ARG_FIRST + 1) * UNITS_PER_WORD;
2991       rtx ptr = stack_pointer_rtx;
2992 
2993       /* If we are doing svr4-abi, sp has already been decremented by fsiz. */
2994       for (; regno <= GP_ARG_LAST; regno++)
2995 	{
2996 	  if (offset != 0)
2997 	    ptr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
2998 	  emit_move_insn (gen_rtx_MEM (SImode, ptr),
2999 			  gen_rtx_REG (SImode, regno));
3000 
3001 	  offset += GET_MODE_SIZE (SImode);
3002 	}
3003     }
3004 
3005   if (fsiz > 0)
3006     {
3007       rtx fsiz_rtx = GEN_INT (fsiz);
3008 
3009       rtx_insn *insn = NULL;
3010       insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
3011 				    fsiz_rtx));
3012       if (insn)
3013 	RTX_FRAME_RELATED_P (insn) = 1;
3014 
3015       /* Handle SUB_RETURN_ADDR_REGNUM specially at first.  */
3016       if (!crtl->is_leaf || interrupt_handler)
3017 	{
3018 	  mem_rtx = gen_rtx_MEM (SImode,
3019 				 gen_rtx_PLUS (Pmode, stack_pointer_rtx,
3020 					       const0_rtx));
3021 
3022 	  if (interrupt_handler)
3023 	    /* Do not optimize in flow analysis.  */
3024 	    MEM_VOLATILE_P (mem_rtx) = 1;
3025 
3026 	  reg_rtx = gen_rtx_REG (SImode, MB_ABI_SUB_RETURN_ADDR_REGNUM);
3027 	  insn = emit_move_insn (mem_rtx, reg_rtx);
3028 	  RTX_FRAME_RELATED_P (insn) = 1;
3029 	}
3030 
3031       /* _save_ registers for prologue.  */
3032       save_restore_insns (1);
3033 
3034       if (frame_pointer_needed)
3035 	{
3036 	  rtx_insn *insn = 0;
3037 
3038 	  insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
3039 				       stack_pointer_rtx));
3040 
3041 	  if (insn)
3042 	    RTX_FRAME_RELATED_P (insn) = 1;
3043 	}
3044     }
3045 
3046   if ((flag_pic == 2 || TLS_NEEDS_GOT )
3047       && df_regs_ever_live_p (MB_ABI_PIC_ADDR_REGNUM))
3048     {
3049       if ((flag_pic == 2 && !TARGET_PIC_DATA_TEXT_REL) || TLS_NEEDS_GOT)
3050 	{
3051 	  SET_REGNO (pic_offset_table_rtx, MB_ABI_PIC_ADDR_REGNUM);
3052 	  /* setting GOT.  */
3053 	  emit_insn (gen_set_got (pic_offset_table_rtx));
3054 	}
3055       else
3056 	{
3057 	  SET_REGNO (pic_offset_table_rtx, MB_ABI_PIC_ADDR_REGNUM);
3058 	  /* setting start of text.  */
3059 	  emit_insn (gen_set_text (pic_offset_table_rtx));
3060 	}
3061     }
3062 
3063   /* If we are profiling, make sure no instructions are scheduled before
3064      the call to mcount.  */
3065 
3066   if (profile_flag)
3067     emit_insn (gen_blockage ());
3068 }
3069 
3070 /* Do necessary cleanup after a function to restore stack, frame, and regs.  */
3071 
3072 #define RA_MASK ((long) 0x80000000)	/* 1 << 31 */
3073 #define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
3074 
3075 static void
microblaze_function_epilogue(FILE * file)3076 microblaze_function_epilogue (FILE *file)
3077 {
3078   const char *fnname;
3079 
3080   /* Get the function name the same way that toplev.c does before calling
3081      assemble_start_function.  This is needed so that the name used here
3082      exactly matches the name used in ASM_DECLARE_FUNCTION_NAME.  */
3083   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
3084 
3085   if (!flag_inhibit_size_directive)
3086     {
3087       fputs ("\t.end\t", file);
3088       if (interrupt_handler && !break_handler)
3089 	fputs ("_interrupt_handler", file);
3090       else if (break_handler)
3091         fputs ("_break_handler", file);
3092       else
3093 	assemble_name (file, fnname);
3094       fputs ("\n", file);
3095     }
3096 
3097   /* Reset state info for each function.  */
3098   current_frame_info = zero_frame_info;
3099 
3100   /* Restore the output file if optimizing the GP (optimizing the GP causes
3101      the text to be diverted to a tempfile, so that data decls come before
3102      references to the data).  */
3103 }
3104 
3105 /* Expand the epilogue into a bunch of separate insns.  */
3106 
3107 void
microblaze_expand_epilogue(void)3108 microblaze_expand_epilogue (void)
3109 {
3110   HOST_WIDE_INT fsiz = current_frame_info.total_size;
3111   rtx fsiz_rtx = GEN_INT (fsiz);
3112   rtx reg_rtx;
3113   rtx mem_rtx;
3114 
3115   /* In case of interrupt handlers use addki instead of addi for changing the
3116      stack pointer value.  */
3117 
3118   if (microblaze_can_use_return_insn ())
3119     {
3120       emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
3121 							GP_REG_FIRST +
3122 							MB_ABI_SUB_RETURN_ADDR_REGNUM)));
3123       return;
3124     }
3125 
3126   if (fsiz > 0)
3127     {
3128       /* Restore SUB_RETURN_ADDR_REGNUM at first. This is to prevent the
3129          sequence of load-followed by a use (in rtsd) in every prologue. Saves
3130          a load-use stall cycle  :)   This is also important to handle alloca.
3131          (See comments for if (frame_pointer_needed) below.  */
3132 
3133       if (!crtl->is_leaf || interrupt_handler)
3134 	{
3135 	  mem_rtx =
3136 	    gen_rtx_MEM (SImode,
3137 			 gen_rtx_PLUS (Pmode, stack_pointer_rtx, const0_rtx));
3138 	  if (interrupt_handler)
3139 	    /* Do not optimize in flow analysis.  */
3140 	    MEM_VOLATILE_P (mem_rtx) = 1;
3141 	  reg_rtx = gen_rtx_REG (SImode, MB_ABI_SUB_RETURN_ADDR_REGNUM);
3142 	  emit_move_insn (reg_rtx, mem_rtx);
3143 	}
3144 
3145       /* It is important that this is done after we restore the return address
3146          register (above).  When alloca is used, we want to restore the
3147 	 sub-routine return address only from the current stack top and not
3148 	 from the frame pointer (which we restore below). (frame_pointer + 0)
3149 	 might have been over-written since alloca allocates memory on the
3150 	 current stack.  */
3151       if (frame_pointer_needed)
3152 	emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
3153 
3154       /* _restore_ registers for epilogue.  */
3155       save_restore_insns (0);
3156       emit_insn (gen_blockage ());
3157       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, fsiz_rtx));
3158     }
3159 
3160   if (crtl->calls_eh_return)
3161     emit_insn (gen_addsi3 (stack_pointer_rtx,
3162                            stack_pointer_rtx,
3163                            gen_raw_REG (SImode,
3164 					MB_EH_STACKADJ_REGNUM)));
3165 
3166   emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, GP_REG_FIRST +
3167 						    MB_ABI_SUB_RETURN_ADDR_REGNUM)));
3168 }
3169 
3170 
3171 /* Return nonzero if this function is known to have a null epilogue.
3172    This allows the optimizer to omit jumps to jumps if no stack
3173    was created.  */
3174 
3175 int
microblaze_can_use_return_insn(void)3176 microblaze_can_use_return_insn (void)
3177 {
3178   if (!reload_completed)
3179     return 0;
3180 
3181   if (df_regs_ever_live_p (MB_ABI_SUB_RETURN_ADDR_REGNUM) || profile_flag)
3182     return 0;
3183 
3184   if (current_frame_info.initialized)
3185     return current_frame_info.total_size == 0;
3186 
3187   return compute_frame_size (get_frame_size ()) == 0;
3188 }
3189 
3190 /* Implement TARGET_SECONDARY_RELOAD.  */
3191 
3192 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)3193 microblaze_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x ATTRIBUTE_UNUSED,
3194 			     reg_class_t rclass, machine_mode mode ATTRIBUTE_UNUSED,
3195 			     secondary_reload_info *sri ATTRIBUTE_UNUSED)
3196 {
3197   if (rclass == ST_REGS)
3198     return GR_REGS;
3199 
3200   return NO_REGS;
3201 }
3202 
3203 static void
microblaze_globalize_label(FILE * stream,const char * name)3204 microblaze_globalize_label (FILE * stream, const char *name)
3205 {
3206   fputs ("\t.globl\t", stream);
3207   if (microblaze_is_interrupt_variant ())
3208     {
3209       if (interrupt_handler && strcmp (name, INTERRUPT_HANDLER_NAME))
3210         fputs (INTERRUPT_HANDLER_NAME, stream);
3211       else if (break_handler && strcmp (name, BREAK_HANDLER_NAME))
3212         fputs (BREAK_HANDLER_NAME, stream);
3213       else if (fast_interrupt && strcmp (name, FAST_INTERRUPT_NAME))
3214         fputs (FAST_INTERRUPT_NAME, stream);
3215       fputs ("\n\t.globl\t", stream);
3216     }
3217   assemble_name (stream, name);
3218   fputs ("\n", stream);
3219 }
3220 
3221 /* Returns true if decl should be placed into a "small data" section.  */
3222 static bool
microblaze_elf_in_small_data_p(const_tree decl)3223 microblaze_elf_in_small_data_p (const_tree decl)
3224 {
3225   HOST_WIDE_INT size;
3226 
3227   if (!TARGET_XLGPOPT)
3228     return false;
3229 
3230   /* We want to merge strings, so we never consider them small data.  */
3231   if (TREE_CODE (decl) == STRING_CST)
3232     return false;
3233 
3234   /* Functions are never in the small data area.  */
3235   if (TREE_CODE (decl) == FUNCTION_DECL)
3236     return false;
3237 
3238   if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl))
3239     {
3240       const char *section = DECL_SECTION_NAME (decl);
3241       if (strcmp (section, ".sdata") == 0
3242 	  || strcmp (section, ".sdata2") == 0
3243 	  || strcmp (section, ".sbss") == 0
3244 	  || strcmp (section, ".sbss2") == 0)
3245 	return true;
3246     }
3247 
3248   size = int_size_in_bytes (TREE_TYPE (decl));
3249 
3250   return (size > 0 && size <= microblaze_section_threshold);
3251 }
3252 
3253 /* We need to disable address diff vectors in
3254 case of pic data text relative mode.  */
3255 
3256 static bool
microblaze_gen_pic_addr_dif_vec(void)3257 microblaze_gen_pic_addr_dif_vec (void)
3258 {
3259   return (flag_pic && !TARGET_PIC_DATA_TEXT_REL);
3260 }
3261 
3262 static section *
microblaze_select_section(tree decl,int reloc,unsigned HOST_WIDE_INT align)3263 microblaze_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
3264 {
3265   switch (categorize_decl_for_section (decl, reloc))
3266     {
3267     case SECCAT_RODATA_MERGE_STR:
3268     case SECCAT_RODATA_MERGE_STR_INIT:
3269       /* MB binutils have various issues with mergeable string sections and
3270          relaxation/relocation. Currently, turning mergeable sections
3271          into regular readonly sections.  */
3272 
3273       return readonly_data_section;
3274     default:
3275       return default_elf_select_section (decl, reloc, align);
3276     }
3277 }
3278 
3279 /*
3280   Encode info about sections into the RTL based on a symbol's declaration.
3281   The default definition of this hook, default_encode_section_info in
3282   `varasm.c', sets a number of commonly-useful bits in SYMBOL_REF_FLAGS. */
3283 
3284 static void
microblaze_encode_section_info(tree decl,rtx rtl,int first)3285 microblaze_encode_section_info (tree decl, rtx rtl, int first)
3286 {
3287   default_encode_section_info (decl, rtl, first);
3288 }
3289 
3290 static rtx
expand_pic_symbol_ref(machine_mode mode ATTRIBUTE_UNUSED,rtx op)3291 expand_pic_symbol_ref (machine_mode mode ATTRIBUTE_UNUSED, rtx op)
3292 {
3293   rtx result;
3294   bool isFunc = (GET_CODE (op) == SYMBOL_REF
3295 		 && (SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_FUNCTION));
3296   result = (!TARGET_PIC_DATA_TEXT_REL)
3297 	    ? gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), UNSPEC_GOTOFF)
3298 	    : gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), UNSPEC_TEXT);
3299   result = gen_rtx_CONST (Pmode, result);
3300   result = (TARGET_PIC_DATA_TEXT_REL && isFunc)
3301 	    ? gen_rtx_PLUS (Pmode, gen_raw_REG (Pmode,
3302 			    get_base_reg (op)), result)
3303 	    : gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result);
3304   result = (!TARGET_PIC_DATA_TEXT_REL)
3305 	    ? gen_const_mem (Pmode, result) : result;
3306 
3307   return result;
3308 }
3309 
3310 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)3311 microblaze_asm_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
3312         HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
3313         tree function)
3314 {
3315   rtx this_rtx, funexp;
3316   rtx_insn *insn;
3317 
3318   reload_completed = 1;
3319   epilogue_completed = 1;
3320 
3321   /* Mark the end of the (empty) prologue.  */
3322   emit_note (NOTE_INSN_PROLOGUE_END);
3323 
3324   /* Find the "this" pointer.  If the function returns a structure,
3325      the structure return pointer is in MB_ABI_FIRST_ARG_REGNUM.  */
3326   if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
3327     this_rtx = gen_rtx_REG (Pmode, (MB_ABI_FIRST_ARG_REGNUM + 1));
3328   else
3329     this_rtx = gen_rtx_REG (Pmode, MB_ABI_FIRST_ARG_REGNUM);
3330 
3331   /* Apply the constant offset, if required.  */
3332   if (delta)
3333     emit_insn (gen_addsi3 (this_rtx, this_rtx, GEN_INT (delta)));
3334 
3335   /* Apply the offset from the vtable, if required.  */
3336   if (vcall_offset)
3337   {
3338     rtx vcall_offset_rtx = GEN_INT (vcall_offset);
3339     rtx temp1 = gen_rtx_REG (Pmode, MB_ABI_TEMP1_REGNUM);
3340 
3341     emit_move_insn (temp1, gen_rtx_MEM (Pmode, this_rtx));
3342 
3343     rtx loc = gen_rtx_PLUS (Pmode, temp1, vcall_offset_rtx);
3344     emit_move_insn (temp1, gen_rtx_MEM (Pmode, loc));
3345 
3346     emit_insn (gen_addsi3 (this_rtx, this_rtx, temp1));
3347   }
3348 
3349   /* Generate a tail call to the target function.  */
3350   if (!TREE_USED (function))
3351   {
3352     assemble_external (function);
3353     TREE_USED (function) = 1;
3354   }
3355 
3356   funexp = XEXP (DECL_RTL (function), 0);
3357   rtx temp2 = gen_rtx_REG (Pmode, MB_ABI_TEMP2_REGNUM);
3358 
3359   if (flag_pic)
3360     emit_move_insn (temp2, expand_pic_symbol_ref (Pmode, funexp));
3361   else
3362     emit_move_insn (temp2, funexp);
3363 
3364   emit_insn (gen_indirect_jump (temp2));
3365 
3366   /* Run just enough of rest_of_compilation.  This sequence was
3367      "borrowed" from rs6000.c.  */
3368   insn = get_insns ();
3369   shorten_branches (insn);
3370   final_start_function (insn, file, 1);
3371   final (insn, file, 1);
3372   final_end_function ();
3373 
3374   reload_completed = 0;
3375   epilogue_completed = 0;
3376 }
3377 
3378 bool
microblaze_expand_move(machine_mode mode,rtx operands[])3379 microblaze_expand_move (machine_mode mode, rtx operands[])
3380 {
3381   rtx op0, op1;
3382 
3383   op0 = operands[0];
3384   op1 = operands[1];
3385 
3386   if (!register_operand (op0, SImode)
3387       && !register_operand (op1, SImode)
3388       && (GET_CODE (op1) != CONST_INT || INTVAL (op1) != 0))
3389     {
3390       rtx temp = force_reg (SImode, op1);
3391       emit_move_insn (op0, temp);
3392       return true;
3393     }
3394   /* If operands[1] is a constant address invalid for pic, then we need to
3395      handle it just like LEGITIMIZE_ADDRESS does.  */
3396   if (GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF)
3397     {
3398       rtx result;
3399       if (microblaze_tls_symbol_p(op1))
3400 	{
3401 	  result = microblaze_legitimize_tls_address (op1, NULL_RTX);
3402 	  emit_move_insn (op0, result);
3403 	  return true;
3404 	}
3405       else if (flag_pic)
3406 	{
3407 	  if (reload_in_progress)
3408 	    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
3409 	  result = expand_pic_symbol_ref (mode, op1);
3410 
3411 	  if (TARGET_PIC_DATA_TEXT_REL && GET_CODE (op0) == REG
3412 	      && REGNO (op0) >= FIRST_PSEUDO_REGISTER)
3413 	    result = force_reg (SImode, result);
3414 
3415 	  emit_move_insn (op0, result);
3416 	  return true;
3417 	}
3418     }
3419   if (GET_CODE (op1) == PLUS && GET_CODE (XEXP (op1,1)) == CONST)
3420     {
3421       rtx p0, p1, result, temp;
3422 
3423       p0 = XEXP (XEXP (op1,1), 0);
3424 
3425       if (GET_CODE (p0) == PLUS)
3426 	{
3427 	  p1 = XEXP (p0, 1);
3428 	  p0 = XEXP (p0, 0);
3429 	}
3430 
3431       if (GET_CODE (p0) == UNSPEC && GET_CODE (p1) == CONST_INT
3432 	  && flag_pic && TARGET_PIC_DATA_TEXT_REL)
3433 	{
3434 	  result = gen_rtx_CONST (Pmode, p0);
3435 	  result = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result);
3436 	  temp = force_reg (SImode, result);
3437 	  emit_move_insn (op0, gen_rtx_PLUS (SImode, temp, p1));
3438 	  return true;
3439 	}
3440     }
3441   /* Handle Case of (const (plus symbol const_int)).  */
3442   if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1,0)) == PLUS)
3443     {
3444       rtx p0, p1;
3445 
3446       p0 = XEXP (XEXP (op1, 0), 0);
3447       p1 = XEXP (XEXP (op1, 0), 1);
3448 
3449       if ((GET_CODE (p1) == CONST_INT)
3450 	  && ((GET_CODE (p0) == UNSPEC)
3451 	      || ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
3452 	          && (flag_pic == 2 || microblaze_tls_symbol_p (p0)
3453 		      || !SMALL_INT (p1)))))
3454 	{
3455 	  rtx temp = force_reg (SImode, p0);
3456 	  rtx temp2 = p1;
3457 
3458 	  if (flag_pic && reload_in_progress)
3459 	    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
3460 	  emit_move_insn (op0, gen_rtx_PLUS (SImode, temp, temp2));
3461 	  return true;
3462 	}
3463     }
3464   return false;
3465 }
3466 
3467 /* Expand shift operations.  */
3468 int
microblaze_expand_shift(rtx operands[])3469 microblaze_expand_shift (rtx operands[])
3470 {
3471   gcc_assert ((GET_CODE (operands[2]) == CONST_INT)
3472 	      || (GET_CODE (operands[2]) == REG)
3473 	      || (GET_CODE (operands[2]) == SUBREG));
3474 
3475   /* Shift by one -- generate pattern.  */
3476   if ((GET_CODE (operands[2]) == CONST_INT) && (INTVAL (operands[2]) == 1))
3477     return 0;
3478 
3479   /* Have barrel shifter and shift > 1: use it.  */
3480   if (TARGET_BARREL_SHIFT)
3481     return 0;
3482 
3483   gcc_assert ((GET_CODE (operands[0]) == REG)
3484 	      || (GET_CODE (operands[0]) == SUBREG)
3485 	      || (GET_CODE (operands[1]) == REG)
3486 	      || (GET_CODE (operands[1]) == SUBREG));
3487 
3488   /* Shift by zero -- copy regs if necessary.  */
3489   if (operands[2] == const0_rtx
3490       && !rtx_equal_p (operands[0], operands[1]))
3491     {
3492       emit_insn (gen_movsi (operands[0], operands[1]));
3493       return 1;
3494     }
3495 
3496   return 0;
3497 }
3498 
3499 /* Return an RTX indicating where the return address to the
3500    calling function can be found.  */
3501 rtx
microblaze_return_addr(int count,rtx frame ATTRIBUTE_UNUSED)3502 microblaze_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
3503 {
3504   if (count != 0)
3505     return NULL_RTX;
3506 
3507   return get_hard_reg_initial_val (Pmode,
3508                                    MB_ABI_SUB_RETURN_ADDR_REGNUM);
3509 }
3510 
3511 void
microblaze_eh_return(rtx op0)3512 microblaze_eh_return (rtx op0)
3513 {
3514   emit_insn (gen_movsi (gen_rtx_MEM (Pmode, stack_pointer_rtx), op0));
3515 }
3516 
3517 /* Queue an .ident string in the queue of top-level asm statements.
3518    If the string size is below the threshold, put it into .sdata2.
3519    If the front-end is done, we must be being called from toplev.c.
3520    In that case, do nothing.  */
3521 void
microblaze_asm_output_ident(const char * string)3522 microblaze_asm_output_ident (const char *string)
3523 {
3524   const char *section_asm_op;
3525   int size;
3526   char *buf;
3527 
3528   if (symtab->state != PARSING)
3529     return;
3530 
3531   size = strlen (string) + 1;
3532   if (size <= microblaze_section_threshold)
3533     section_asm_op = SDATA2_SECTION_ASM_OP;
3534   else
3535     section_asm_op = READONLY_DATA_SECTION_ASM_OP;
3536 
3537   buf = ACONCAT (("\t.pushsection", section_asm_op,
3538                   "\n\t.ascii \"", string, "\\0\"\n",
3539                   "\t.popsection\n", NULL));
3540   symtab->finalize_toplevel_asm (build_string (strlen (buf), buf));
3541 }
3542 
3543 static void
microblaze_elf_asm_init_sections(void)3544 microblaze_elf_asm_init_sections (void)
3545 {
3546   sdata2_section
3547     = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
3548 			   SDATA2_SECTION_ASM_OP);
3549 }
3550 
3551 /*  Generate assembler code for constant parts of a trampoline.  */
3552 
3553 static void
microblaze_asm_trampoline_template(FILE * f)3554 microblaze_asm_trampoline_template (FILE *f)
3555 {
3556   fprintf (f, "\tmfs r18, rpc\n");
3557   fprintf (f, "\tlwi r3, r18, 16\n");
3558   fprintf (f, "\tlwi r18, r18, 20\n");
3559   fprintf (f, "\tbra r18\n");
3560   /* fprintf (f, "\t.word\t0x00000000\t\t# <function address>\n");  */
3561   /* fprintf (f, "\t.word\t0x00000000\t\t# <static chain value>\n");  */
3562 }
3563 
3564 /* Implement TARGET_TRAMPOLINE_INIT.  */
3565 
3566 static void
microblaze_trampoline_init(rtx m_tramp,tree fndecl,rtx chain_value)3567 microblaze_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
3568 {
3569   rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
3570   rtx mem;
3571 
3572   emit_block_move (m_tramp, assemble_trampoline_template (),
3573 		   GEN_INT (6*UNITS_PER_WORD), BLOCK_OP_NORMAL);
3574 
3575   mem = adjust_address (m_tramp, SImode, 16);
3576   emit_move_insn (mem, chain_value);
3577   mem = adjust_address (m_tramp, SImode, 20);
3578   emit_move_insn (mem, fnaddr);
3579 }
3580 
3581 /* Generate conditional branch -- first, generate test condition,
3582    second, generate correct branch instruction.  */
3583 
3584 void
microblaze_expand_conditional_branch(machine_mode mode,rtx operands[])3585 microblaze_expand_conditional_branch (machine_mode mode, rtx operands[])
3586 {
3587   enum rtx_code code = GET_CODE (operands[0]);
3588   rtx cmp_op0 = operands[1];
3589   rtx cmp_op1 = operands[2];
3590   rtx label1 = operands[3];
3591   rtx comp_reg = gen_reg_rtx (SImode);
3592   rtx condition;
3593 
3594   gcc_assert ((GET_CODE (cmp_op0) == REG) || (GET_CODE (cmp_op0) == SUBREG));
3595 
3596   /* If comparing against zero, just test source reg.  */
3597   if (cmp_op1 == const0_rtx)
3598     {
3599       comp_reg = cmp_op0;
3600       condition = gen_rtx_fmt_ee (signed_condition (code), SImode, comp_reg, const0_rtx);
3601       emit_jump_insn (gen_condjump (condition, label1));
3602     }
3603 
3604   else if (code == EQ || code == NE)
3605     {
3606       /* Use xor for equal/not-equal comparison.  */
3607       emit_insn (gen_xorsi3 (comp_reg, cmp_op0, cmp_op1));
3608       condition = gen_rtx_fmt_ee (signed_condition (code), SImode, comp_reg, const0_rtx);
3609       emit_jump_insn (gen_condjump (condition, label1));
3610     }
3611   else
3612     {
3613       /* Generate compare and branch in single instruction. */
3614       cmp_op1 = force_reg (mode, cmp_op1);
3615       condition = gen_rtx_fmt_ee (code, mode, cmp_op0, cmp_op1);
3616       emit_jump_insn (gen_branch_compare(condition, cmp_op0, cmp_op1, label1));
3617     }
3618 }
3619 
3620 void
microblaze_expand_conditional_branch_reg(machine_mode mode,rtx operands[])3621 microblaze_expand_conditional_branch_reg (machine_mode mode, rtx operands[])
3622 {
3623   enum rtx_code code = GET_CODE (operands[0]);
3624   rtx cmp_op0 = operands[1];
3625   rtx cmp_op1 = operands[2];
3626   rtx label1 = operands[3];
3627   rtx comp_reg = gen_reg_rtx (SImode);
3628   rtx condition;
3629 
3630   gcc_assert ((GET_CODE (cmp_op0) == REG)
3631                || (GET_CODE (cmp_op0) == SUBREG));
3632 
3633   /* If comparing against zero, just test source reg.  */
3634   if (cmp_op1 == const0_rtx)
3635     {
3636       comp_reg = cmp_op0;
3637       condition = gen_rtx_fmt_ee (signed_condition (code),
3638                                   SImode, comp_reg, const0_rtx);
3639       emit_jump_insn (gen_condjump (condition, label1));
3640     }
3641   else if (code == EQ)
3642     {
3643       emit_insn (gen_seq_internal_pat (comp_reg,
3644                                        cmp_op0, cmp_op1));
3645       condition = gen_rtx_EQ (SImode, comp_reg, const0_rtx);
3646       emit_jump_insn (gen_condjump (condition, label1));
3647     }
3648   else if (code == NE)
3649     {
3650       emit_insn (gen_sne_internal_pat (comp_reg, cmp_op0,
3651                                        cmp_op1));
3652       condition = gen_rtx_NE (SImode, comp_reg, const0_rtx);
3653       emit_jump_insn (gen_condjump (condition, label1));
3654     }
3655   else
3656     {
3657       /* Generate compare and branch in single instruction. */
3658       cmp_op1 = force_reg (mode, cmp_op1);
3659       condition = gen_rtx_fmt_ee (code, mode, cmp_op0, cmp_op1);
3660       emit_jump_insn (gen_branch_compare (condition, cmp_op0,
3661                                          cmp_op1, label1));
3662     }
3663 }
3664 
3665 void
microblaze_expand_conditional_branch_sf(rtx operands[])3666 microblaze_expand_conditional_branch_sf (rtx operands[])
3667 {
3668   rtx condition;
3669   rtx cmp_op0 = XEXP (operands[0], 0);
3670   rtx cmp_op1 = XEXP (operands[0], 1);
3671   rtx comp_reg = gen_reg_rtx (SImode);
3672 
3673   emit_insn (gen_cstoresf4 (comp_reg, operands[0], cmp_op0, cmp_op1));
3674   condition = gen_rtx_NE (SImode, comp_reg, const0_rtx);
3675   emit_jump_insn (gen_condjump (condition, operands[3]));
3676 }
3677 
3678 /* Implement TARGET_FRAME_POINTER_REQUIRED.  */
3679 
3680 static bool
microblaze_frame_pointer_required(void)3681 microblaze_frame_pointer_required (void)
3682 {
3683   /* If the function contains dynamic stack allocations, we need to
3684      use the frame pointer to access the static parts of the frame.  */
3685   if (cfun->calls_alloca)
3686     return true;
3687   return false;
3688 }
3689 
3690 void
microblaze_expand_divide(rtx operands[])3691 microblaze_expand_divide (rtx operands[])
3692 {
3693   /* Table lookup software divides. Works for all (nr/dr) where (0 <= nr,dr <= 15).  */
3694 
3695   rtx regt1 = gen_reg_rtx (SImode);
3696   rtx reg18 = gen_rtx_REG (SImode, R_TMP);
3697   rtx regqi = gen_reg_rtx (QImode);
3698   rtx_code_label *div_label = gen_label_rtx ();
3699   rtx_code_label *div_end_label = gen_label_rtx ();
3700   rtx div_table_rtx = gen_rtx_SYMBOL_REF (QImode,"_divsi3_table");
3701   rtx mem_rtx;
3702   rtx ret;
3703   rtx_insn *jump, *cjump, *insn;
3704 
3705   insn = emit_insn (gen_iorsi3 (regt1, operands[1], operands[2]));
3706   cjump = emit_jump_insn_after (gen_cbranchsi4 (
3707 					gen_rtx_GTU (SImode, regt1, GEN_INT (15)),
3708 					regt1, GEN_INT (15), div_label), insn);
3709   LABEL_NUSES (div_label) = 1;
3710   JUMP_LABEL (cjump) = div_label;
3711   emit_insn (gen_rtx_CLOBBER (SImode, reg18));
3712 
3713   emit_insn (gen_ashlsi3_bshift (regt1, operands[1], GEN_INT(4)));
3714   emit_insn (gen_addsi3 (regt1, regt1, operands[2]));
3715   mem_rtx = gen_rtx_MEM (QImode,
3716                             gen_rtx_PLUS (Pmode, regt1, div_table_rtx));
3717 
3718   insn = emit_insn (gen_movqi (regqi, mem_rtx));
3719   insn = emit_insn (gen_movsi (operands[0], gen_rtx_SUBREG (SImode, regqi, 0)));
3720   jump = emit_jump_insn_after (gen_jump (div_end_label), insn);
3721   JUMP_LABEL (jump) = div_end_label;
3722   LABEL_NUSES (div_end_label) = 1;
3723   emit_barrier ();
3724 
3725   emit_label (div_label);
3726   ret = emit_library_call_value (gen_rtx_SYMBOL_REF (Pmode, "__divsi3"),
3727 				 operands[0], LCT_NORMAL,
3728 				 GET_MODE (operands[0]),
3729 				 operands[1], GET_MODE (operands[1]),
3730 				 operands[2], GET_MODE (operands[2]));
3731   if (ret != operands[0])
3732                 emit_move_insn (operands[0], ret);
3733 
3734   emit_label (div_end_label);
3735   emit_insn (gen_blockage ());
3736 }
3737 
3738 /* Implement TARGET_FUNCTION_VALUE.  */
3739 static rtx
microblaze_function_value(const_tree valtype,const_tree func ATTRIBUTE_UNUSED,bool outgoing ATTRIBUTE_UNUSED)3740 microblaze_function_value (const_tree valtype,
3741 			   const_tree func ATTRIBUTE_UNUSED,
3742 			   bool outgoing ATTRIBUTE_UNUSED)
3743 {
3744   return LIBCALL_VALUE (TYPE_MODE (valtype));
3745 }
3746 
3747 /* Implement TARGET_SCHED_ADJUST_COST.  */
3748 static int
microblaze_adjust_cost(rtx_insn *,int dep_type,rtx_insn *,int cost,unsigned int)3749 microblaze_adjust_cost (rtx_insn *, int dep_type, rtx_insn *, int cost,
3750 			unsigned int)
3751 {
3752   if (dep_type == REG_DEP_OUTPUT || dep_type == 0)
3753     return cost;
3754   return 0;
3755 }
3756 
3757 /* Implement TARGET_LEGITIMATE_CONSTANT_P.
3758 
3759    At present, GAS doesn't understand li.[sd], so don't allow it
3760    to be generated at present.  */
3761 static bool
microblaze_legitimate_constant_p(machine_mode mode ATTRIBUTE_UNUSED,rtx x)3762 microblaze_legitimate_constant_p (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
3763 {
3764 
3765   if (microblaze_cannot_force_const_mem(mode, x))
3766         return false;
3767 
3768   if (GET_CODE (x) == CONST_DOUBLE)
3769     {
3770       return microblaze_const_double_ok (x, GET_MODE (x));
3771     }
3772 
3773    /* Handle Case of (const (plus unspec const_int)).  */
3774    if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
3775      {
3776         rtx p0, p1;
3777 
3778         p0 = XEXP (XEXP (x, 0), 0);
3779         p1 = XEXP (XEXP (x, 0), 1);
3780 
3781         if (GET_CODE(p1) == CONST_INT)
3782           {
3783             /* Const offset from UNSPEC is not supported.  */
3784             if ((GET_CODE (p0) == UNSPEC))
3785               return false;
3786 
3787             if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
3788                  && (microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
3789               return false;
3790           }
3791       }
3792 
3793   return true;
3794 }
3795 
3796 static rtx
get_branch_target(rtx branch)3797 get_branch_target (rtx branch)
3798 {
3799   if (CALL_P (branch))
3800     {
3801       rtx call;
3802 
3803       call = XVECEXP (PATTERN (branch), 0, 0);
3804       if (GET_CODE (call) == SET)
3805         call = SET_SRC (call);
3806       if (GET_CODE (call) != CALL)
3807         abort ();
3808       return XEXP (XEXP (call, 0), 0);
3809     }
3810 
3811   return NULL_RTX;
3812 }
3813 
3814 /* Heuristics to identify where to insert at the
3815    fall through path of the caller function. If there
3816    is a call after the caller branch delay slot then
3817    we dont generate the instruction prefetch instruction.
3818 
3819    Scan up to 32 instructions after the call and checks
3820    for the JUMP and call instruction . If there is a call
3821    or JUMP instruction in the range of 32 instruction "wic"
3822    instruction wont be generated. Otherwise insert the "wic"
3823    instruction in the fall through of the call instruction
3824    four instruction after the call. before_4 is used for
3825    the position to insert "wic" instructions. before_16 is
3826    used to check for call and JUMP instruction for first
3827    15 insns.  */
3828 
3829 static void
insert_wic_for_ilb_runout(rtx_insn * first)3830 insert_wic_for_ilb_runout (rtx_insn *first)
3831 {
3832   rtx_insn *insn;
3833   rtx_insn *before_4 = 0;
3834   rtx_insn *before_16 = 0;
3835   int addr_offset = 0;
3836   int length;
3837   int wic_addr0 = 128 * 4;
3838 
3839   int first_addr = INSN_ADDRESSES (INSN_UID (first));
3840 
3841   for (insn = first; insn; insn = NEXT_INSN (insn))
3842     if (INSN_P (insn))
3843       {
3844         addr_offset = INSN_ADDRESSES (INSN_UID (insn)) - first_addr;
3845         length = get_attr_length (insn);
3846         if (before_4 == 0 && addr_offset + length >= 4 * 4)
3847           before_4 = insn;
3848 
3849         if (JUMP_P(insn))
3850           return;
3851         if (before_16 == 0 && addr_offset + length >= 14 * 4)
3852           before_16 = insn;
3853         if (CALL_P (insn) || tablejump_p (insn, 0, 0))
3854           return;
3855         if (addr_offset + length >= 32 * 4)
3856           {
3857             gcc_assert (before_4 && before_16);
3858             if (wic_addr0 > 4 * 4)
3859               {
3860                 insn =
3861                   emit_insn_before (gen_iprefetch
3862                                     (gen_int_mode (addr_offset, SImode)),
3863                                     before_4);
3864                 recog_memoized (insn);
3865                 INSN_LOCATION (insn) = INSN_LOCATION (before_4);
3866                 INSN_ADDRESSES_NEW (insn, INSN_ADDRESSES (INSN_UID (before_4)));
3867                 return;
3868               }
3869            }
3870        }
3871 }
3872 
3873 /* Insert instruction prefetch instruction at the fall
3874    through path of the function call.  */
3875 
3876 static void
insert_wic(void)3877 insert_wic (void)
3878 {
3879   rtx_insn *insn;
3880   int i;
3881   basic_block bb, prev = 0;
3882   rtx branch_target = 0;
3883 
3884   shorten_branches (get_insns ());
3885 
3886   for (i = 0; i < n_basic_blocks_for_fn (cfun) - 1; i++)
3887      {
3888        edge e;
3889        edge_iterator ei;
3890        bool simple_loop = false;
3891 
3892        bb = BASIC_BLOCK_FOR_FN (cfun, i);
3893 
3894        if (bb == NULL)
3895          continue;
3896 
3897        if ((prev != 0) && (prev != bb))
3898          continue;
3899        else
3900          prev = 0;
3901 
3902        FOR_EACH_EDGE (e, ei, bb->preds)
3903          if (e->src == bb)
3904            {
3905              simple_loop = true;
3906              prev= e->dest;
3907              break;
3908            }
3909 
3910        for (insn = BB_END (bb); insn; insn = PREV_INSN (insn))
3911           {
3912             if (INSN_P (insn) && !simple_loop
3913                && CALL_P(insn))
3914               {
3915                 if ((branch_target = get_branch_target (insn)))
3916                   insert_wic_for_ilb_runout (
3917                     next_active_insn (next_active_insn (insn)));
3918               }
3919               if (insn == BB_HEAD (bb))
3920                 break;
3921            }
3922       }
3923 }
3924 
3925 /* The reorg function defined through the macro
3926    TARGET_MACHINE_DEPENDENT_REORG.  */
3927 
3928 static void
microblaze_machine_dependent_reorg(void)3929 microblaze_machine_dependent_reorg (void)
3930 {
3931   if (TARGET_PREFETCH)
3932     {
3933       compute_bb_for_insn ();
3934       loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
3935       shorten_branches (get_insns ());
3936       insert_wic ();
3937       loop_optimizer_finalize ();
3938       free_bb_for_insn ();
3939       return;
3940     }
3941 }
3942 
3943 /* Implement TARGET_CONSTANT_ALIGNMENT.  */
3944 
3945 static HOST_WIDE_INT
microblaze_constant_alignment(const_tree exp,HOST_WIDE_INT align)3946 microblaze_constant_alignment (const_tree exp, HOST_WIDE_INT align)
3947 {
3948   if (TREE_CODE (exp) == STRING_CST || TREE_CODE (exp) == CONSTRUCTOR)
3949     return MAX (align, BITS_PER_WORD);
3950   return align;
3951 }
3952 
3953 /* Implement TARGET_STARTING_FRAME_OFFSET.  */
3954 
3955 static HOST_WIDE_INT
microblaze_starting_frame_offset(void)3956 microblaze_starting_frame_offset (void)
3957 {
3958   return (crtl->outgoing_args_size + FIRST_PARM_OFFSET(FNDECL));
3959 }
3960 
3961 #undef TARGET_ENCODE_SECTION_INFO
3962 #define TARGET_ENCODE_SECTION_INFO      microblaze_encode_section_info
3963 
3964 #undef TARGET_ASM_GLOBALIZE_LABEL
3965 #define TARGET_ASM_GLOBALIZE_LABEL      microblaze_globalize_label
3966 
3967 #undef TARGET_ASM_FUNCTION_PROLOGUE
3968 #define TARGET_ASM_FUNCTION_PROLOGUE    microblaze_function_prologue
3969 
3970 #undef TARGET_ASM_FUNCTION_EPILOGUE
3971 #define TARGET_ASM_FUNCTION_EPILOGUE    microblaze_function_epilogue
3972 
3973 #undef TARGET_RTX_COSTS
3974 #define TARGET_RTX_COSTS                microblaze_rtx_costs
3975 
3976 #undef TARGET_CANNOT_FORCE_CONST_MEM
3977 #define TARGET_CANNOT_FORCE_CONST_MEM   microblaze_cannot_force_const_mem
3978 
3979 #undef TARGET_ADDRESS_COST
3980 #define TARGET_ADDRESS_COST             microblaze_address_cost
3981 
3982 #undef TARGET_ATTRIBUTE_TABLE
3983 #define TARGET_ATTRIBUTE_TABLE          microblaze_attribute_table
3984 
3985 #undef TARGET_IN_SMALL_DATA_P
3986 #define TARGET_IN_SMALL_DATA_P          microblaze_elf_in_small_data_p
3987 
3988 #undef TARGET_ASM_SELECT_SECTION
3989 #define TARGET_ASM_SELECT_SECTION       microblaze_select_section
3990 
3991 #undef TARGET_HAVE_SRODATA_SECTION
3992 #define TARGET_HAVE_SRODATA_SECTION     true
3993 
3994 #undef TARGET_ASM_FUNCTION_END_PROLOGUE
3995 #define TARGET_ASM_FUNCTION_END_PROLOGUE \
3996                                         microblaze_function_end_prologue
3997 
3998 #undef TARGET_ARG_PARTIAL_BYTES
3999 #define TARGET_ARG_PARTIAL_BYTES	function_arg_partial_bytes
4000 
4001 #undef TARGET_FUNCTION_ARG
4002 #define TARGET_FUNCTION_ARG		microblaze_function_arg
4003 
4004 #undef TARGET_FUNCTION_ARG_ADVANCE
4005 #define TARGET_FUNCTION_ARG_ADVANCE	microblaze_function_arg_advance
4006 
4007 #undef TARGET_CAN_ELIMINATE
4008 #define TARGET_CAN_ELIMINATE 		microblaze_can_eliminate
4009 
4010 #undef TARGET_LEGITIMIZE_ADDRESS
4011 #define TARGET_LEGITIMIZE_ADDRESS 	microblaze_legitimize_address
4012 
4013 #undef TARGET_LEGITIMATE_ADDRESS_P
4014 #define TARGET_LEGITIMATE_ADDRESS_P 	microblaze_legitimate_address_p
4015 
4016 #undef TARGET_LRA_P
4017 #define TARGET_LRA_P hook_bool_void_false
4018 
4019 #undef TARGET_FRAME_POINTER_REQUIRED
4020 #define TARGET_FRAME_POINTER_REQUIRED	microblaze_frame_pointer_required
4021 
4022 #undef  TARGET_ASM_TRAMPOLINE_TEMPLATE
4023 #define TARGET_ASM_TRAMPOLINE_TEMPLATE	microblaze_asm_trampoline_template
4024 
4025 #undef  TARGET_TRAMPOLINE_INIT
4026 #define TARGET_TRAMPOLINE_INIT		microblaze_trampoline_init
4027 
4028 #undef  TARGET_PROMOTE_FUNCTION_MODE
4029 #define TARGET_PROMOTE_FUNCTION_MODE 	default_promote_function_mode_always_promote
4030 
4031 #undef TARGET_FUNCTION_VALUE
4032 #define TARGET_FUNCTION_VALUE		microblaze_function_value
4033 
4034 #undef TARGET_SECONDARY_RELOAD
4035 #define TARGET_SECONDARY_RELOAD		microblaze_secondary_reload
4036 
4037 #undef  TARGET_ASM_OUTPUT_MI_THUNK
4038 #define TARGET_ASM_OUTPUT_MI_THUNK      microblaze_asm_output_mi_thunk
4039 
4040 #undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
4041 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK  hook_bool_const_tree_hwi_hwi_const_tree_true
4042 
4043 #undef TARGET_SCHED_ADJUST_COST
4044 #define TARGET_SCHED_ADJUST_COST	microblaze_adjust_cost
4045 
4046 #undef TARGET_ASM_INIT_SECTIONS
4047 #define TARGET_ASM_INIT_SECTIONS	microblaze_elf_asm_init_sections
4048 
4049 #undef  TARGET_OPTION_OVERRIDE
4050 #define TARGET_OPTION_OVERRIDE		microblaze_option_override
4051 
4052 #undef TARGET_LEGITIMATE_CONSTANT_P
4053 #define TARGET_LEGITIMATE_CONSTANT_P microblaze_legitimate_constant_p
4054 
4055 #undef  TARGET_ASM_GENERATE_PIC_ADDR_DIFF_VEC
4056 #define TARGET_ASM_GENERATE_PIC_ADDR_DIFF_VEC	microblaze_gen_pic_addr_dif_vec
4057 
4058 #undef TARGET_MACHINE_DEPENDENT_REORG
4059 #define TARGET_MACHINE_DEPENDENT_REORG microblaze_machine_dependent_reorg
4060 
4061 #undef TARGET_HARD_REGNO_MODE_OK
4062 #define TARGET_HARD_REGNO_MODE_OK microblaze_hard_regno_mode_ok
4063 
4064 #undef TARGET_MODES_TIEABLE_P
4065 #define TARGET_MODES_TIEABLE_P microblaze_modes_tieable_p
4066 
4067 #undef TARGET_CONSTANT_ALIGNMENT
4068 #define TARGET_CONSTANT_ALIGNMENT microblaze_constant_alignment
4069 
4070 #undef TARGET_STARTING_FRAME_OFFSET
4071 #define TARGET_STARTING_FRAME_OFFSET microblaze_starting_frame_offset
4072 
4073 struct gcc_target targetm = TARGET_INITIALIZER;
4074 
4075 #include "gt-microblaze.h"
4076