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