1 /* score-mdaux.c for Sunplus S+CORE processor
2    Copyright (C) 2005 Free Software Foundation, Inc.
3    Contributed by Sunnorth
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 2, 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 COPYING.  If not, write to
19    the Free Software Foundation, 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.  */
21 
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "tm.h"
26 #include <signal.h>
27 #include "rtl.h"
28 #include "regs.h"
29 #include "hard-reg-set.h"
30 #include "real.h"
31 #include "insn-config.h"
32 #include "conditions.h"
33 #include "insn-attr.h"
34 #include "recog.h"
35 #include "toplev.h"
36 #include "output.h"
37 #include "tree.h"
38 #include "function.h"
39 #include "expr.h"
40 #include "optabs.h"
41 #include "flags.h"
42 #include "reload.h"
43 #include "tm_p.h"
44 #include "ggc.h"
45 #include "gstab.h"
46 #include "hashtab.h"
47 #include "debug.h"
48 #include "target.h"
49 #include "target-def.h"
50 #include "integrate.h"
51 #include "langhooks.h"
52 #include "cfglayout.h"
53 #include "score-mdaux.h"
54 
55 #define BITSET_P(VALUE, BIT)      (((VALUE) & (1L << (BIT))) != 0)
56 #define INS_BUF_SZ                100
57 
58 /* Define the information needed to generate branch insns.  This is
59    stored from the compare operation.  */
60 rtx cmp_op0, cmp_op1;
61 
62 static char ins[INS_BUF_SZ + 8];
63 
64 /* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points
65    to the same object as SYMBOL.  */
66 static int
score_offset_within_object_p(rtx symbol,HOST_WIDE_INT offset)67 score_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset)
68 {
69   if (GET_CODE (symbol) != SYMBOL_REF)
70     return 0;
71 
72   if (CONSTANT_POOL_ADDRESS_P (symbol)
73       && offset >= 0
74       && offset < (int)GET_MODE_SIZE (get_pool_mode (symbol)))
75     return 1;
76 
77   if (SYMBOL_REF_DECL (symbol) != 0
78       && offset >= 0
79       && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol))))
80     return 1;
81 
82   return 0;
83 }
84 
85 /* Split X into a base and a constant offset, storing them in *BASE
86    and *OFFSET respectively.  */
87 static void
score_split_const(rtx x,rtx * base,HOST_WIDE_INT * offset)88 score_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset)
89 {
90   *offset = 0;
91 
92   if (GET_CODE (x) == CONST)
93     x = XEXP (x, 0);
94 
95   if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
96     {
97       *offset += INTVAL (XEXP (x, 1));
98       x = XEXP (x, 0);
99     }
100 
101   *base = x;
102 }
103 
104 /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
105 static enum
score_classify_symbol(rtx x)106 score_symbol_type score_classify_symbol (rtx x)
107 {
108   if (GET_CODE (x) == LABEL_REF)
109     return SYMBOL_GENERAL;
110 
111   gcc_assert (GET_CODE (x) == SYMBOL_REF);
112 
113   if (CONSTANT_POOL_ADDRESS_P (x))
114     {
115       if (GET_MODE_SIZE (get_pool_mode (x)) <= SCORE_SDATA_MAX)
116         return SYMBOL_SMALL_DATA;
117       return SYMBOL_GENERAL;
118     }
119   if (SYMBOL_REF_SMALL_P (x))
120     return SYMBOL_SMALL_DATA;
121   return SYMBOL_GENERAL;
122 }
123 
124 /* Return true if the current function must save REGNO.  */
125 static int
score_save_reg_p(unsigned int regno)126 score_save_reg_p (unsigned int regno)
127 {
128   /* Check call-saved registers.  */
129   if (regs_ever_live[regno] && !call_used_regs[regno])
130     return 1;
131 
132   /* We need to save the old frame pointer before setting up a new one.  */
133   if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
134     return 1;
135 
136   /* We need to save the incoming return address if it is ever clobbered
137      within the function.  */
138   if (regno == RA_REGNUM && regs_ever_live[regno])
139     return 1;
140 
141   return 0;
142 }
143 
144 /* Return one word of double-word value OP, taking into account the fixed
145    endianness of certain registers.  HIGH_P is true to select the high part,
146    false to select the low part.  */
147 static rtx
subw(rtx op,int high_p)148 subw (rtx op, int high_p)
149 {
150   unsigned int byte;
151   enum machine_mode mode = GET_MODE (op);
152 
153   if (mode == VOIDmode)
154     mode = DImode;
155 
156   byte = (TARGET_LITTLE_ENDIAN ? high_p : !high_p) ? UNITS_PER_WORD : 0;
157 
158   if (GET_CODE (op) == REG && REGNO (op) == HI_REGNUM)
159     return gen_rtx_REG (SImode, high_p ? HI_REGNUM : LO_REGNUM);
160 
161   if (GET_CODE (op) == MEM)
162     return adjust_address (op, SImode, byte);
163 
164   return simplify_gen_subreg (SImode, op, mode, byte);
165 }
166 
167 struct score_frame_info *
mda_cached_frame(void)168 mda_cached_frame (void)
169 {
170   static struct score_frame_info _frame_info;
171   return &_frame_info;
172 }
173 
174 /* Return the bytes needed to compute the frame pointer from the current
175    stack pointer.  SIZE is the size (in bytes) of the local variables.  */
176 struct score_frame_info *
mda_compute_frame_size(HOST_WIDE_INT size)177 mda_compute_frame_size (HOST_WIDE_INT size)
178 {
179   unsigned int regno;
180   struct score_frame_info *f = mda_cached_frame ();
181 
182   memset (f, 0, sizeof (struct score_frame_info));
183   f->gp_reg_size = 0;
184   f->mask = 0;
185   f->var_size = SCORE_STACK_ALIGN (size);
186   f->args_size = current_function_outgoing_args_size;
187   f->cprestore_size = flag_pic ? UNITS_PER_WORD : 0;
188   if (f->var_size == 0 && current_function_is_leaf)
189     f->args_size = f->cprestore_size = 0;
190 
191   if (f->args_size == 0 && current_function_calls_alloca)
192     f->args_size = UNITS_PER_WORD;
193 
194   f->total_size = f->var_size + f->args_size + f->cprestore_size;
195   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
196     {
197       if (score_save_reg_p (regno))
198         {
199           f->gp_reg_size += GET_MODE_SIZE (SImode);
200           f->mask |= 1 << (regno - GP_REG_FIRST);
201         }
202     }
203 
204   if (current_function_calls_eh_return)
205     {
206       unsigned int i;
207       for (i = 0;; ++i)
208         {
209           regno = EH_RETURN_DATA_REGNO (i);
210           if (regno == INVALID_REGNUM)
211             break;
212           f->gp_reg_size += GET_MODE_SIZE (SImode);
213           f->mask |= 1 << (regno - GP_REG_FIRST);
214         }
215     }
216 
217   f->total_size += f->gp_reg_size;
218   f->num_gp = f->gp_reg_size / UNITS_PER_WORD;
219 
220   if (f->mask)
221     {
222       HOST_WIDE_INT offset;
223       offset = (f->args_size + f->cprestore_size + f->var_size
224                 + f->gp_reg_size - GET_MODE_SIZE (SImode));
225       f->gp_sp_offset = offset;
226     }
227   else
228     f->gp_sp_offset = 0;
229 
230   return f;
231 }
232 
233 /* Generate the prologue instructions for entry into a S+core function.  */
234 void
mdx_prologue(void)235 mdx_prologue (void)
236 {
237 #define EMIT_PL(_rtx)        RTX_FRAME_RELATED_P (_rtx) = 1
238 
239   struct score_frame_info *f = mda_compute_frame_size (get_frame_size ());
240   HOST_WIDE_INT size;
241   int regno;
242 
243   size = f->total_size - f->gp_reg_size;
244 
245   if (flag_pic)
246     emit_insn (gen_cpload ());
247 
248   for (regno = (int) GP_REG_LAST; regno >= (int) GP_REG_FIRST; regno--)
249     {
250       if (BITSET_P (f->mask, regno - GP_REG_FIRST))
251         {
252           rtx mem = gen_rtx_MEM (SImode,
253                                  gen_rtx_PRE_DEC (SImode, stack_pointer_rtx));
254           rtx reg = gen_rtx_REG (SImode, regno);
255           if (!current_function_calls_eh_return)
256             MEM_READONLY_P (mem) = 1;
257           EMIT_PL (emit_insn (gen_pushsi (mem, reg)));
258         }
259     }
260 
261   if (size > 0)
262     {
263       rtx insn;
264 
265       if (CONST_OK_FOR_LETTER_P (-size, 'L'))
266         EMIT_PL (emit_insn (gen_add3_insn (stack_pointer_rtx,
267                                            stack_pointer_rtx,
268                                            GEN_INT (-size))));
269       else
270         {
271           EMIT_PL (emit_move_insn (gen_rtx_REG (Pmode, PROLOGUE_TEMP_REGNUM),
272                                    GEN_INT (size)));
273           EMIT_PL (emit_insn
274                    (gen_sub3_insn (stack_pointer_rtx,
275                                    stack_pointer_rtx,
276                                    gen_rtx_REG (Pmode,
277                                                 PROLOGUE_TEMP_REGNUM))));
278         }
279       insn = get_last_insn ();
280       REG_NOTES (insn) =
281         alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
282                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
283                                       plus_constant (stack_pointer_rtx,
284                                                      -size)),
285                                       REG_NOTES (insn));
286     }
287 
288   if (frame_pointer_needed)
289     EMIT_PL (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
290 
291   if (flag_pic && f->cprestore_size)
292     {
293       if (frame_pointer_needed)
294         emit_insn (gen_cprestore_use_fp (GEN_INT (size - f->cprestore_size)));
295       else
296         emit_insn (gen_cprestore_use_sp (GEN_INT (size - f->cprestore_size)));
297     }
298 
299 #undef EMIT_PL
300 }
301 
302 /* Generate the epilogue instructions in a S+core function.  */
303 void
mdx_epilogue(int sibcall_p)304 mdx_epilogue (int sibcall_p)
305 {
306   struct score_frame_info *f = mda_compute_frame_size (get_frame_size ());
307   HOST_WIDE_INT size;
308   int regno;
309   rtx base;
310 
311   size = f->total_size - f->gp_reg_size;
312 
313   if (!frame_pointer_needed)
314     base = stack_pointer_rtx;
315   else
316     base = hard_frame_pointer_rtx;
317 
318   if (size)
319     {
320       if (CONST_OK_FOR_LETTER_P (size, 'L'))
321         emit_insn (gen_add3_insn (base, base, GEN_INT (size)));
322       else
323         {
324           emit_move_insn (gen_rtx_REG (Pmode, EPILOGUE_TEMP_REGNUM),
325                           GEN_INT (size));
326           emit_insn (gen_add3_insn (base, base,
327                                     gen_rtx_REG (Pmode,
328                                                  EPILOGUE_TEMP_REGNUM)));
329         }
330     }
331 
332   if (base != stack_pointer_rtx)
333     emit_move_insn (stack_pointer_rtx, base);
334 
335   if (current_function_calls_eh_return)
336     emit_insn (gen_add3_insn (stack_pointer_rtx,
337                               stack_pointer_rtx,
338                               EH_RETURN_STACKADJ_RTX));
339 
340   for (regno = (int) GP_REG_FIRST; regno <= (int) GP_REG_LAST; regno++)
341     {
342       if (BITSET_P (f->mask, regno - GP_REG_FIRST))
343         {
344           rtx mem = gen_rtx_MEM (SImode,
345                                  gen_rtx_POST_INC (SImode, stack_pointer_rtx));
346           rtx reg = gen_rtx_REG (SImode, regno);
347 
348           if (!current_function_calls_eh_return)
349             MEM_READONLY_P (mem) = 1;
350 
351           emit_insn (gen_popsi (reg, mem));
352         }
353     }
354 
355   if (!sibcall_p)
356     emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, RA_REGNUM)));
357 }
358 
359 /* Return true if X is a valid base register for the given mode.
360    Allow only hard registers if STRICT.  */
361 int
mda_valid_base_register_p(rtx x,int strict)362 mda_valid_base_register_p (rtx x, int strict)
363 {
364   if (!strict && GET_CODE (x) == SUBREG)
365     x = SUBREG_REG (x);
366 
367   return (GET_CODE (x) == REG
368           && score_regno_mode_ok_for_base_p (REGNO (x), strict));
369 }
370 
371 /* Return true if X is a valid address for machine mode MODE.  If it is,
372    fill in INFO appropriately.  STRICT is true if we should only accept
373    hard base registers.  */
374 int
mda_classify_address(struct score_address_info * info,enum machine_mode mode,rtx x,int strict)375 mda_classify_address (struct score_address_info *info,
376                       enum machine_mode mode, rtx x, int strict)
377 {
378   info->code = GET_CODE (x);
379 
380   switch (info->code)
381     {
382     case REG:
383     case SUBREG:
384       info->type = ADD_REG;
385       info->reg = x;
386       info->offset = const0_rtx;
387       return mda_valid_base_register_p (info->reg, strict);
388     case PLUS:
389       info->type = ADD_REG;
390       info->reg = XEXP (x, 0);
391       info->offset = XEXP (x, 1);
392       return (mda_valid_base_register_p (info->reg, strict)
393               && GET_CODE (info->offset) == CONST_INT
394               && IMM_IN_RANGE (INTVAL (info->offset), 15, 1));
395     case PRE_DEC:
396     case POST_DEC:
397     case PRE_INC:
398     case POST_INC:
399       if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (SImode))
400         return false;
401       info->type = ADD_REG;
402       info->reg = XEXP (x, 0);
403       info->offset = GEN_INT (GET_MODE_SIZE (mode));
404       return mda_valid_base_register_p (info->reg, strict);
405     case CONST_INT:
406       info->type = ADD_CONST_INT;
407       return IMM_IN_RANGE (INTVAL (x), 15, 1);
408     case CONST:
409     case LABEL_REF:
410     case SYMBOL_REF:
411       info->type = ADD_SYMBOLIC;
412       return (mda_symbolic_constant_p (x, &info->symbol_type)
413               && (info->symbol_type == SYMBOL_GENERAL
414                   || info->symbol_type == SYMBOL_SMALL_DATA));
415     default:
416       return 0;
417     }
418 }
419 
420 void
mda_gen_cmp(enum machine_mode mode)421 mda_gen_cmp (enum machine_mode mode)
422 {
423   emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM),
424                           gen_rtx_COMPARE (mode, cmp_op0, cmp_op1)));
425 }
426 
427 /* Return true if X is a symbolic constant that can be calculated in
428    the same way as a bare symbol.  If it is, store the type of the
429    symbol in *SYMBOL_TYPE.  */
430 int
mda_symbolic_constant_p(rtx x,enum score_symbol_type * symbol_type)431 mda_symbolic_constant_p (rtx x, enum score_symbol_type *symbol_type)
432 {
433   HOST_WIDE_INT offset;
434 
435   score_split_const (x, &x, &offset);
436   if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
437     *symbol_type = score_classify_symbol (x);
438   else
439     return 0;
440 
441   if (offset == 0)
442     return 1;
443 
444   /* if offset > 15bit, must reload  */
445   if (!IMM_IN_RANGE (offset, 15, 1))
446     return 0;
447 
448   switch (*symbol_type)
449     {
450     case SYMBOL_GENERAL:
451       return 1;
452     case SYMBOL_SMALL_DATA:
453       return score_offset_within_object_p (x, offset);
454     }
455   gcc_unreachable ();
456 }
457 
458 void
mdx_movsicc(rtx * ops)459 mdx_movsicc (rtx *ops)
460 {
461   enum machine_mode mode;
462 
463   mode = score_select_cc_mode (GET_CODE (ops[1]), ops[2], ops[3]);
464   emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM),
465                           gen_rtx_COMPARE (mode, cmp_op0, cmp_op1)));
466 }
467 
468 /* Call and sibcall pattern all need call this function.  */
469 void
mdx_call(rtx * ops,bool sib)470 mdx_call (rtx *ops, bool sib)
471 {
472   rtx addr = XEXP (ops[0], 0);
473   if (!call_insn_operand (addr, VOIDmode))
474     {
475       rtx oaddr = addr;
476       addr = gen_reg_rtx (Pmode);
477       gen_move_insn (addr, oaddr);
478     }
479 
480   if (sib)
481     emit_call_insn (gen_sibcall_internal (addr, ops[1]));
482   else
483     emit_call_insn (gen_call_internal (addr, ops[1]));
484 }
485 
486 /* Call value and sibcall value pattern all need call this function.  */
487 void
mdx_call_value(rtx * ops,bool sib)488 mdx_call_value (rtx *ops, bool sib)
489 {
490   rtx result = ops[0];
491   rtx addr = XEXP (ops[1], 0);
492   rtx arg = ops[2];
493 
494   if (!call_insn_operand (addr, VOIDmode))
495     {
496       rtx oaddr = addr;
497       addr = gen_reg_rtx (Pmode);
498       gen_move_insn (addr, oaddr);
499     }
500 
501   if (sib)
502     emit_call_insn (gen_sibcall_value_internal (result, addr, arg));
503   else
504     emit_call_insn (gen_call_value_internal (result, addr, arg));
505 }
506 
507 /* Machine Split  */
508 void
mds_movdi(rtx * ops)509 mds_movdi (rtx *ops)
510 {
511   rtx dst = ops[0];
512   rtx src = ops[1];
513   rtx dst0 = subw (dst, 0);
514   rtx dst1 = subw (dst, 1);
515   rtx src0 = subw (src, 0);
516   rtx src1 = subw (src, 1);
517 
518   if (GET_CODE (dst0) == REG && reg_overlap_mentioned_p (dst0, src))
519     {
520       emit_move_insn (dst1, src1);
521       emit_move_insn (dst0, src0);
522     }
523   else
524     {
525       emit_move_insn (dst0, src0);
526       emit_move_insn (dst1, src1);
527     }
528 }
529 
530 void
mds_zero_extract_andi(rtx * ops)531 mds_zero_extract_andi (rtx *ops)
532 {
533   if (INTVAL (ops[1]) == 1 && const_uimm5 (ops[2], SImode))
534     emit_insn (gen_zero_extract_bittst (ops[0], ops[2]));
535   else
536     {
537       unsigned HOST_WIDE_INT mask;
538       mask = (0xffffffffU & ((1U << INTVAL (ops[1])) - 1U));
539       mask = mask << INTVAL (ops[2]);
540       emit_insn (gen_andsi3_cmp (ops[3], ops[0],
541                                  gen_int_mode (mask, SImode)));
542     }
543 }
544 
545 /* Check addr could be present as PRE/POST mode.  */
546 static bool
mda_pindex_mem(rtx addr)547 mda_pindex_mem (rtx addr)
548 {
549   if (GET_CODE (addr) == MEM)
550     {
551       switch (GET_CODE (XEXP (addr, 0)))
552         {
553         case PRE_DEC:
554         case POST_DEC:
555         case PRE_INC:
556         case POST_INC:
557           return true;
558         default:
559           break;
560         }
561     }
562   return false;
563 }
564 
565 /* Output asm code for ld/sw insn.  */
566 static int
pr_addr_post(rtx * ops,int idata,int iaddr,char * ip,enum mda_mem_unit unit)567 pr_addr_post (rtx *ops, int idata, int iaddr, char *ip, enum mda_mem_unit unit)
568 {
569   struct score_address_info ai;
570 
571   gcc_assert (GET_CODE (ops[idata]) == REG);
572   gcc_assert (mda_classify_address (&ai, SImode, XEXP (ops[iaddr], 0), true));
573 
574   if (!mda_pindex_mem (ops[iaddr])
575       && ai.type == ADD_REG
576       && GET_CODE (ai.offset) == CONST_INT
577       && G16_REG_P (REGNO (ops[idata]))
578       && G16_REG_P (REGNO (ai.reg)))
579     {
580       if (INTVAL (ai.offset) == 0)
581         {
582           ops[iaddr] = ai.reg;
583           return snprintf (ip, INS_BUF_SZ,
584                            "!        %%%d, [%%%d]", idata, iaddr);
585         }
586       if (REGNO (ai.reg) == HARD_FRAME_POINTER_REGNUM)
587         {
588           HOST_WIDE_INT offset = INTVAL (ai.offset);
589           if (MDA_ALIGN_UNIT (offset, unit)
590               && CONST_OK_FOR_LETTER_P (offset >> unit, 'J'))
591             {
592               ops[iaddr] = ai.offset;
593               return snprintf (ip, INS_BUF_SZ,
594                                "p!        %%%d, %%c%d", idata, iaddr);
595             }
596         }
597     }
598   return snprintf (ip, INS_BUF_SZ, "        %%%d, %%a%d", idata, iaddr);
599 }
600 
601 /* Output asm insn for load.  */
602 const char *
mdp_linsn(rtx * ops,enum mda_mem_unit unit,bool sign)603 mdp_linsn (rtx *ops, enum mda_mem_unit unit, bool sign)
604 {
605   const char *pre_ins[] =
606     {"lbu", "lhu", "lw", "??", "lb", "lh", "lw", "??"};
607   char *ip;
608 
609   strcpy (ins, pre_ins[(sign ? 4 : 0) + unit]);
610   ip = ins + strlen (ins);
611 
612   if ((!sign && unit != MDA_HWORD)
613       || (sign && unit != MDA_BYTE))
614     pr_addr_post (ops, 0, 1, ip, unit);
615   else
616     snprintf (ip, INS_BUF_SZ, "        %%0, %%a1");
617 
618   return ins;
619 }
620 
621 /* Output asm insn for store.  */
622 const char *
mdp_sinsn(rtx * ops,enum mda_mem_unit unit)623 mdp_sinsn (rtx *ops, enum mda_mem_unit unit)
624 {
625   const char *pre_ins[] = {"sb", "sh", "sw"};
626   char *ip;
627 
628   strcpy (ins, pre_ins[unit]);
629   ip = ins + strlen (ins);
630   pr_addr_post (ops, 1, 0, ip, unit);
631   return ins;
632 }
633 
634 /* Output asm insn for load immediate.  */
635 const char *
mdp_limm(rtx * ops)636 mdp_limm (rtx *ops)
637 {
638   HOST_WIDE_INT v;
639 
640   gcc_assert (GET_CODE (ops[0]) == REG);
641   gcc_assert (GET_CODE (ops[1]) == CONST_INT);
642 
643   v = INTVAL (ops[1]);
644   if (G16_REG_P (REGNO (ops[0])) && IMM_IN_RANGE (v, 8, 0))
645     return "ldiu!   %0, %c1";
646   else if (IMM_IN_RANGE (v, 16, 1))
647     return "ldi     %0, %c1";
648   else if ((v & 0xffff) == 0)
649     return "ldis    %0, %U1";
650   else
651     return "li      %0, %c1";
652 }
653 
654 /* Output asm insn for move.  */
655 const char *
mdp_move(rtx * ops)656 mdp_move (rtx *ops)
657 {
658   gcc_assert (GET_CODE (ops[0]) == REG);
659   gcc_assert (GET_CODE (ops[1]) == REG);
660 
661   if (G16_REG_P (REGNO (ops[0])))
662     {
663       if (G16_REG_P (REGNO (ops[1])))
664         return "mv!     %0, %1";
665       else
666         return "mlfh!   %0, %1";
667     }
668   else if (G16_REG_P (REGNO (ops[1])))
669     return "mhfl!   %0, %1";
670   else
671     return "mv      %0, %1";
672 }
673 
674 /* Emit lcb/lce insns.  */
675 bool
mdx_unaligned_load(rtx * ops)676 mdx_unaligned_load (rtx *ops)
677 {
678   rtx dst = ops[0];
679   rtx src = ops[1];
680   rtx len = ops[2];
681   rtx off = ops[3];
682   rtx addr_reg;
683 
684   if (INTVAL (len) != BITS_PER_WORD
685       || (INTVAL (off) % BITS_PER_UNIT) != 0)
686     return false;
687 
688   gcc_assert (GET_MODE_SIZE (GET_MODE (dst)) == GET_MODE_SIZE (SImode));
689 
690   addr_reg = copy_addr_to_reg (XEXP (src, 0));
691   emit_insn (gen_move_lcb (addr_reg, addr_reg));
692   emit_insn (gen_move_lce (addr_reg, addr_reg, dst));
693 
694   return true;
695 }
696 
697 /* Emit scb/sce insns.  */
698 bool
mdx_unaligned_store(rtx * ops)699 mdx_unaligned_store (rtx *ops)
700 {
701   rtx dst = ops[0];
702   rtx len = ops[1];
703   rtx off = ops[2];
704   rtx src = ops[3];
705   rtx addr_reg;
706 
707   if (INTVAL(len) != BITS_PER_WORD
708       || (INTVAL(off) % BITS_PER_UNIT) != 0)
709     return false;
710 
711   gcc_assert (GET_MODE_SIZE (GET_MODE (src)) == GET_MODE_SIZE (SImode));
712 
713   addr_reg = copy_addr_to_reg (XEXP (dst, 0));
714   emit_insn (gen_move_scb (addr_reg, addr_reg, src));
715   emit_insn (gen_move_sce (addr_reg, addr_reg));
716 
717   return true;
718 }
719 
720 /* If length is short, generate move insns straight.  */
721 static void
mdx_block_move_straight(rtx dst,rtx src,HOST_WIDE_INT length)722 mdx_block_move_straight (rtx dst, rtx src, HOST_WIDE_INT length)
723 {
724   HOST_WIDE_INT leftover;
725   int i, reg_count;
726   rtx *regs;
727 
728   leftover = length % UNITS_PER_WORD;
729   length -= leftover;
730   reg_count = length / UNITS_PER_WORD;
731 
732   regs = alloca (sizeof (rtx) * reg_count);
733   for (i = 0; i < reg_count; i++)
734     regs[i] = gen_reg_rtx (SImode);
735 
736   /* Load from src to regs.  */
737   if (MEM_ALIGN (src) >= BITS_PER_WORD)
738     {
739       HOST_WIDE_INT offset = 0;
740       for (i = 0; i < reg_count; offset += UNITS_PER_WORD, i++)
741         emit_move_insn (regs[i], adjust_address (src, SImode, offset));
742     }
743   else if (reg_count >= 1)
744     {
745       rtx src_reg = copy_addr_to_reg (XEXP (src, 0));
746 
747       emit_insn (gen_move_lcb (src_reg, src_reg));
748       for (i = 0; i < (reg_count - 1); i++)
749         emit_insn (gen_move_lcw (src_reg, src_reg, regs[i]));
750       emit_insn (gen_move_lce (src_reg, src_reg, regs[i]));
751     }
752 
753   /* Store regs to dest.  */
754   if (MEM_ALIGN (dst) >= BITS_PER_WORD)
755     {
756       HOST_WIDE_INT offset = 0;
757       for (i = 0; i < reg_count; offset += UNITS_PER_WORD, i++)
758         emit_move_insn (adjust_address (dst, SImode, offset), regs[i]);
759     }
760   else if (reg_count >= 1)
761     {
762       rtx dst_reg = copy_addr_to_reg (XEXP (dst, 0));
763 
764       emit_insn (gen_move_scb (dst_reg, dst_reg, regs[0]));
765       for (i = 1; i < reg_count; i++)
766         emit_insn (gen_move_scw (dst_reg, dst_reg, regs[i]));
767       emit_insn (gen_move_sce (dst_reg, dst_reg));
768     }
769 
770   /* Mop up any left-over bytes.  */
771   if (leftover > 0)
772     {
773       src = adjust_address (src, BLKmode, length);
774       dst = adjust_address (dst, BLKmode, length);
775       move_by_pieces (dst, src, leftover,
776                       MIN (MEM_ALIGN (src), MEM_ALIGN (dst)), 0);
777     }
778 }
779 
780 /* Generate loop head when dst or src is unaligned.  */
781 static void
mdx_block_move_loop_head(rtx dst_reg,HOST_WIDE_INT dst_align,rtx src_reg,HOST_WIDE_INT src_align,HOST_WIDE_INT length)782 mdx_block_move_loop_head (rtx dst_reg, HOST_WIDE_INT dst_align,
783                           rtx src_reg, HOST_WIDE_INT src_align,
784                           HOST_WIDE_INT length)
785 {
786   bool src_unaligned = (src_align < BITS_PER_WORD);
787   bool dst_unaligned = (dst_align < BITS_PER_WORD);
788 
789   rtx temp = gen_reg_rtx (SImode);
790 
791   gcc_assert (length == UNITS_PER_WORD);
792 
793   if (src_unaligned)
794     {
795       emit_insn (gen_move_lcb (src_reg, src_reg));
796       emit_insn (gen_move_lcw (src_reg, src_reg, temp));
797     }
798   else
799     emit_insn (gen_move_lw_a (src_reg,
800                               src_reg, gen_int_mode (4, SImode), temp));
801 
802   if (dst_unaligned)
803     emit_insn (gen_move_scb (dst_reg, dst_reg, temp));
804   else
805     emit_insn (gen_move_sw_a (dst_reg,
806                               dst_reg, gen_int_mode (4, SImode), temp));
807 }
808 
809 /* Generate loop body, copy length bytes per iteration.  */
810 static void
mdx_block_move_loop_body(rtx dst_reg,HOST_WIDE_INT dst_align,rtx src_reg,HOST_WIDE_INT src_align,HOST_WIDE_INT length)811 mdx_block_move_loop_body (rtx dst_reg, HOST_WIDE_INT dst_align,
812                           rtx src_reg, HOST_WIDE_INT src_align,
813                           HOST_WIDE_INT length)
814 {
815   int reg_count = length / UNITS_PER_WORD;
816   rtx *regs = alloca (sizeof (rtx) * reg_count);
817   int i;
818   bool src_unaligned = (src_align < BITS_PER_WORD);
819   bool dst_unaligned = (dst_align < BITS_PER_WORD);
820 
821   for (i = 0; i < reg_count; i++)
822     regs[i] = gen_reg_rtx (SImode);
823 
824   if (src_unaligned)
825     {
826       for (i = 0; i < reg_count; i++)
827         emit_insn (gen_move_lcw (src_reg, src_reg, regs[i]));
828     }
829   else
830     {
831       for (i = 0; i < reg_count; i++)
832         emit_insn (gen_move_lw_a (src_reg,
833                                   src_reg, gen_int_mode (4, SImode), regs[i]));
834     }
835 
836   if (dst_unaligned)
837     {
838       for (i = 0; i < reg_count; i++)
839         emit_insn (gen_move_scw (dst_reg, dst_reg, regs[i]));
840     }
841   else
842     {
843       for (i = 0; i < reg_count; i++)
844         emit_insn (gen_move_sw_a (dst_reg,
845                                   dst_reg, gen_int_mode (4, SImode), regs[i]));
846     }
847 }
848 
849 /* Generate loop foot, copy the leftover bytes.  */
850 static void
mdx_block_move_loop_foot(rtx dst_reg,HOST_WIDE_INT dst_align,rtx src_reg,HOST_WIDE_INT src_align,HOST_WIDE_INT length)851 mdx_block_move_loop_foot (rtx dst_reg, HOST_WIDE_INT dst_align,
852                           rtx src_reg, HOST_WIDE_INT src_align,
853                           HOST_WIDE_INT length)
854 {
855   bool src_unaligned = (src_align < BITS_PER_WORD);
856   bool dst_unaligned = (dst_align < BITS_PER_WORD);
857 
858   HOST_WIDE_INT leftover;
859 
860   leftover = length % UNITS_PER_WORD;
861   length -= leftover;
862 
863   if (length > 0)
864     mdx_block_move_loop_body (dst_reg, dst_align,
865                               src_reg, src_align, length);
866 
867   if (dst_unaligned)
868     emit_insn (gen_move_sce (dst_reg, dst_reg));
869 
870   if (leftover > 0)
871     {
872       HOST_WIDE_INT src_adj = src_unaligned ? -4 : 0;
873       HOST_WIDE_INT dst_adj = dst_unaligned ? -4 : 0;
874       rtx temp;
875 
876       gcc_assert (leftover < UNITS_PER_WORD);
877 
878       if (leftover >= UNITS_PER_WORD / 2
879           && src_align >= BITS_PER_WORD / 2
880           && dst_align >= BITS_PER_WORD / 2)
881         {
882           temp = gen_reg_rtx (HImode);
883           emit_insn (gen_move_lhu_b (src_reg, src_reg,
884                                      gen_int_mode (src_adj, SImode), temp));
885           emit_insn (gen_move_sh_b (dst_reg, dst_reg,
886                                     gen_int_mode (dst_adj, SImode), temp));
887           leftover -= UNITS_PER_WORD / 2;
888           src_adj = UNITS_PER_WORD / 2;
889           dst_adj = UNITS_PER_WORD / 2;
890         }
891 
892       while (leftover > 0)
893         {
894           temp = gen_reg_rtx (QImode);
895           emit_insn (gen_move_lbu_b (src_reg, src_reg,
896                                      gen_int_mode (src_adj, SImode), temp));
897           emit_insn (gen_move_sb_b (dst_reg, dst_reg,
898                                     gen_int_mode (dst_adj, SImode), temp));
899           leftover--;
900           src_adj = 1;
901           dst_adj = 1;
902         }
903     }
904 }
905 
906 #define MIN_MOVE_REGS 3
907 #define MIN_MOVE_BYTES (MIN_MOVE_REGS * UNITS_PER_WORD)
908 #define MAX_MOVE_REGS 4
909 #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
910 
911 /* The length is large, generate a loop if necessary.
912    The loop is consisted by loop head/body/foot.  */
913 static void
mdx_block_move_loop(rtx dst,rtx src,HOST_WIDE_INT length)914 mdx_block_move_loop (rtx dst, rtx src, HOST_WIDE_INT length)
915 {
916   HOST_WIDE_INT src_align = MEM_ALIGN (src);
917   HOST_WIDE_INT dst_align = MEM_ALIGN (dst);
918   HOST_WIDE_INT loop_mov_bytes;
919   HOST_WIDE_INT iteration = 0;
920   HOST_WIDE_INT head_length = 0, leftover;
921   rtx label, src_reg, dst_reg, final_dst;
922 
923   bool gen_loop_head = (src_align < BITS_PER_WORD
924                         || dst_align < BITS_PER_WORD);
925 
926   if (gen_loop_head)
927     head_length += UNITS_PER_WORD;
928 
929   for (loop_mov_bytes = MAX_MOVE_BYTES;
930        loop_mov_bytes >= MIN_MOVE_BYTES;
931        loop_mov_bytes -= UNITS_PER_WORD)
932     {
933       iteration = (length - head_length) / loop_mov_bytes;
934       if (iteration > 1)
935         break;
936     }
937   if (iteration <= 1)
938     {
939       mdx_block_move_straight (dst, src, length);
940       return;
941     }
942 
943   leftover = (length - head_length) % loop_mov_bytes;
944   length -= leftover;
945 
946   src_reg = copy_addr_to_reg (XEXP (src, 0));
947   dst_reg = copy_addr_to_reg (XEXP (dst, 0));
948   final_dst = expand_simple_binop (Pmode, PLUS, dst_reg, GEN_INT (length),
949                                    0, 0, OPTAB_WIDEN);
950 
951   if (gen_loop_head)
952     mdx_block_move_loop_head (dst_reg, dst_align,
953                               src_reg, src_align, head_length);
954 
955   label = gen_label_rtx ();
956   emit_label (label);
957 
958   mdx_block_move_loop_body (dst_reg, dst_align,
959                             src_reg, src_align, loop_mov_bytes);
960 
961   emit_insn (gen_cmpsi (dst_reg, final_dst));
962   emit_jump_insn (gen_bne (label));
963 
964   mdx_block_move_loop_foot (dst_reg, dst_align,
965                             src_reg, src_align, leftover);
966 }
967 
968 /* Generate block move, for misc.md: "movmemsi".  */
969 bool
mdx_block_move(rtx * ops)970 mdx_block_move (rtx *ops)
971 {
972   rtx dst = ops[0];
973   rtx src = ops[1];
974   rtx length = ops[2];
975 
976   if (TARGET_LITTLE_ENDIAN
977       && (MEM_ALIGN (src) < BITS_PER_WORD || MEM_ALIGN (dst) < BITS_PER_WORD)
978       && INTVAL (length) >= UNITS_PER_WORD)
979     return false;
980 
981   if (GET_CODE (length) == CONST_INT)
982     {
983       if (INTVAL (length) <= 2 * MAX_MOVE_BYTES)
984         {
985            mdx_block_move_straight (dst, src, INTVAL (length));
986            return true;
987         }
988       else if (optimize &&
989                !(flag_unroll_loops || flag_unroll_all_loops))
990         {
991           mdx_block_move_loop (dst, src, INTVAL (length));
992           return true;
993         }
994     }
995   return false;
996 }
997 
998 /* Generate add insn.  */
999 const char *
mdp_select_add_imm(rtx * ops,bool set_cc)1000 mdp_select_add_imm (rtx *ops, bool set_cc)
1001 {
1002   HOST_WIDE_INT v = INTVAL (ops[2]);
1003 
1004   gcc_assert (GET_CODE (ops[2]) == CONST_INT);
1005   gcc_assert (REGNO (ops[0]) == REGNO (ops[1]));
1006 
1007   if (set_cc && G16_REG_P (REGNO (ops[0])))
1008     {
1009       if (v > 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) v, 0, 15))
1010         {
1011           ops[2] = GEN_INT (ffs (v) - 1);
1012           return "addei!  %0, %c2";
1013         }
1014 
1015       if (v < 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) (-v), 0, 15))
1016         {
1017           ops[2] = GEN_INT (ffs (-v) - 1);
1018           return "subei!  %0, %c2";
1019         }
1020     }
1021 
1022   if (set_cc)
1023     return "addi.c  %0, %c2";
1024   else
1025     return "addi    %0, %c2";
1026 }
1027 
1028 /* Output arith insn.  */
1029 const char *
mdp_select(rtx * ops,const char * inst_pre,bool commu,const char * letter,bool set_cc)1030 mdp_select (rtx *ops, const char *inst_pre,
1031             bool commu, const char *letter, bool set_cc)
1032 {
1033   gcc_assert (GET_CODE (ops[0]) == REG);
1034   gcc_assert (GET_CODE (ops[1]) == REG);
1035 
1036   if (set_cc && G16_REG_P (REGNO (ops[0]))
1037       && (GET_CODE (ops[2]) == REG ? G16_REG_P (REGNO (ops[2])) : 1)
1038       && REGNO (ops[0]) == REGNO (ops[1]))
1039     {
1040       snprintf (ins, INS_BUF_SZ, "%s!  %%0, %%%s2", inst_pre, letter);
1041       return ins;
1042     }
1043 
1044   if (commu && set_cc && G16_REG_P (REGNO (ops[0]))
1045       && G16_REG_P (REGNO (ops[1]))
1046       && REGNO (ops[0]) == REGNO (ops[2]))
1047     {
1048       gcc_assert (GET_CODE (ops[2]) == REG);
1049       snprintf (ins, INS_BUF_SZ, "%s!  %%0, %%%s1", inst_pre, letter);
1050       return ins;
1051     }
1052 
1053   if (set_cc)
1054     snprintf (ins, INS_BUF_SZ, "%s.c  %%0, %%1, %%%s2", inst_pre, letter);
1055   else
1056     snprintf (ins, INS_BUF_SZ, "%s    %%0, %%1, %%%s2", inst_pre, letter);
1057   return ins;
1058 }
1059 
1060