xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/config/moxie/moxie.c (revision fdd524d4ccd2bb0c6f67401e938dabf773eb0372)
1 /* Target Code for moxie
2    Copyright (C) 2008-2013 Free Software Foundation, Inc.
3    Contributed by Anthony Green.
4 
5    This file is part of GCC.
6 
7    GCC is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published
9    by the Free Software Foundation; either version 3, or (at your
10    option) any later version.
11 
12    GCC is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15    License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with GCC; see the file COPYING3.  If not see
19    <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tm.h"
25 #include "rtl.h"
26 #include "regs.h"
27 #include "hard-reg-set.h"
28 #include "insn-config.h"
29 #include "conditions.h"
30 #include "insn-flags.h"
31 #include "output.h"
32 #include "insn-attr.h"
33 #include "flags.h"
34 #include "recog.h"
35 #include "reload.h"
36 #include "diagnostic-core.h"
37 #include "obstack.h"
38 #include "tree.h"
39 #include "expr.h"
40 #include "optabs.h"
41 #include "except.h"
42 #include "function.h"
43 #include "ggc.h"
44 #include "target.h"
45 #include "target-def.h"
46 #include "tm_p.h"
47 #include "langhooks.h"
48 #include "df.h"
49 
50 #define LOSE_AND_RETURN(msgid, x)		\
51   do						\
52     {						\
53       moxie_operand_lossage (msgid, x);		\
54       return;					\
55     } while (0)
56 
57 /* Worker function for TARGET_RETURN_IN_MEMORY.  */
58 
59 static bool
60 moxie_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
61 {
62   const HOST_WIDE_INT size = int_size_in_bytes (type);
63   return (size == -1 || size > 2 * UNITS_PER_WORD);
64 }
65 
66 /* Define how to find the value returned by a function.
67    VALTYPE is the data type of the value (as a tree).
68    If the precise function being called is known, FUNC is its
69    FUNCTION_DECL; otherwise, FUNC is 0.
70 
71    We always return values in register $r0 for moxie.  */
72 
73 static rtx
74 moxie_function_value (const_tree valtype,
75 		      const_tree fntype_or_decl ATTRIBUTE_UNUSED,
76 		      bool outgoing ATTRIBUTE_UNUSED)
77 {
78   return gen_rtx_REG (TYPE_MODE (valtype), MOXIE_R0);
79 }
80 
81 /* Define how to find the value returned by a library function.
82 
83    We always return values in register $r0 for moxie.  */
84 
85 static rtx
86 moxie_libcall_value (enum machine_mode mode,
87                      const_rtx fun ATTRIBUTE_UNUSED)
88 {
89   return gen_rtx_REG (mode, MOXIE_R0);
90 }
91 
92 /* Handle TARGET_FUNCTION_VALUE_REGNO_P.
93 
94    We always return values in register $r0 for moxie.  */
95 
96 static bool
97 moxie_function_value_regno_p (const unsigned int regno)
98 {
99   return (regno == MOXIE_R0);
100 }
101 
102 /* Emit an error message when we're in an asm, and a fatal error for
103    "normal" insns.  Formatted output isn't easily implemented, since we
104    use output_operand_lossage to output the actual message and handle the
105    categorization of the error.  */
106 
107 static void
108 moxie_operand_lossage (const char *msgid, rtx op)
109 {
110   debug_rtx (op);
111   output_operand_lossage ("%s", msgid);
112 }
113 
114 /* The PRINT_OPERAND_ADDRESS worker.  */
115 
116 void
117 moxie_print_operand_address (FILE *file, rtx x)
118 {
119   switch (GET_CODE (x))
120     {
121     case REG:
122       fprintf (file, "(%s)", reg_names[REGNO (x)]);
123       break;
124 
125     case PLUS:
126       switch (GET_CODE (XEXP (x, 1)))
127 	{
128 	case CONST_INT:
129 	  fprintf (file, "%ld(%s)",
130 		   INTVAL(XEXP (x, 1)), reg_names[REGNO (XEXP (x, 0))]);
131 	  break;
132 	case SYMBOL_REF:
133 	  output_addr_const (file, XEXP (x, 1));
134 	  fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]);
135 	  break;
136 	case CONST:
137 	  {
138 	    rtx plus = XEXP (XEXP (x, 1), 0);
139 	    if (GET_CODE (XEXP (plus, 0)) == SYMBOL_REF
140 		&& CONST_INT_P (XEXP (plus, 1)))
141 	      {
142 		output_addr_const(file, XEXP (plus, 0));
143 		fprintf (file,"+%ld(%s)", INTVAL (XEXP (plus, 1)),
144 			 reg_names[REGNO (XEXP (x, 0))]);
145 	      }
146 	    else
147 	      abort();
148 	  }
149 	  break;
150 	default:
151 	  abort();
152 	}
153       break;
154 
155     default:
156       output_addr_const (file, x);
157       break;
158     }
159 }
160 
161 /* The PRINT_OPERAND worker.  */
162 
163 void
164 moxie_print_operand (FILE *file, rtx x, int code)
165 {
166   rtx operand = x;
167 
168   /* New code entries should just be added to the switch below.  If
169      handling is finished, just return.  If handling was just a
170      modification of the operand, the modified operand should be put in
171      "operand", and then do a break to let default handling
172      (zero-modifier) output the operand.  */
173 
174   switch (code)
175     {
176     case 0:
177       /* No code, print as usual.  */
178       break;
179 
180     default:
181       LOSE_AND_RETURN ("invalid operand modifier letter", x);
182     }
183 
184   /* Print an operand as without a modifier letter.  */
185   switch (GET_CODE (operand))
186     {
187     case REG:
188       if (REGNO (operand) > MOXIE_R13)
189 	internal_error ("internal error: bad register: %d", REGNO (operand));
190       fprintf (file, "%s", reg_names[REGNO (operand)]);
191       return;
192 
193     case MEM:
194       output_address (XEXP (operand, 0));
195       return;
196 
197     default:
198       /* No need to handle all strange variants, let output_addr_const
199 	 do it for us.  */
200       if (CONSTANT_P (operand))
201 	{
202 	  output_addr_const (file, operand);
203 	  return;
204 	}
205 
206       LOSE_AND_RETURN ("unexpected operand", x);
207     }
208 }
209 
210 /* Per-function machine data.  */
211 struct GTY(()) machine_function
212  {
213    /* Number of bytes saved on the stack for callee saved registers.  */
214    int callee_saved_reg_size;
215 
216    /* Number of bytes saved on the stack for local variables.  */
217    int local_vars_size;
218 
219    /* The sum of 2 sizes: locals vars and padding byte for saving the
220     * registers.  Used in expand_prologue () and expand_epilogue().  */
221    int size_for_adjusting_sp;
222  };
223 
224 /* Zero initialization is OK for all current fields.  */
225 
226 static struct machine_function *
227 moxie_init_machine_status (void)
228 {
229   return ggc_alloc_cleared_machine_function ();
230 }
231 
232 
233 /* The TARGET_OPTION_OVERRIDE worker.
234    All this curently does is set init_machine_status.  */
235 static void
236 moxie_option_override (void)
237 {
238   /* Set the per-function-data initializer.  */
239   init_machine_status = moxie_init_machine_status;
240 }
241 
242 /* Compute the size of the local area and the size to be adjusted by the
243  * prologue and epilogue.  */
244 
245 static void
246 moxie_compute_frame (void)
247 {
248   /* For aligning the local variables.  */
249   int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
250   int padding_locals;
251   int regno;
252 
253   /* Padding needed for each element of the frame.  */
254   cfun->machine->local_vars_size = get_frame_size ();
255 
256   /* Align to the stack alignment.  */
257   padding_locals = cfun->machine->local_vars_size % stack_alignment;
258   if (padding_locals)
259     padding_locals = stack_alignment - padding_locals;
260 
261   cfun->machine->local_vars_size += padding_locals;
262 
263   cfun->machine->callee_saved_reg_size = 0;
264 
265   /* Save callee-saved registers.  */
266   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
267     if (df_regs_ever_live_p (regno) && (! call_used_regs[regno]))
268       cfun->machine->callee_saved_reg_size += 4;
269 
270   cfun->machine->size_for_adjusting_sp =
271     crtl->args.pretend_args_size
272     + cfun->machine->local_vars_size
273     + (ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0);
274 }
275 
276 void
277 moxie_expand_prologue (void)
278 {
279   int regno;
280   rtx insn;
281 
282   moxie_compute_frame ();
283 
284   if (flag_stack_usage_info)
285     current_function_static_stack_size = cfun->machine->size_for_adjusting_sp;
286 
287   /* Save callee-saved registers.  */
288   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
289     {
290       if (!fixed_regs[regno] && df_regs_ever_live_p (regno) && !call_used_regs[regno])
291 	{
292 	  insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno)));
293 	  RTX_FRAME_RELATED_P (insn) = 1;
294 	}
295     }
296 
297   if (cfun->machine->size_for_adjusting_sp > 0)
298     {
299       int i = cfun->machine->size_for_adjusting_sp;
300       while ((i >= 255) && (i <= 510))
301 	{
302 	  insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
303 					stack_pointer_rtx,
304 					GEN_INT (255)));
305 	  RTX_FRAME_RELATED_P (insn) = 1;
306 	  i -= 255;
307 	}
308       if (i <= 255)
309 	{
310 	  insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
311 					stack_pointer_rtx,
312 					GEN_INT (i)));
313 	  RTX_FRAME_RELATED_P (insn) = 1;
314 	}
315       else
316 	{
317 	  rtx reg = gen_rtx_REG (SImode, MOXIE_R12);
318 	  insn = emit_move_insn (reg, GEN_INT (i));
319 	  RTX_FRAME_RELATED_P (insn) = 1;
320 	  insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
321 					stack_pointer_rtx,
322 					reg));
323 	  RTX_FRAME_RELATED_P (insn) = 1;
324 	}
325     }
326 }
327 
328 void
329 moxie_expand_epilogue (void)
330 {
331   int regno;
332   rtx reg;
333 
334   if (cfun->machine->callee_saved_reg_size != 0)
335     {
336       reg = gen_rtx_REG (Pmode, MOXIE_R12);
337       if (cfun->machine->callee_saved_reg_size <= 255)
338 	{
339 	  emit_move_insn (reg, hard_frame_pointer_rtx);
340 	  emit_insn (gen_subsi3
341 		     (reg, reg,
342 		      GEN_INT (cfun->machine->callee_saved_reg_size)));
343 	}
344       else
345 	{
346 	  emit_move_insn (reg,
347 			  GEN_INT (-cfun->machine->callee_saved_reg_size));
348 	  emit_insn (gen_addsi3 (reg, reg, hard_frame_pointer_rtx));
349 	}
350       for (regno = FIRST_PSEUDO_REGISTER; regno-- > 0; )
351 	if (!fixed_regs[regno] && !call_used_regs[regno]
352 	    && df_regs_ever_live_p (regno))
353 	  {
354 	    rtx preg = gen_rtx_REG (Pmode, regno);
355 	    emit_insn (gen_movsi_pop (reg, preg));
356 	  }
357     }
358 
359   emit_jump_insn (gen_returner ());
360 }
361 
362 /* Implements the macro INITIAL_ELIMINATION_OFFSET, return the OFFSET.  */
363 
364 int
365 moxie_initial_elimination_offset (int from, int to)
366 {
367   int ret;
368 
369   if ((from) == FRAME_POINTER_REGNUM && (to) == HARD_FRAME_POINTER_REGNUM)
370     {
371       /* Compute this since we need to use cfun->machine->local_vars_size.  */
372       moxie_compute_frame ();
373       ret = -cfun->machine->callee_saved_reg_size;
374     }
375   else if ((from) == ARG_POINTER_REGNUM && (to) == HARD_FRAME_POINTER_REGNUM)
376     ret = 0x00;
377   else
378     abort ();
379 
380   return ret;
381 }
382 
383 /* Worker function for TARGET_SETUP_INCOMING_VARARGS.  */
384 
385 static void
386 moxie_setup_incoming_varargs (cumulative_args_t cum_v,
387 			      enum machine_mode mode ATTRIBUTE_UNUSED,
388 			      tree type ATTRIBUTE_UNUSED,
389 			      int *pretend_size, int no_rtl)
390 {
391   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
392   int regno;
393   int regs = 8 - *cum;
394 
395   *pretend_size = regs < 0 ? 0 : GET_MODE_SIZE (SImode) * regs;
396 
397   if (no_rtl)
398     return;
399 
400   for (regno = *cum; regno < 8; regno++)
401     {
402       rtx reg = gen_rtx_REG (SImode, regno);
403       rtx slot = gen_rtx_PLUS (Pmode,
404 			       gen_rtx_REG (SImode, ARG_POINTER_REGNUM),
405 			       GEN_INT (UNITS_PER_WORD * (3 + (regno-2))));
406 
407       emit_move_insn (gen_rtx_MEM (SImode, slot), reg);
408     }
409 }
410 
411 
412 /* Return the fixed registers used for condition codes.  */
413 
414 static bool
415 moxie_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
416 {
417   *p1 = CC_REG;
418   *p2 = INVALID_REGNUM;
419   return true;
420 }
421 
422 /* Return the next register to be used to hold a function argument or
423    NULL_RTX if there's no more space.  */
424 
425 static rtx
426 moxie_function_arg (cumulative_args_t cum_v, enum machine_mode mode,
427 		    const_tree type ATTRIBUTE_UNUSED,
428 		    bool named ATTRIBUTE_UNUSED)
429 {
430   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
431 
432   if (*cum < 8)
433     return gen_rtx_REG (mode, *cum);
434   else
435     return NULL_RTX;
436 }
437 
438 #define MOXIE_FUNCTION_ARG_SIZE(MODE, TYPE)	\
439   ((MODE) != BLKmode ? GET_MODE_SIZE (MODE)	\
440    : (unsigned) int_size_in_bytes (TYPE))
441 
442 static void
443 moxie_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
444 			    const_tree type, bool named ATTRIBUTE_UNUSED)
445 {
446   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
447 
448   *cum = (*cum < MOXIE_R6
449 	  ? *cum + ((3 + MOXIE_FUNCTION_ARG_SIZE (mode, type)) / 4)
450 	  : *cum);
451 }
452 
453 /* Return non-zero if the function argument described by TYPE is to be
454    passed by reference.  */
455 
456 static bool
457 moxie_pass_by_reference (cumulative_args_t cum ATTRIBUTE_UNUSED,
458 			 enum machine_mode mode, const_tree type,
459 			 bool named ATTRIBUTE_UNUSED)
460 {
461   unsigned HOST_WIDE_INT size;
462 
463   if (type)
464     {
465       if (AGGREGATE_TYPE_P (type))
466 	return true;
467       size = int_size_in_bytes (type);
468     }
469   else
470     size = GET_MODE_SIZE (mode);
471 
472   return size > 4*6;
473 }
474 
475 /* Some function arguments will only partially fit in the registers
476    that hold arguments.  Given a new arg, return the number of bytes
477    that fit in argument passing registers.  */
478 
479 static int
480 moxie_arg_partial_bytes (cumulative_args_t cum_v,
481 			 enum machine_mode mode,
482 			 tree type, bool named)
483 {
484   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
485   int bytes_left, size;
486 
487   if (*cum >= 8)
488     return 0;
489 
490   if (moxie_pass_by_reference (cum_v, mode, type, named))
491     size = 4;
492   else if (type)
493     {
494       if (AGGREGATE_TYPE_P (type))
495 	return 0;
496       size = int_size_in_bytes (type);
497     }
498   else
499     size = GET_MODE_SIZE (mode);
500 
501   bytes_left = (4 * 6) - ((*cum - 2) * 4);
502 
503   if (size > bytes_left)
504     return bytes_left;
505   else
506     return 0;
507 }
508 
509 /* Worker function for TARGET_STATIC_CHAIN.  */
510 
511 static rtx
512 moxie_static_chain (const_tree fndecl, bool incoming_p)
513 {
514   rtx addr, mem;
515 
516   if (!DECL_STATIC_CHAIN (fndecl))
517     return NULL;
518 
519   if (incoming_p)
520     addr = plus_constant (Pmode, arg_pointer_rtx, 2 * UNITS_PER_WORD);
521   else
522     addr = plus_constant (Pmode, stack_pointer_rtx, -UNITS_PER_WORD);
523 
524   mem = gen_rtx_MEM (Pmode, addr);
525   MEM_NOTRAP_P (mem) = 1;
526 
527   return mem;
528 }
529 
530 /* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE.  */
531 
532 static void
533 moxie_asm_trampoline_template (FILE *f)
534 {
535   fprintf (f, "\tpush  $sp, $r0\n");
536   fprintf (f, "\tldi.l $r0, 0x0\n");
537   fprintf (f, "\tsto.l 0x8($fp), $r0\n");
538   fprintf (f, "\tpop   $sp, $r0\n");
539   fprintf (f, "\tnop\n");
540   fprintf (f, "\tjmpa  0x0\n");
541 }
542 
543 /* Worker function for TARGET_TRAMPOLINE_INIT.  */
544 
545 static void
546 moxie_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
547 {
548   rtx mem, fnaddr = XEXP (DECL_RTL (fndecl), 0);
549 
550   emit_block_move (m_tramp, assemble_trampoline_template (),
551 		   GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
552 
553   mem = adjust_address (m_tramp, SImode, 4);
554   emit_move_insn (mem, chain_value);
555   mem = adjust_address (m_tramp, SImode, 20);
556   emit_move_insn (mem, fnaddr);
557 }
558 
559 /* The Global `targetm' Variable.  */
560 
561 /* Initialize the GCC target structure.  */
562 
563 #undef  TARGET_PROMOTE_PROTOTYPES
564 #define TARGET_PROMOTE_PROTOTYPES	hook_bool_const_tree_true
565 
566 #undef  TARGET_RETURN_IN_MEMORY
567 #define TARGET_RETURN_IN_MEMORY		moxie_return_in_memory
568 #undef  TARGET_MUST_PASS_IN_STACK
569 #define TARGET_MUST_PASS_IN_STACK	must_pass_in_stack_var_size
570 #undef  TARGET_PASS_BY_REFERENCE
571 #define TARGET_PASS_BY_REFERENCE        moxie_pass_by_reference
572 #undef  TARGET_ARG_PARTIAL_BYTES
573 #define TARGET_ARG_PARTIAL_BYTES        moxie_arg_partial_bytes
574 #undef  TARGET_FUNCTION_ARG
575 #define TARGET_FUNCTION_ARG		moxie_function_arg
576 #undef  TARGET_FUNCTION_ARG_ADVANCE
577 #define TARGET_FUNCTION_ARG_ADVANCE	moxie_function_arg_advance
578 
579 
580 #undef  TARGET_SETUP_INCOMING_VARARGS
581 #define TARGET_SETUP_INCOMING_VARARGS 	moxie_setup_incoming_varargs
582 
583 #undef	TARGET_FIXED_CONDITION_CODE_REGS
584 #define	TARGET_FIXED_CONDITION_CODE_REGS moxie_fixed_condition_code_regs
585 
586 /* Define this to return an RTX representing the place where a
587    function returns or receives a value of data type RET_TYPE, a tree
588    node node representing a data type.  */
589 #undef TARGET_FUNCTION_VALUE
590 #define TARGET_FUNCTION_VALUE moxie_function_value
591 #undef TARGET_LIBCALL_VALUE
592 #define TARGET_LIBCALL_VALUE moxie_libcall_value
593 #undef TARGET_FUNCTION_VALUE_REGNO_P
594 #define TARGET_FUNCTION_VALUE_REGNO_P moxie_function_value_regno_p
595 
596 #undef TARGET_FRAME_POINTER_REQUIRED
597 #define TARGET_FRAME_POINTER_REQUIRED hook_bool_void_true
598 
599 #undef TARGET_STATIC_CHAIN
600 #define TARGET_STATIC_CHAIN moxie_static_chain
601 #undef TARGET_ASM_TRAMPOLINE_TEMPLATE
602 #define TARGET_ASM_TRAMPOLINE_TEMPLATE moxie_asm_trampoline_template
603 #undef TARGET_TRAMPOLINE_INIT
604 #define TARGET_TRAMPOLINE_INIT moxie_trampoline_init
605 
606 #undef TARGET_OPTION_OVERRIDE
607 #define TARGET_OPTION_OVERRIDE moxie_option_override
608 
609 struct gcc_target targetm = TARGET_INITIALIZER;
610 
611 #include "gt-moxie.h"
612