Mercurial > hg > CbC > CbC_gcc
comparison gcc/config/nds32/nds32-md-auxiliary.c @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | 04ced10e8804 |
children | 1830386684a0 |
comparison
equal
deleted
inserted
replaced
111:04ced10e8804 | 131:84e7813d76e9 |
---|---|
1 /* Auxiliary functions for output asm template or expand rtl | 1 /* Auxiliary functions for output asm template or expand rtl |
2 pattern of Andes NDS32 cpu for GNU compiler | 2 pattern of Andes NDS32 cpu for GNU compiler |
3 Copyright (C) 2012-2017 Free Software Foundation, Inc. | 3 Copyright (C) 2012-2018 Free Software Foundation, Inc. |
4 Contributed by Andes Technology Corporation. | 4 Contributed by Andes Technology Corporation. |
5 | 5 |
6 This file is part of GCC. | 6 This file is part of GCC. |
7 | 7 |
8 GCC is free software; you can redistribute it and/or modify it | 8 GCC is free software; you can redistribute it and/or modify it |
18 You should have received a copy of the GNU General Public License | 18 You should have received a copy of the GNU General Public License |
19 along with GCC; see the file COPYING3. If not see | 19 along with GCC; see the file COPYING3. If not see |
20 <http://www.gnu.org/licenses/>. */ | 20 <http://www.gnu.org/licenses/>. */ |
21 | 21 |
22 /* ------------------------------------------------------------------------ */ | 22 /* ------------------------------------------------------------------------ */ |
23 | |
24 #define IN_TARGET_CODE 1 | |
23 | 25 |
24 #include "config.h" | 26 #include "config.h" |
25 #include "system.h" | 27 #include "system.h" |
26 #include "coretypes.h" | 28 #include "coretypes.h" |
27 #include "backend.h" | 29 #include "backend.h" |
32 #include "tm_p.h" | 34 #include "tm_p.h" |
33 #include "optabs.h" /* For GEN_FCN. */ | 35 #include "optabs.h" /* For GEN_FCN. */ |
34 #include "recog.h" | 36 #include "recog.h" |
35 #include "output.h" | 37 #include "output.h" |
36 #include "tm-constrs.h" | 38 #include "tm-constrs.h" |
39 #include "expr.h" | |
40 #include "emit-rtl.h" | |
41 #include "explow.h" | |
42 #include "stringpool.h" | |
43 #include "attribs.h" | |
44 | |
37 | 45 |
38 /* ------------------------------------------------------------------------ */ | 46 /* ------------------------------------------------------------------------ */ |
47 | |
48 static int | |
49 nds32_regno_to_enable4 (unsigned regno) | |
50 { | |
51 switch (regno) | |
52 { | |
53 case 28: /* $r28/fp */ | |
54 return 0x8; | |
55 case 29: /* $r29/gp */ | |
56 return 0x4; | |
57 case 30: /* $r30/lp */ | |
58 return 0x2; | |
59 case 31: /* $r31/sp */ | |
60 return 0x1; | |
61 default: | |
62 gcc_unreachable (); | |
63 } | |
64 } | |
39 | 65 |
40 /* A helper function to return character based on byte size. */ | 66 /* A helper function to return character based on byte size. */ |
41 static char | 67 static char |
42 nds32_byte_to_size (int byte) | 68 nds32_byte_to_size (int byte) |
43 { | 69 { |
53 /* Normally it should not be here. */ | 79 /* Normally it should not be here. */ |
54 gcc_unreachable (); | 80 gcc_unreachable (); |
55 } | 81 } |
56 } | 82 } |
57 | 83 |
58 /* A helper function to return memory format. */ | 84 static int |
85 nds32_inverse_cond_code (int code) | |
86 { | |
87 switch (code) | |
88 { | |
89 case NE: | |
90 return EQ; | |
91 case EQ: | |
92 return NE; | |
93 case GT: | |
94 return LE; | |
95 case LE: | |
96 return GT; | |
97 case GE: | |
98 return LT; | |
99 case LT: | |
100 return GE; | |
101 default: | |
102 gcc_unreachable (); | |
103 } | |
104 } | |
105 | |
106 static const char * | |
107 nds32_cond_code_str (int code) | |
108 { | |
109 switch (code) | |
110 { | |
111 case NE: | |
112 return "ne"; | |
113 case EQ: | |
114 return "eq"; | |
115 case GT: | |
116 return "gt"; | |
117 case LE: | |
118 return "le"; | |
119 case GE: | |
120 return "ge"; | |
121 case LT: | |
122 return "lt"; | |
123 default: | |
124 gcc_unreachable (); | |
125 } | |
126 } | |
127 | |
128 static void | |
129 output_cond_branch (int code, const char *suffix, bool r5_p, | |
130 bool long_jump_p, rtx *operands) | |
131 { | |
132 char pattern[256]; | |
133 const char *cond_code; | |
134 bool align_p = NDS32_ALIGN_P (); | |
135 const char *align = align_p ? "\t.align\t2\n" : ""; | |
136 | |
137 if (r5_p && REGNO (operands[2]) == 5 && TARGET_16_BIT) | |
138 { | |
139 /* This is special case for beqs38 and bnes38, | |
140 second operand 2 can't be $r5 and it's almost meanless, | |
141 however it may occur after copy propgation. */ | |
142 if (code == EQ) | |
143 { | |
144 /* $r5 == $r5 always taken! */ | |
145 if (long_jump_p) | |
146 snprintf (pattern, sizeof (pattern), | |
147 "j\t%%3"); | |
148 else | |
149 snprintf (pattern, sizeof (pattern), | |
150 "j8\t%%3"); | |
151 } | |
152 else | |
153 /* Don't output anything since $r5 != $r5 never taken! */ | |
154 pattern[0] = '\0'; | |
155 } | |
156 else if (long_jump_p) | |
157 { | |
158 int inverse_code = nds32_inverse_cond_code (code); | |
159 cond_code = nds32_cond_code_str (inverse_code); | |
160 | |
161 /* b<cond><suffix> $r0, $r1, .L0 | |
162 => | |
163 b<inverse_cond><suffix> $r0, $r1, .LCB0 | |
164 j .L0 | |
165 .LCB0: | |
166 | |
167 or | |
168 | |
169 b<cond><suffix> $r0, $r1, .L0 | |
170 => | |
171 b<inverse_cond><suffix> $r0, $r1, .LCB0 | |
172 j .L0 | |
173 .LCB0: | |
174 */ | |
175 if (r5_p && TARGET_16_BIT) | |
176 { | |
177 snprintf (pattern, sizeof (pattern), | |
178 "b%ss38\t %%2, .LCB%%=\n\tj\t%%3\n%s.LCB%%=:", | |
179 cond_code, align); | |
180 } | |
181 else | |
182 { | |
183 snprintf (pattern, sizeof (pattern), | |
184 "b%s%s\t%%1, %%2, .LCB%%=\n\tj\t%%3\n%s.LCB%%=:", | |
185 cond_code, suffix, align); | |
186 } | |
187 } | |
188 else | |
189 { | |
190 cond_code = nds32_cond_code_str (code); | |
191 if (r5_p && TARGET_16_BIT) | |
192 { | |
193 /* b<cond>s38 $r1, .L0 */ | |
194 snprintf (pattern, sizeof (pattern), | |
195 "b%ss38\t %%2, %%3", cond_code); | |
196 } | |
197 else | |
198 { | |
199 /* b<cond><suffix> $r0, $r1, .L0 */ | |
200 snprintf (pattern, sizeof (pattern), | |
201 "b%s%s\t%%1, %%2, %%3", cond_code, suffix); | |
202 } | |
203 } | |
204 | |
205 output_asm_insn (pattern, operands); | |
206 } | |
207 | |
208 static void | |
209 output_cond_branch_compare_zero (int code, const char *suffix, | |
210 bool long_jump_p, rtx *operands, | |
211 bool ta_implied_p) | |
212 { | |
213 char pattern[256]; | |
214 const char *cond_code; | |
215 bool align_p = NDS32_ALIGN_P (); | |
216 const char *align = align_p ? "\t.align\t2\n" : ""; | |
217 if (long_jump_p) | |
218 { | |
219 int inverse_code = nds32_inverse_cond_code (code); | |
220 cond_code = nds32_cond_code_str (inverse_code); | |
221 | |
222 if (ta_implied_p && TARGET_16_BIT) | |
223 { | |
224 /* b<cond>z<suffix> .L0 | |
225 => | |
226 b<inverse_cond>z<suffix> .LCB0 | |
227 j .L0 | |
228 .LCB0: | |
229 */ | |
230 snprintf (pattern, sizeof (pattern), | |
231 "b%sz%s\t.LCB%%=\n\tj\t%%2\n%s.LCB%%=:", | |
232 cond_code, suffix, align); | |
233 } | |
234 else | |
235 { | |
236 /* b<cond>z<suffix> $r0, .L0 | |
237 => | |
238 b<inverse_cond>z<suffix> $r0, .LCB0 | |
239 j .L0 | |
240 .LCB0: | |
241 */ | |
242 snprintf (pattern, sizeof (pattern), | |
243 "b%sz%s\t%%1, .LCB%%=\n\tj\t%%2\n%s.LCB%%=:", | |
244 cond_code, suffix, align); | |
245 } | |
246 } | |
247 else | |
248 { | |
249 cond_code = nds32_cond_code_str (code); | |
250 if (ta_implied_p && TARGET_16_BIT) | |
251 { | |
252 /* b<cond>z<suffix> .L0 */ | |
253 snprintf (pattern, sizeof (pattern), | |
254 "b%sz%s\t%%2", cond_code, suffix); | |
255 } | |
256 else | |
257 { | |
258 /* b<cond>z<suffix> $r0, .L0 */ | |
259 snprintf (pattern, sizeof (pattern), | |
260 "b%sz%s\t%%1, %%2", cond_code, suffix); | |
261 } | |
262 } | |
263 | |
264 output_asm_insn (pattern, operands); | |
265 } | |
266 | |
267 static void | |
268 nds32_split_shiftrtdi3 (rtx dst, rtx src, rtx shiftamount, bool logic_shift_p) | |
269 { | |
270 rtx src_high_part; | |
271 rtx dst_high_part, dst_low_part; | |
272 | |
273 dst_high_part = nds32_di_high_part_subreg (dst); | |
274 src_high_part = nds32_di_high_part_subreg (src); | |
275 dst_low_part = nds32_di_low_part_subreg (dst); | |
276 | |
277 if (CONST_INT_P (shiftamount)) | |
278 { | |
279 if (INTVAL (shiftamount) < 32) | |
280 { | |
281 if (logic_shift_p) | |
282 { | |
283 emit_insn (gen_uwext (dst_low_part, src, | |
284 shiftamount)); | |
285 emit_insn (gen_lshrsi3 (dst_high_part, src_high_part, | |
286 shiftamount)); | |
287 } | |
288 else | |
289 { | |
290 emit_insn (gen_wext (dst_low_part, src, | |
291 shiftamount)); | |
292 emit_insn (gen_ashrsi3 (dst_high_part, src_high_part, | |
293 shiftamount)); | |
294 } | |
295 } | |
296 else | |
297 { | |
298 rtx new_shift_amout = gen_int_mode(INTVAL (shiftamount) - 32, SImode); | |
299 | |
300 if (logic_shift_p) | |
301 { | |
302 emit_insn (gen_lshrsi3 (dst_low_part, src_high_part, | |
303 new_shift_amout)); | |
304 emit_move_insn (dst_high_part, const0_rtx); | |
305 } | |
306 else | |
307 { | |
308 emit_insn (gen_ashrsi3 (dst_low_part, src_high_part, | |
309 new_shift_amout)); | |
310 emit_insn (gen_ashrsi3 (dst_high_part, src_high_part, | |
311 GEN_INT (31))); | |
312 } | |
313 } | |
314 } | |
315 else | |
316 { | |
317 rtx dst_low_part_l32, dst_high_part_l32; | |
318 rtx dst_low_part_g32, dst_high_part_g32; | |
319 rtx new_shift_amout, select_reg; | |
320 dst_low_part_l32 = gen_reg_rtx (SImode); | |
321 dst_high_part_l32 = gen_reg_rtx (SImode); | |
322 dst_low_part_g32 = gen_reg_rtx (SImode); | |
323 dst_high_part_g32 = gen_reg_rtx (SImode); | |
324 new_shift_amout = gen_reg_rtx (SImode); | |
325 select_reg = gen_reg_rtx (SImode); | |
326 | |
327 emit_insn (gen_andsi3 (shiftamount, shiftamount, GEN_INT (0x3f))); | |
328 | |
329 if (logic_shift_p) | |
330 { | |
331 /* | |
332 if (shiftamount < 32) | |
333 dst_low_part = wext (src, shiftamount) | |
334 dst_high_part = src_high_part >> shiftamount | |
335 else | |
336 dst_low_part = src_high_part >> (shiftamount & 0x1f) | |
337 dst_high_part = 0 | |
338 */ | |
339 emit_insn (gen_uwext (dst_low_part_l32, src, shiftamount)); | |
340 emit_insn (gen_lshrsi3 (dst_high_part_l32, src_high_part, | |
341 shiftamount)); | |
342 | |
343 emit_insn (gen_andsi3 (new_shift_amout, shiftamount, GEN_INT (0x1f))); | |
344 emit_insn (gen_lshrsi3 (dst_low_part_g32, src_high_part, | |
345 new_shift_amout)); | |
346 emit_move_insn (dst_high_part_g32, const0_rtx); | |
347 } | |
348 else | |
349 { | |
350 /* | |
351 if (shiftamount < 32) | |
352 dst_low_part = wext (src, shiftamount) | |
353 dst_high_part = src_high_part >> shiftamount | |
354 else | |
355 dst_low_part = src_high_part >> (shiftamount & 0x1f) | |
356 # shift 31 for sign extend | |
357 dst_high_part = src_high_part >> 31 | |
358 */ | |
359 emit_insn (gen_wext (dst_low_part_l32, src, shiftamount)); | |
360 emit_insn (gen_ashrsi3 (dst_high_part_l32, src_high_part, | |
361 shiftamount)); | |
362 | |
363 emit_insn (gen_andsi3 (new_shift_amout, shiftamount, GEN_INT (0x1f))); | |
364 emit_insn (gen_ashrsi3 (dst_low_part_g32, src_high_part, | |
365 new_shift_amout)); | |
366 emit_insn (gen_ashrsi3 (dst_high_part_g32, src_high_part, | |
367 GEN_INT (31))); | |
368 } | |
369 | |
370 emit_insn (gen_slt_compare (select_reg, shiftamount, GEN_INT (32))); | |
371 | |
372 emit_insn (gen_cmovnsi (dst_low_part, select_reg, | |
373 dst_low_part_l32, dst_low_part_g32)); | |
374 emit_insn (gen_cmovnsi (dst_high_part, select_reg, | |
375 dst_high_part_l32, dst_high_part_g32)); | |
376 } | |
377 } | |
378 | |
379 /* ------------------------------------------------------------------------ */ | |
380 | |
381 /* Auxiliary function for expand RTL pattern. */ | |
382 | |
383 enum nds32_expand_result_type | |
384 nds32_expand_cbranch (rtx *operands) | |
385 { | |
386 rtx tmp_reg; | |
387 enum rtx_code code; | |
388 | |
389 code = GET_CODE (operands[0]); | |
390 | |
391 /* If operands[2] is (const_int 0), | |
392 we can use beqz,bnez,bgtz,bgez,bltz,or blez instructions. | |
393 So we have gcc generate original template rtx. */ | |
394 if (GET_CODE (operands[2]) == CONST_INT) | |
395 if (INTVAL (operands[2]) == 0) | |
396 if ((code != GTU) | |
397 && (code != GEU) | |
398 && (code != LTU) | |
399 && (code != LEU)) | |
400 return EXPAND_CREATE_TEMPLATE; | |
401 | |
402 /* For other comparison, NDS32 ISA only has slt (Set-on-Less-Than) | |
403 behavior for the comparison, we might need to generate other | |
404 rtx patterns to achieve same semantic. */ | |
405 switch (code) | |
406 { | |
407 case GT: | |
408 case GTU: | |
409 if (GET_CODE (operands[2]) == CONST_INT) | |
410 { | |
411 /* GT reg_A, const_int => !(LT reg_A, const_int + 1) */ | |
412 if (optimize_size || optimize == 0) | |
413 tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); | |
414 else | |
415 tmp_reg = gen_reg_rtx (SImode); | |
416 | |
417 /* We want to plus 1 into the integer value | |
418 of operands[2] to create 'slt' instruction. | |
419 This caculation is performed on the host machine, | |
420 which may be 64-bit integer. | |
421 So the meaning of caculation result may be | |
422 different from the 32-bit nds32 target. | |
423 | |
424 For example: | |
425 0x7fffffff + 0x1 -> 0x80000000, | |
426 this value is POSITIVE on 64-bit machine, | |
427 but the expected value on 32-bit nds32 target | |
428 should be NEGATIVE value. | |
429 | |
430 Hence, instead of using GEN_INT(), we use gen_int_mode() to | |
431 explicitly create SImode constant rtx. */ | |
432 enum rtx_code cmp_code; | |
433 | |
434 rtx plus1 = gen_int_mode (INTVAL (operands[2]) + 1, SImode); | |
435 if (satisfies_constraint_Is15 (plus1)) | |
436 { | |
437 operands[2] = plus1; | |
438 cmp_code = EQ; | |
439 if (code == GT) | |
440 { | |
441 /* GT, use slts instruction */ | |
442 emit_insn ( | |
443 gen_slts_compare (tmp_reg, operands[1], operands[2])); | |
444 } | |
445 else | |
446 { | |
447 /* GTU, use slt instruction */ | |
448 emit_insn ( | |
449 gen_slt_compare (tmp_reg, operands[1], operands[2])); | |
450 } | |
451 } | |
452 else | |
453 { | |
454 cmp_code = NE; | |
455 if (code == GT) | |
456 { | |
457 /* GT, use slts instruction */ | |
458 emit_insn ( | |
459 gen_slts_compare (tmp_reg, operands[2], operands[1])); | |
460 } | |
461 else | |
462 { | |
463 /* GTU, use slt instruction */ | |
464 emit_insn ( | |
465 gen_slt_compare (tmp_reg, operands[2], operands[1])); | |
466 } | |
467 } | |
468 | |
469 PUT_CODE (operands[0], cmp_code); | |
470 operands[1] = tmp_reg; | |
471 operands[2] = const0_rtx; | |
472 emit_insn (gen_cbranchsi4 (operands[0], operands[1], | |
473 operands[2], operands[3])); | |
474 | |
475 return EXPAND_DONE; | |
476 } | |
477 else | |
478 { | |
479 /* GT reg_A, reg_B => LT reg_B, reg_A */ | |
480 if (optimize_size || optimize == 0) | |
481 tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); | |
482 else | |
483 tmp_reg = gen_reg_rtx (SImode); | |
484 | |
485 if (code == GT) | |
486 { | |
487 /* GT, use slts instruction */ | |
488 emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1])); | |
489 } | |
490 else | |
491 { | |
492 /* GTU, use slt instruction */ | |
493 emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1])); | |
494 } | |
495 | |
496 PUT_CODE (operands[0], NE); | |
497 operands[1] = tmp_reg; | |
498 operands[2] = const0_rtx; | |
499 emit_insn (gen_cbranchsi4 (operands[0], operands[1], | |
500 operands[2], operands[3])); | |
501 | |
502 return EXPAND_DONE; | |
503 } | |
504 | |
505 case GE: | |
506 case GEU: | |
507 /* GE reg_A, reg_B => !(LT reg_A, reg_B) */ | |
508 /* GE reg_A, const_int => !(LT reg_A, const_int) */ | |
509 if (optimize_size || optimize == 0) | |
510 tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); | |
511 else | |
512 tmp_reg = gen_reg_rtx (SImode); | |
513 | |
514 if (code == GE) | |
515 { | |
516 /* GE, use slts instruction */ | |
517 emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2])); | |
518 } | |
519 else | |
520 { | |
521 /* GEU, use slt instruction */ | |
522 emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2])); | |
523 } | |
524 | |
525 PUT_CODE (operands[0], EQ); | |
526 operands[1] = tmp_reg; | |
527 operands[2] = const0_rtx; | |
528 emit_insn (gen_cbranchsi4 (operands[0], operands[1], | |
529 operands[2], operands[3])); | |
530 | |
531 return EXPAND_DONE; | |
532 | |
533 case LT: | |
534 case LTU: | |
535 /* LT reg_A, reg_B => LT reg_A, reg_B */ | |
536 /* LT reg_A, const_int => LT reg_A, const_int */ | |
537 if (optimize_size || optimize == 0) | |
538 tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); | |
539 else | |
540 tmp_reg = gen_reg_rtx (SImode); | |
541 | |
542 if (code == LT) | |
543 { | |
544 /* LT, use slts instruction */ | |
545 emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2])); | |
546 } | |
547 else | |
548 { | |
549 /* LTU, use slt instruction */ | |
550 emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2])); | |
551 } | |
552 | |
553 PUT_CODE (operands[0], NE); | |
554 operands[1] = tmp_reg; | |
555 operands[2] = const0_rtx; | |
556 emit_insn (gen_cbranchsi4 (operands[0], operands[1], | |
557 operands[2], operands[3])); | |
558 | |
559 return EXPAND_DONE; | |
560 | |
561 case LE: | |
562 case LEU: | |
563 if (GET_CODE (operands[2]) == CONST_INT) | |
564 { | |
565 /* LE reg_A, const_int => LT reg_A, const_int + 1 */ | |
566 if (optimize_size || optimize == 0) | |
567 tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); | |
568 else | |
569 tmp_reg = gen_reg_rtx (SImode); | |
570 | |
571 enum rtx_code cmp_code; | |
572 /* Note that (le:SI X INT_MAX) is not the same as (lt:SI X INT_MIN). | |
573 We better have an assert here in case GCC does not properly | |
574 optimize it away. The INT_MAX here is 0x7fffffff for target. */ | |
575 rtx plus1 = gen_int_mode (INTVAL (operands[2]) + 1, SImode); | |
576 if (satisfies_constraint_Is15 (plus1)) | |
577 { | |
578 operands[2] = plus1; | |
579 cmp_code = NE; | |
580 if (code == LE) | |
581 { | |
582 /* LE, use slts instruction */ | |
583 emit_insn ( | |
584 gen_slts_compare (tmp_reg, operands[1], operands[2])); | |
585 } | |
586 else | |
587 { | |
588 /* LEU, use slt instruction */ | |
589 emit_insn ( | |
590 gen_slt_compare (tmp_reg, operands[1], operands[2])); | |
591 } | |
592 } | |
593 else | |
594 { | |
595 cmp_code = EQ; | |
596 if (code == LE) | |
597 { | |
598 /* LE, use slts instruction */ | |
599 emit_insn ( | |
600 gen_slts_compare (tmp_reg, operands[2], operands[1])); | |
601 } | |
602 else | |
603 { | |
604 /* LEU, use slt instruction */ | |
605 emit_insn ( | |
606 gen_slt_compare (tmp_reg, operands[2], operands[1])); | |
607 } | |
608 } | |
609 | |
610 PUT_CODE (operands[0], cmp_code); | |
611 operands[1] = tmp_reg; | |
612 operands[2] = const0_rtx; | |
613 emit_insn (gen_cbranchsi4 (operands[0], operands[1], | |
614 operands[2], operands[3])); | |
615 | |
616 return EXPAND_DONE; | |
617 } | |
618 else | |
619 { | |
620 /* LE reg_A, reg_B => !(LT reg_B, reg_A) */ | |
621 if (optimize_size || optimize == 0) | |
622 tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); | |
623 else | |
624 tmp_reg = gen_reg_rtx (SImode); | |
625 | |
626 if (code == LE) | |
627 { | |
628 /* LE, use slts instruction */ | |
629 emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1])); | |
630 } | |
631 else | |
632 { | |
633 /* LEU, use slt instruction */ | |
634 emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1])); | |
635 } | |
636 | |
637 PUT_CODE (operands[0], EQ); | |
638 operands[1] = tmp_reg; | |
639 operands[2] = const0_rtx; | |
640 emit_insn (gen_cbranchsi4 (operands[0], operands[1], | |
641 operands[2], operands[3])); | |
642 | |
643 return EXPAND_DONE; | |
644 } | |
645 | |
646 case EQ: | |
647 case NE: | |
648 /* NDS32 ISA has various form for eq/ne behavior no matter | |
649 what kind of the operand is. | |
650 So just generate original template rtx. */ | |
651 | |
652 /* Put operands[2] into register if operands[2] is a large | |
653 const_int or ISAv2. */ | |
654 if (GET_CODE (operands[2]) == CONST_INT | |
655 && (!satisfies_constraint_Is11 (operands[2]) | |
656 || TARGET_ISA_V2)) | |
657 operands[2] = force_reg (SImode, operands[2]); | |
658 | |
659 return EXPAND_CREATE_TEMPLATE; | |
660 | |
661 default: | |
662 return EXPAND_FAIL; | |
663 } | |
664 } | |
665 | |
666 enum nds32_expand_result_type | |
667 nds32_expand_cstore (rtx *operands) | |
668 { | |
669 rtx tmp_reg; | |
670 enum rtx_code code; | |
671 | |
672 code = GET_CODE (operands[1]); | |
673 | |
674 switch (code) | |
675 { | |
676 case EQ: | |
677 case NE: | |
678 if (GET_CODE (operands[3]) == CONST_INT) | |
679 { | |
680 /* reg_R = (reg_A == const_int_B) | |
681 --> xori reg_C, reg_A, const_int_B | |
682 slti reg_R, reg_C, const_int_1 | |
683 reg_R = (reg_A != const_int_B) | |
684 --> xori reg_C, reg_A, const_int_B | |
685 slti reg_R, const_int0, reg_C */ | |
686 tmp_reg = gen_reg_rtx (SImode); | |
687 | |
688 /* If the integer value is not in the range of imm15s, | |
689 we need to force register first because our addsi3 pattern | |
690 only accept nds32_rimm15s_operand predicate. */ | |
691 rtx new_imm = gen_int_mode (-INTVAL (operands[3]), SImode); | |
692 if (satisfies_constraint_Is15 (new_imm)) | |
693 emit_insn (gen_addsi3 (tmp_reg, operands[2], new_imm)); | |
694 else | |
695 { | |
696 if (!(satisfies_constraint_Iu15 (operands[3]) | |
697 || (TARGET_EXT_PERF | |
698 && satisfies_constraint_It15 (operands[3])))) | |
699 operands[3] = force_reg (SImode, operands[3]); | |
700 emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3])); | |
701 } | |
702 | |
703 if (code == EQ) | |
704 emit_insn (gen_slt_eq0 (operands[0], tmp_reg)); | |
705 else | |
706 emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg)); | |
707 | |
708 return EXPAND_DONE; | |
709 } | |
710 else | |
711 { | |
712 /* reg_R = (reg_A == reg_B) | |
713 --> xor reg_C, reg_A, reg_B | |
714 slti reg_R, reg_C, const_int_1 | |
715 reg_R = (reg_A != reg_B) | |
716 --> xor reg_C, reg_A, reg_B | |
717 slti reg_R, const_int0, reg_C */ | |
718 tmp_reg = gen_reg_rtx (SImode); | |
719 emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3])); | |
720 if (code == EQ) | |
721 emit_insn (gen_slt_eq0 (operands[0], tmp_reg)); | |
722 else | |
723 emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg)); | |
724 | |
725 return EXPAND_DONE; | |
726 } | |
727 case GT: | |
728 case GTU: | |
729 /* reg_R = (reg_A > reg_B) --> slt reg_R, reg_B, reg_A */ | |
730 /* reg_R = (reg_A > const_int_B) --> slt reg_R, const_int_B, reg_A */ | |
731 if (code == GT) | |
732 { | |
733 /* GT, use slts instruction */ | |
734 emit_insn (gen_slts_compare (operands[0], operands[3], operands[2])); | |
735 } | |
736 else | |
737 { | |
738 /* GTU, use slt instruction */ | |
739 emit_insn (gen_slt_compare (operands[0], operands[3], operands[2])); | |
740 } | |
741 | |
742 return EXPAND_DONE; | |
743 | |
744 case GE: | |
745 case GEU: | |
746 if (GET_CODE (operands[3]) == CONST_INT) | |
747 { | |
748 /* reg_R = (reg_A >= const_int_B) | |
749 --> movi reg_C, const_int_B - 1 | |
750 slt reg_R, reg_C, reg_A */ | |
751 tmp_reg = gen_reg_rtx (SImode); | |
752 | |
753 emit_insn (gen_movsi (tmp_reg, | |
754 gen_int_mode (INTVAL (operands[3]) - 1, | |
755 SImode))); | |
756 if (code == GE) | |
757 { | |
758 /* GE, use slts instruction */ | |
759 emit_insn (gen_slts_compare (operands[0], tmp_reg, operands[2])); | |
760 } | |
761 else | |
762 { | |
763 /* GEU, use slt instruction */ | |
764 emit_insn (gen_slt_compare (operands[0], tmp_reg, operands[2])); | |
765 } | |
766 | |
767 return EXPAND_DONE; | |
768 } | |
769 else | |
770 { | |
771 /* reg_R = (reg_A >= reg_B) | |
772 --> slt reg_R, reg_A, reg_B | |
773 xori reg_R, reg_R, const_int_1 */ | |
774 if (code == GE) | |
775 { | |
776 /* GE, use slts instruction */ | |
777 emit_insn (gen_slts_compare (operands[0], | |
778 operands[2], operands[3])); | |
779 } | |
780 else | |
781 { | |
782 /* GEU, use slt instruction */ | |
783 emit_insn (gen_slt_compare (operands[0], | |
784 operands[2], operands[3])); | |
785 } | |
786 | |
787 /* perform 'not' behavior */ | |
788 emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); | |
789 | |
790 return EXPAND_DONE; | |
791 } | |
792 | |
793 case LT: | |
794 case LTU: | |
795 /* reg_R = (reg_A < reg_B) --> slt reg_R, reg_A, reg_B */ | |
796 /* reg_R = (reg_A < const_int_B) --> slt reg_R, reg_A, const_int_B */ | |
797 if (code == LT) | |
798 { | |
799 /* LT, use slts instruction */ | |
800 emit_insn (gen_slts_compare (operands[0], operands[2], operands[3])); | |
801 } | |
802 else | |
803 { | |
804 /* LTU, use slt instruction */ | |
805 emit_insn (gen_slt_compare (operands[0], operands[2], operands[3])); | |
806 } | |
807 | |
808 return EXPAND_DONE; | |
809 | |
810 case LE: | |
811 case LEU: | |
812 if (GET_CODE (operands[3]) == CONST_INT) | |
813 { | |
814 /* reg_R = (reg_A <= const_int_B) | |
815 --> movi reg_C, const_int_B + 1 | |
816 slt reg_R, reg_A, reg_C */ | |
817 tmp_reg = gen_reg_rtx (SImode); | |
818 | |
819 emit_insn (gen_movsi (tmp_reg, | |
820 gen_int_mode (INTVAL (operands[3]) + 1, | |
821 SImode))); | |
822 if (code == LE) | |
823 { | |
824 /* LE, use slts instruction */ | |
825 emit_insn (gen_slts_compare (operands[0], operands[2], tmp_reg)); | |
826 } | |
827 else | |
828 { | |
829 /* LEU, use slt instruction */ | |
830 emit_insn (gen_slt_compare (operands[0], operands[2], tmp_reg)); | |
831 } | |
832 | |
833 return EXPAND_DONE; | |
834 } | |
835 else | |
836 { | |
837 /* reg_R = (reg_A <= reg_B) --> slt reg_R, reg_B, reg_A | |
838 xori reg_R, reg_R, const_int_1 */ | |
839 if (code == LE) | |
840 { | |
841 /* LE, use slts instruction */ | |
842 emit_insn (gen_slts_compare (operands[0], | |
843 operands[3], operands[2])); | |
844 } | |
845 else | |
846 { | |
847 /* LEU, use slt instruction */ | |
848 emit_insn (gen_slt_compare (operands[0], | |
849 operands[3], operands[2])); | |
850 } | |
851 | |
852 /* perform 'not' behavior */ | |
853 emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); | |
854 | |
855 return EXPAND_DONE; | |
856 } | |
857 | |
858 | |
859 default: | |
860 gcc_unreachable (); | |
861 } | |
862 } | |
863 | |
864 void | |
865 nds32_expand_float_cbranch (rtx *operands) | |
866 { | |
867 enum rtx_code code = GET_CODE (operands[0]); | |
868 enum rtx_code new_code = code; | |
869 rtx cmp_op0 = operands[1]; | |
870 rtx cmp_op1 = operands[2]; | |
871 rtx tmp_reg; | |
872 rtx tmp; | |
873 | |
874 int reverse = 0; | |
875 | |
876 /* Main Goal: Use compare instruction + branch instruction. | |
877 | |
878 For example: | |
879 GT, GE: swap condition and swap operands and generate | |
880 compare instruction(LT, LE) + branch not equal instruction. | |
881 | |
882 UNORDERED, LT, LE, EQ: no need to change and generate | |
883 compare instruction(UNORDERED, LT, LE, EQ) + branch not equal instruction. | |
884 | |
885 ORDERED, NE: reverse condition and generate | |
886 compare instruction(EQ) + branch equal instruction. */ | |
887 | |
888 switch (code) | |
889 { | |
890 case GT: | |
891 case GE: | |
892 tmp = cmp_op0; | |
893 cmp_op0 = cmp_op1; | |
894 cmp_op1 = tmp; | |
895 new_code = swap_condition (new_code); | |
896 break; | |
897 case UNORDERED: | |
898 case LT: | |
899 case LE: | |
900 case EQ: | |
901 break; | |
902 case ORDERED: | |
903 case NE: | |
904 new_code = reverse_condition (new_code); | |
905 reverse = 1; | |
906 break; | |
907 case UNGT: | |
908 case UNGE: | |
909 new_code = reverse_condition_maybe_unordered (new_code); | |
910 reverse = 1; | |
911 break; | |
912 case UNLT: | |
913 case UNLE: | |
914 new_code = reverse_condition_maybe_unordered (new_code); | |
915 tmp = cmp_op0; | |
916 cmp_op0 = cmp_op1; | |
917 cmp_op1 = tmp; | |
918 new_code = swap_condition (new_code); | |
919 reverse = 1; | |
920 break; | |
921 default: | |
922 return; | |
923 } | |
924 | |
925 tmp_reg = gen_reg_rtx (SImode); | |
926 emit_insn (gen_rtx_SET (tmp_reg, | |
927 gen_rtx_fmt_ee (new_code, SImode, | |
928 cmp_op0, cmp_op1))); | |
929 | |
930 PUT_CODE (operands[0], reverse ? EQ : NE); | |
931 emit_insn (gen_cbranchsi4 (operands[0], tmp_reg, | |
932 const0_rtx, operands[3])); | |
933 } | |
934 | |
935 void | |
936 nds32_expand_float_cstore (rtx *operands) | |
937 { | |
938 enum rtx_code code = GET_CODE (operands[1]); | |
939 enum rtx_code new_code = code; | |
940 machine_mode mode = GET_MODE (operands[2]); | |
941 | |
942 rtx cmp_op0 = operands[2]; | |
943 rtx cmp_op1 = operands[3]; | |
944 rtx tmp; | |
945 | |
946 /* Main Goal: Use compare instruction to store value. | |
947 | |
948 For example: | |
949 GT, GE: swap condition and swap operands. | |
950 reg_R = (reg_A > reg_B) --> fcmplt reg_R, reg_B, reg_A | |
951 reg_R = (reg_A >= reg_B) --> fcmple reg_R, reg_B, reg_A | |
952 | |
953 LT, LE, EQ: no need to change, it is already LT, LE, EQ. | |
954 reg_R = (reg_A < reg_B) --> fcmplt reg_R, reg_A, reg_B | |
955 reg_R = (reg_A <= reg_B) --> fcmple reg_R, reg_A, reg_B | |
956 reg_R = (reg_A == reg_B) --> fcmpeq reg_R, reg_A, reg_B | |
957 | |
958 ORDERED: reverse condition and using xor insturction to achieve 'ORDERED'. | |
959 reg_R = (reg_A != reg_B) --> fcmpun reg_R, reg_A, reg_B | |
960 xor reg_R, reg_R, const1_rtx | |
961 | |
962 NE: reverse condition and using xor insturction to achieve 'NE'. | |
963 reg_R = (reg_A != reg_B) --> fcmpeq reg_R, reg_A, reg_B | |
964 xor reg_R, reg_R, const1_rtx */ | |
965 switch (code) | |
966 { | |
967 case GT: | |
968 case GE: | |
969 tmp = cmp_op0; | |
970 cmp_op0 = cmp_op1; | |
971 cmp_op1 =tmp; | |
972 new_code = swap_condition (new_code); | |
973 break; | |
974 case UNORDERED: | |
975 case LT: | |
976 case LE: | |
977 case EQ: | |
978 break; | |
979 case ORDERED: | |
980 if (mode == SFmode) | |
981 emit_insn (gen_cmpsf_un (operands[0], cmp_op0, cmp_op1)); | |
982 else | |
983 emit_insn (gen_cmpdf_un (operands[0], cmp_op0, cmp_op1)); | |
984 | |
985 emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); | |
986 return; | |
987 case NE: | |
988 if (mode == SFmode) | |
989 emit_insn (gen_cmpsf_eq (operands[0], cmp_op0, cmp_op1)); | |
990 else | |
991 emit_insn (gen_cmpdf_eq (operands[0], cmp_op0, cmp_op1)); | |
992 | |
993 emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); | |
994 return; | |
995 default: | |
996 return; | |
997 } | |
998 | |
999 emit_insn (gen_rtx_SET (operands[0], | |
1000 gen_rtx_fmt_ee (new_code, SImode, | |
1001 cmp_op0, cmp_op1))); | |
1002 } | |
1003 | |
1004 enum nds32_expand_result_type | |
1005 nds32_expand_movcc (rtx *operands) | |
1006 { | |
1007 enum rtx_code code = GET_CODE (operands[1]); | |
1008 enum rtx_code new_code = code; | |
1009 machine_mode cmp0_mode = GET_MODE (XEXP (operands[1], 0)); | |
1010 rtx cmp_op0 = XEXP (operands[1], 0); | |
1011 rtx cmp_op1 = XEXP (operands[1], 1); | |
1012 rtx tmp; | |
1013 | |
1014 if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE) | |
1015 && XEXP (operands[1], 1) == const0_rtx) | |
1016 { | |
1017 /* If the operands[1] rtx is already (eq X 0) or (ne X 0), | |
1018 we have gcc generate original template rtx. */ | |
1019 return EXPAND_CREATE_TEMPLATE; | |
1020 } | |
1021 else if ((TARGET_FPU_SINGLE && cmp0_mode == SFmode) | |
1022 || (TARGET_FPU_DOUBLE && cmp0_mode == DFmode)) | |
1023 { | |
1024 nds32_expand_float_movcc (operands); | |
1025 } | |
1026 else | |
1027 { | |
1028 /* Since there is only 'slt'(Set when Less Than) instruction for | |
1029 comparison in Andes ISA, the major strategy we use here is to | |
1030 convert conditional move into 'LT + EQ' or 'LT + NE' rtx combination. | |
1031 We design constraints properly so that the reload phase will assist | |
1032 to make one source operand to use same register as result operand. | |
1033 Then we can use cmovz/cmovn to catch the other source operand | |
1034 which has different register. */ | |
1035 int reverse = 0; | |
1036 | |
1037 /* Main Goal: Use 'LT + EQ' or 'LT + NE' to target "then" part | |
1038 Strategy : Reverse condition and swap comparison operands | |
1039 | |
1040 For example: | |
1041 | |
1042 a <= b ? P : Q (LE or LEU) | |
1043 --> a > b ? Q : P (reverse condition) | |
1044 --> b < a ? Q : P (swap comparison operands to achieve 'LT/LTU') | |
1045 | |
1046 a >= b ? P : Q (GE or GEU) | |
1047 --> a < b ? Q : P (reverse condition to achieve 'LT/LTU') | |
1048 | |
1049 a < b ? P : Q (LT or LTU) | |
1050 --> (NO NEED TO CHANGE, it is already 'LT/LTU') | |
1051 | |
1052 a > b ? P : Q (GT or GTU) | |
1053 --> b < a ? P : Q (swap comparison operands to achieve 'LT/LTU') */ | |
1054 switch (code) | |
1055 { | |
1056 case GE: case GEU: case LE: case LEU: | |
1057 new_code = reverse_condition (code); | |
1058 reverse = 1; | |
1059 break; | |
1060 case EQ: | |
1061 case NE: | |
1062 /* no need to reverse condition */ | |
1063 break; | |
1064 default: | |
1065 return EXPAND_FAIL; | |
1066 } | |
1067 | |
1068 /* For '>' comparison operator, we swap operands | |
1069 so that we can have 'LT/LTU' operator. */ | |
1070 if (new_code == GT || new_code == GTU) | |
1071 { | |
1072 tmp = cmp_op0; | |
1073 cmp_op0 = cmp_op1; | |
1074 cmp_op1 = tmp; | |
1075 | |
1076 new_code = swap_condition (new_code); | |
1077 } | |
1078 | |
1079 /* Use a temporary register to store slt/slts result. */ | |
1080 tmp = gen_reg_rtx (SImode); | |
1081 | |
1082 if (new_code == EQ || new_code == NE) | |
1083 { | |
1084 emit_insn (gen_xorsi3 (tmp, cmp_op0, cmp_op1)); | |
1085 /* tmp == 0 if cmp_op0 == cmp_op1. */ | |
1086 operands[1] = gen_rtx_fmt_ee (new_code, VOIDmode, tmp, const0_rtx); | |
1087 } | |
1088 else | |
1089 { | |
1090 /* This emit_insn will create corresponding 'slt/slts' | |
1091 insturction. */ | |
1092 if (new_code == LT) | |
1093 emit_insn (gen_slts_compare (tmp, cmp_op0, cmp_op1)); | |
1094 else if (new_code == LTU) | |
1095 emit_insn (gen_slt_compare (tmp, cmp_op0, cmp_op1)); | |
1096 else | |
1097 gcc_unreachable (); | |
1098 | |
1099 /* Change comparison semantic into (eq X 0) or (ne X 0) behavior | |
1100 so that cmovz or cmovn will be matched later. | |
1101 | |
1102 For reverse condition cases, we want to create a semantic that: | |
1103 (eq X 0) --> pick up "else" part | |
1104 For normal cases, we want to create a semantic that: | |
1105 (ne X 0) --> pick up "then" part | |
1106 | |
1107 Later we will have cmovz/cmovn instruction pattern to | |
1108 match corresponding behavior and output instruction. */ | |
1109 operands[1] = gen_rtx_fmt_ee (reverse ? EQ : NE, | |
1110 VOIDmode, tmp, const0_rtx); | |
1111 } | |
1112 } | |
1113 return EXPAND_CREATE_TEMPLATE; | |
1114 } | |
1115 | |
1116 void | |
1117 nds32_expand_float_movcc (rtx *operands) | |
1118 { | |
1119 if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE) | |
1120 && GET_MODE (XEXP (operands[1], 0)) == SImode | |
1121 && XEXP (operands[1], 1) == const0_rtx) | |
1122 { | |
1123 /* If the operands[1] rtx is already (eq X 0) or (ne X 0), | |
1124 we have gcc generate original template rtx. */ | |
1125 return; | |
1126 } | |
1127 else | |
1128 { | |
1129 enum rtx_code code = GET_CODE (operands[1]); | |
1130 enum rtx_code new_code = code; | |
1131 machine_mode cmp0_mode = GET_MODE (XEXP (operands[1], 0)); | |
1132 machine_mode cmp1_mode = GET_MODE (XEXP (operands[1], 1)); | |
1133 rtx cmp_op0 = XEXP (operands[1], 0); | |
1134 rtx cmp_op1 = XEXP (operands[1], 1); | |
1135 rtx tmp; | |
1136 | |
1137 /* Compare instruction Operations: (cmp_op0 condition cmp_op1) ? 1 : 0, | |
1138 when result is 1, and 'reverse' be set 1 for fcmovzs instructuin. */ | |
1139 int reverse = 0; | |
1140 | |
1141 /* Main Goal: Use cmpare instruction + conditional move instruction. | |
1142 Strategy : swap condition and swap comparison operands. | |
1143 | |
1144 For example: | |
1145 a > b ? P : Q (GT) | |
1146 --> a < b ? Q : P (swap condition) | |
1147 --> b < a ? Q : P (swap comparison operands to achieve 'GT') | |
1148 | |
1149 a >= b ? P : Q (GE) | |
1150 --> a <= b ? Q : P (swap condition) | |
1151 --> b <= a ? Q : P (swap comparison operands to achieve 'GE') | |
1152 | |
1153 a < b ? P : Q (LT) | |
1154 --> (NO NEED TO CHANGE, it is already 'LT') | |
1155 | |
1156 a >= b ? P : Q (LE) | |
1157 --> (NO NEED TO CHANGE, it is already 'LE') | |
1158 | |
1159 a == b ? P : Q (EQ) | |
1160 --> (NO NEED TO CHANGE, it is already 'EQ') */ | |
1161 | |
1162 switch (code) | |
1163 { | |
1164 case GT: | |
1165 case GE: | |
1166 tmp = cmp_op0; | |
1167 cmp_op0 = cmp_op1; | |
1168 cmp_op1 =tmp; | |
1169 new_code = swap_condition (new_code); | |
1170 break; | |
1171 case UNORDERED: | |
1172 case LT: | |
1173 case LE: | |
1174 case EQ: | |
1175 break; | |
1176 case ORDERED: | |
1177 case NE: | |
1178 reverse = 1; | |
1179 new_code = reverse_condition (new_code); | |
1180 break; | |
1181 case UNGT: | |
1182 case UNGE: | |
1183 new_code = reverse_condition_maybe_unordered (new_code); | |
1184 reverse = 1; | |
1185 break; | |
1186 case UNLT: | |
1187 case UNLE: | |
1188 new_code = reverse_condition_maybe_unordered (new_code); | |
1189 tmp = cmp_op0; | |
1190 cmp_op0 = cmp_op1; | |
1191 cmp_op1 = tmp; | |
1192 new_code = swap_condition (new_code); | |
1193 reverse = 1; | |
1194 break; | |
1195 default: | |
1196 return; | |
1197 } | |
1198 | |
1199 /* Use a temporary register to store fcmpxxs result. */ | |
1200 tmp = gen_reg_rtx (SImode); | |
1201 | |
1202 /* Create float compare instruction for SFmode and DFmode, | |
1203 other MODE using cstoresi create compare instruction. */ | |
1204 if ((cmp0_mode == DFmode || cmp0_mode == SFmode) | |
1205 && (cmp1_mode == DFmode || cmp1_mode == SFmode)) | |
1206 { | |
1207 /* This emit_insn create corresponding float compare instruction */ | |
1208 emit_insn (gen_rtx_SET (tmp, | |
1209 gen_rtx_fmt_ee (new_code, SImode, | |
1210 cmp_op0, cmp_op1))); | |
1211 } | |
1212 else | |
1213 { | |
1214 /* This emit_insn using cstoresi create corresponding | |
1215 compare instruction */ | |
1216 PUT_CODE (operands[1], new_code); | |
1217 emit_insn (gen_cstoresi4 (tmp, operands[1], | |
1218 cmp_op0, cmp_op1)); | |
1219 } | |
1220 /* operands[1] crete corresponding condition move instruction | |
1221 for fcmovzs and fcmovns. */ | |
1222 operands[1] = gen_rtx_fmt_ee (reverse ? EQ : NE, | |
1223 VOIDmode, tmp, const0_rtx); | |
1224 } | |
1225 } | |
1226 | |
1227 void | |
1228 nds32_emit_push_fpr_callee_saved (int base_offset) | |
1229 { | |
1230 rtx fpu_insn; | |
1231 rtx reg, mem; | |
1232 unsigned int regno = cfun->machine->callee_saved_first_fpr_regno; | |
1233 unsigned int last_fpr = cfun->machine->callee_saved_last_fpr_regno; | |
1234 | |
1235 while (regno <= last_fpr) | |
1236 { | |
1237 /* Handling two registers, using fsdi instruction. */ | |
1238 reg = gen_rtx_REG (DFmode, regno); | |
1239 mem = gen_frame_mem (DFmode, plus_constant (Pmode, | |
1240 stack_pointer_rtx, | |
1241 base_offset)); | |
1242 base_offset += 8; | |
1243 regno += 2; | |
1244 fpu_insn = emit_move_insn (mem, reg); | |
1245 RTX_FRAME_RELATED_P (fpu_insn) = 1; | |
1246 } | |
1247 } | |
1248 | |
1249 void | |
1250 nds32_emit_pop_fpr_callee_saved (int gpr_padding_size) | |
1251 { | |
1252 rtx fpu_insn; | |
1253 rtx reg, mem, addr; | |
1254 rtx dwarf, adjust_sp_rtx; | |
1255 unsigned int regno = cfun->machine->callee_saved_first_fpr_regno; | |
1256 unsigned int last_fpr = cfun->machine->callee_saved_last_fpr_regno; | |
1257 int padding = 0; | |
1258 | |
1259 while (regno <= last_fpr) | |
1260 { | |
1261 /* Handling two registers, using fldi.bi instruction. */ | |
1262 if ((regno + 1) >= last_fpr) | |
1263 padding = gpr_padding_size; | |
1264 | |
1265 reg = gen_rtx_REG (DFmode, (regno)); | |
1266 addr = gen_rtx_POST_MODIFY (Pmode, stack_pointer_rtx, | |
1267 gen_rtx_PLUS (Pmode, stack_pointer_rtx, | |
1268 GEN_INT (8 + padding))); | |
1269 mem = gen_frame_mem (DFmode, addr); | |
1270 regno += 2; | |
1271 fpu_insn = emit_move_insn (reg, mem); | |
1272 | |
1273 adjust_sp_rtx = | |
1274 gen_rtx_SET (stack_pointer_rtx, | |
1275 plus_constant (Pmode, stack_pointer_rtx, | |
1276 8 + padding)); | |
1277 | |
1278 dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, NULL_RTX); | |
1279 /* Tell gcc we adjust SP in this insn. */ | |
1280 dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, copy_rtx (adjust_sp_rtx), | |
1281 dwarf); | |
1282 RTX_FRAME_RELATED_P (fpu_insn) = 1; | |
1283 REG_NOTES (fpu_insn) = dwarf; | |
1284 } | |
1285 } | |
1286 | |
1287 void | |
1288 nds32_emit_v3pop_fpr_callee_saved (int base) | |
1289 { | |
1290 int fpu_base_addr = base; | |
1291 int regno; | |
1292 rtx fpu_insn; | |
1293 rtx reg, mem; | |
1294 rtx dwarf; | |
1295 | |
1296 regno = cfun->machine->callee_saved_first_fpr_regno; | |
1297 while (regno <= cfun->machine->callee_saved_last_fpr_regno) | |
1298 { | |
1299 /* Handling two registers, using fldi instruction. */ | |
1300 reg = gen_rtx_REG (DFmode, regno); | |
1301 mem = gen_frame_mem (DFmode, plus_constant (Pmode, | |
1302 stack_pointer_rtx, | |
1303 fpu_base_addr)); | |
1304 fpu_base_addr += 8; | |
1305 regno += 2; | |
1306 fpu_insn = emit_move_insn (reg, mem); | |
1307 dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, NULL_RTX); | |
1308 RTX_FRAME_RELATED_P (fpu_insn) = 1; | |
1309 REG_NOTES (fpu_insn) = dwarf; | |
1310 } | |
1311 } | |
1312 | |
1313 enum nds32_expand_result_type | |
1314 nds32_expand_extv (rtx *operands) | |
1315 { | |
1316 gcc_assert (CONST_INT_P (operands[2]) && CONST_INT_P (operands[3])); | |
1317 HOST_WIDE_INT width = INTVAL (operands[2]); | |
1318 HOST_WIDE_INT bitpos = INTVAL (operands[3]); | |
1319 rtx dst = operands[0]; | |
1320 rtx src = operands[1]; | |
1321 | |
1322 if (MEM_P (src) | |
1323 && width == 32 | |
1324 && (bitpos % BITS_PER_UNIT) == 0 | |
1325 && GET_MODE_BITSIZE (GET_MODE (dst)) == width) | |
1326 { | |
1327 rtx newmem = adjust_address (src, GET_MODE (dst), | |
1328 bitpos / BITS_PER_UNIT); | |
1329 | |
1330 rtx base_addr = force_reg (Pmode, XEXP (newmem, 0)); | |
1331 | |
1332 emit_insn (gen_unaligned_loadsi (dst, base_addr)); | |
1333 | |
1334 return EXPAND_DONE; | |
1335 } | |
1336 return EXPAND_FAIL; | |
1337 } | |
1338 | |
1339 enum nds32_expand_result_type | |
1340 nds32_expand_insv (rtx *operands) | |
1341 { | |
1342 gcc_assert (CONST_INT_P (operands[1]) && CONST_INT_P (operands[2])); | |
1343 HOST_WIDE_INT width = INTVAL (operands[1]); | |
1344 HOST_WIDE_INT bitpos = INTVAL (operands[2]); | |
1345 rtx dst = operands[0]; | |
1346 rtx src = operands[3]; | |
1347 | |
1348 if (MEM_P (dst) | |
1349 && width == 32 | |
1350 && (bitpos % BITS_PER_UNIT) == 0 | |
1351 && GET_MODE_BITSIZE (GET_MODE (src)) == width) | |
1352 { | |
1353 rtx newmem = adjust_address (dst, GET_MODE (src), | |
1354 bitpos / BITS_PER_UNIT); | |
1355 | |
1356 rtx base_addr = force_reg (Pmode, XEXP (newmem, 0)); | |
1357 | |
1358 emit_insn (gen_unaligned_storesi (base_addr, src)); | |
1359 | |
1360 return EXPAND_DONE; | |
1361 } | |
1362 return EXPAND_FAIL; | |
1363 } | |
1364 | |
1365 /* ------------------------------------------------------------------------ */ | |
1366 | |
1367 /* Function to generate PC relative jump table. | |
1368 Refer to nds32.md for more details. | |
1369 | |
1370 The following is the sample for the case that diff value | |
1371 can be presented in '.short' size. | |
1372 | |
1373 addi $r1, $r1, -(case_lower_bound) | |
1374 slti $ta, $r1, (case_number) | |
1375 beqz $ta, .L_skip_label | |
1376 | |
1377 la $ta, .L35 ! get jump table address | |
1378 lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry | |
1379 addi $ta, $r1, $ta | |
1380 jr5 $ta | |
1381 | |
1382 ! jump table entry | |
1383 L35: | |
1384 .short .L25-.L35 | |
1385 .short .L26-.L35 | |
1386 .short .L27-.L35 | |
1387 .short .L28-.L35 | |
1388 .short .L29-.L35 | |
1389 .short .L30-.L35 | |
1390 .short .L31-.L35 | |
1391 .short .L32-.L35 | |
1392 .short .L33-.L35 | |
1393 .short .L34-.L35 */ | |
1394 const char * | |
1395 nds32_output_casesi_pc_relative (rtx *operands) | |
1396 { | |
1397 machine_mode mode; | |
1398 rtx diff_vec; | |
1399 | |
1400 diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[1]))); | |
1401 | |
1402 gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC); | |
1403 | |
1404 /* Step C: "t <-- operands[1]". */ | |
1405 if (flag_pic) | |
1406 { | |
1407 output_asm_insn ("sethi\t$ta, hi20(%l1@GOTOFF)", operands); | |
1408 output_asm_insn ("ori\t$ta, $ta, lo12(%l1@GOTOFF)", operands); | |
1409 output_asm_insn ("add\t$ta, $ta, $gp", operands); | |
1410 } | |
1411 else | |
1412 output_asm_insn ("la\t$ta, %l1", operands); | |
1413 | |
1414 /* Get the mode of each element in the difference vector. */ | |
1415 mode = GET_MODE (diff_vec); | |
1416 | |
1417 /* Step D: "z <-- (mem (plus (operands[0] << m) t))", | |
1418 where m is 0, 1, or 2 to load address-diff value from table. */ | |
1419 switch (mode) | |
1420 { | |
1421 case E_QImode: | |
1422 output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands); | |
1423 break; | |
1424 case E_HImode: | |
1425 output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands); | |
1426 break; | |
1427 case E_SImode: | |
1428 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands); | |
1429 break; | |
1430 default: | |
1431 gcc_unreachable (); | |
1432 } | |
1433 | |
1434 /* Step E: "t <-- z + t". | |
1435 Add table label_ref with address-diff value to | |
1436 obtain target case address. */ | |
1437 output_asm_insn ("add\t$ta, %2, $ta", operands); | |
1438 | |
1439 /* Step F: jump to target with register t. */ | |
1440 if (TARGET_16_BIT) | |
1441 return "jr5\t$ta"; | |
1442 else | |
1443 return "jr\t$ta"; | |
1444 } | |
1445 | |
1446 /* Function to generate normal jump table. */ | |
1447 const char * | |
1448 nds32_output_casesi (rtx *operands) | |
1449 { | |
1450 /* Step C: "t <-- operands[1]". */ | |
1451 if (flag_pic) | |
1452 { | |
1453 output_asm_insn ("sethi\t$ta, hi20(%l1@GOTOFF)", operands); | |
1454 output_asm_insn ("ori\t$ta, $ta, lo12(%l1@GOTOFF)", operands); | |
1455 output_asm_insn ("add\t$ta, $ta, $gp", operands); | |
1456 } | |
1457 else | |
1458 output_asm_insn ("la\t$ta, %l1", operands); | |
1459 | |
1460 /* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */ | |
1461 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands); | |
1462 | |
1463 /* No need to perform Step E, which is only used for | |
1464 pc relative jump table. */ | |
1465 | |
1466 /* Step F: jump to target with register z. */ | |
1467 if (TARGET_16_BIT) | |
1468 return "jr5\t%2"; | |
1469 else | |
1470 return "jr\t%2"; | |
1471 } | |
1472 | |
1473 /* Function to return memory format. */ | |
59 enum nds32_16bit_address_type | 1474 enum nds32_16bit_address_type |
60 nds32_mem_format (rtx op) | 1475 nds32_mem_format (rtx op) |
61 { | 1476 { |
62 machine_mode mode_test; | 1477 machine_mode mode_test; |
63 int val; | 1478 int val; |
69 mode_test = GET_MODE (op); | 1484 mode_test = GET_MODE (op); |
70 | 1485 |
71 op = XEXP (op, 0); | 1486 op = XEXP (op, 0); |
72 | 1487 |
73 /* 45 format. */ | 1488 /* 45 format. */ |
74 if (GET_CODE (op) == REG && (mode_test == SImode)) | 1489 if (GET_CODE (op) == REG |
1490 && ((mode_test == SImode) || (mode_test == SFmode))) | |
75 return ADDRESS_REG; | 1491 return ADDRESS_REG; |
76 | 1492 |
77 /* 333 format for QI/HImode. */ | 1493 /* 333 format for QI/HImode. */ |
78 if (GET_CODE (op) == REG && (REGNO (op) < R8_REGNUM)) | 1494 if (GET_CODE (op) == REG && (REGNO (op) < R8_REGNUM)) |
79 return ADDRESS_LO_REG_IMM3U; | 1495 return ADDRESS_LO_REG_IMM3U; |
80 | 1496 |
81 /* post_inc 333 format. */ | 1497 /* post_inc 333 format. */ |
82 if ((GET_CODE (op) == POST_INC) && (mode_test == SImode)) | 1498 if ((GET_CODE (op) == POST_INC) |
1499 && ((mode_test == SImode) || (mode_test == SFmode))) | |
83 { | 1500 { |
84 regno = REGNO(XEXP (op, 0)); | 1501 regno = REGNO(XEXP (op, 0)); |
85 | 1502 |
86 if (regno < 8) | 1503 if (regno < 8) |
87 return ADDRESS_POST_INC_LO_REG_IMM3U; | 1504 return ADDRESS_POST_INC_LO_REG_IMM3U; |
88 } | 1505 } |
89 | 1506 |
90 /* post_inc 333 format. */ | 1507 /* post_inc 333 format. */ |
91 if ((GET_CODE (op) == POST_MODIFY) | 1508 if ((GET_CODE (op) == POST_MODIFY) |
92 && (mode_test == SImode) | 1509 && ((mode_test == SImode) || (mode_test == SFmode)) |
93 && (REG_P (XEXP (XEXP (op, 1), 0))) | 1510 && (REG_P (XEXP (XEXP (op, 1), 0))) |
94 && (CONST_INT_P (XEXP (XEXP (op, 1), 1)))) | 1511 && (CONST_INT_P (XEXP (XEXP (op, 1), 1)))) |
95 { | 1512 { |
96 regno = REGNO (XEXP (XEXP (op, 1), 0)); | 1513 regno = REGNO (XEXP (XEXP (op, 1), 0)); |
97 val = INTVAL (XEXP (XEXP (op, 1), 1)); | 1514 val = INTVAL (XEXP (XEXP (op, 1), 1)); |
98 if (regno < 8 && val < 32) | 1515 if (regno < 8 && val > 0 && val < 32) |
99 return ADDRESS_POST_INC_LO_REG_IMM3U; | 1516 return ADDRESS_POST_MODIFY_LO_REG_IMM3U; |
100 } | 1517 } |
101 | 1518 |
102 if ((GET_CODE (op) == PLUS) | 1519 if ((GET_CODE (op) == PLUS) |
103 && (GET_CODE (XEXP (op, 0)) == REG) | 1520 && (GET_CODE (XEXP (op, 0)) == REG) |
104 && (GET_CODE (XEXP (op, 1)) == CONST_INT)) | 1521 && (GET_CODE (XEXP (op, 1)) == CONST_INT)) |
105 { | 1522 { |
106 val = INTVAL (XEXP (op, 1)); | 1523 val = INTVAL (XEXP (op, 1)); |
107 | 1524 |
108 regno = REGNO(XEXP (op, 0)); | 1525 regno = REGNO(XEXP (op, 0)); |
109 | 1526 |
110 if (regno > 7 | 1527 if (regno > 8 |
111 && regno != SP_REGNUM | 1528 && regno != SP_REGNUM |
112 && regno != FP_REGNUM) | 1529 && regno != FP_REGNUM) |
113 return ADDRESS_NOT_16BIT_FORMAT; | 1530 return ADDRESS_NOT_16BIT_FORMAT; |
114 | 1531 |
115 switch (mode_test) | 1532 switch (mode_test) |
127 break; | 1544 break; |
128 | 1545 |
129 case E_SImode: | 1546 case E_SImode: |
130 case E_SFmode: | 1547 case E_SFmode: |
131 case E_DFmode: | 1548 case E_DFmode: |
1549 /* r8 imply fe format. */ | |
1550 if ((regno == 8) && | |
1551 (val >= -128 && val <= -4 && (val % 4 == 0))) | |
1552 return ADDRESS_R8_IMM7U; | |
132 /* fp imply 37 format. */ | 1553 /* fp imply 37 format. */ |
133 if ((regno == FP_REGNUM) && | 1554 if ((regno == FP_REGNUM) && |
134 (val >= 0 && val < 512 && (val % 4 == 0))) | 1555 (val >= 0 && val < 512 && (val % 4 == 0))) |
135 return ADDRESS_FP_IMM7U; | 1556 return ADDRESS_FP_IMM7U; |
136 /* sp imply 37 format. */ | 1557 /* sp imply 37 format. */ |
169 case ADDRESS_LO_REG_IMM3U: | 1590 case ADDRESS_LO_REG_IMM3U: |
170 snprintf (pattern, sizeof (pattern), "s%ci333\t%%1, %%0", size); | 1591 snprintf (pattern, sizeof (pattern), "s%ci333\t%%1, %%0", size); |
171 output_asm_insn (pattern, operands); | 1592 output_asm_insn (pattern, operands); |
172 break; | 1593 break; |
173 case ADDRESS_POST_INC_LO_REG_IMM3U: | 1594 case ADDRESS_POST_INC_LO_REG_IMM3U: |
174 snprintf (pattern, sizeof (pattern), "s%ci333.bi\t%%1, %%0", size); | 1595 snprintf (pattern, sizeof (pattern), "swi333.bi\t%%1, %%0, 4"); |
1596 output_asm_insn (pattern, operands); | |
1597 break; | |
1598 case ADDRESS_POST_MODIFY_LO_REG_IMM3U: | |
1599 snprintf (pattern, sizeof (pattern), "swi333.bi\t%%1, %%0"); | |
175 output_asm_insn (pattern, operands); | 1600 output_asm_insn (pattern, operands); |
176 break; | 1601 break; |
177 case ADDRESS_FP_IMM7U: | 1602 case ADDRESS_FP_IMM7U: |
178 output_asm_insn ("swi37\t%1, %0", operands); | 1603 output_asm_insn ("swi37\t%1, %0", operands); |
179 break; | 1604 break; |
208 case ADDRESS_LO_REG_IMM3U: | 1633 case ADDRESS_LO_REG_IMM3U: |
209 snprintf (pattern, sizeof (pattern), "l%ci333\t%%0, %%1", size); | 1634 snprintf (pattern, sizeof (pattern), "l%ci333\t%%0, %%1", size); |
210 output_asm_insn (pattern, operands); | 1635 output_asm_insn (pattern, operands); |
211 break; | 1636 break; |
212 case ADDRESS_POST_INC_LO_REG_IMM3U: | 1637 case ADDRESS_POST_INC_LO_REG_IMM3U: |
213 snprintf (pattern, sizeof (pattern), "l%ci333.bi\t%%0, %%1", size); | 1638 snprintf (pattern, sizeof (pattern), "lwi333.bi\t%%0, %%1, 4"); |
214 output_asm_insn (pattern, operands); | 1639 output_asm_insn (pattern, operands); |
1640 break; | |
1641 case ADDRESS_POST_MODIFY_LO_REG_IMM3U: | |
1642 snprintf (pattern, sizeof (pattern), "lwi333.bi\t%%0, %%1"); | |
1643 output_asm_insn (pattern, operands); | |
1644 break; | |
1645 case ADDRESS_R8_IMM7U: | |
1646 output_asm_insn ("lwi45.fe\t%0, %e1", operands); | |
215 break; | 1647 break; |
216 case ADDRESS_FP_IMM7U: | 1648 case ADDRESS_FP_IMM7U: |
217 output_asm_insn ("lwi37\t%0, %1", operands); | 1649 output_asm_insn ("lwi37\t%0, %1", operands); |
218 break; | 1650 break; |
219 case ADDRESS_SP_IMM7U: | 1651 case ADDRESS_SP_IMM7U: |
555 int rb_va_args = cfun->machine->va_args_first_regno; | 1987 int rb_va_args = cfun->machine->va_args_first_regno; |
556 int re_va_args = cfun->machine->va_args_last_regno; | 1988 int re_va_args = cfun->machine->va_args_last_regno; |
557 int last_argument_regno = NDS32_FIRST_GPR_REGNUM | 1989 int last_argument_regno = NDS32_FIRST_GPR_REGNUM |
558 + NDS32_MAX_GPR_REGS_FOR_ARGS | 1990 + NDS32_MAX_GPR_REGS_FOR_ARGS |
559 - 1; | 1991 - 1; |
1992 /* Pick up first and last eh data regno for further use. */ | |
1993 int rb_eh_data = cfun->machine->eh_return_data_first_regno; | |
1994 int re_eh_data = cfun->machine->eh_return_data_last_regno; | |
1995 int first_eh_data_regno = EH_RETURN_DATA_REGNO (0); | |
560 /* Pick up callee-saved first regno and last regno for further use. */ | 1996 /* Pick up callee-saved first regno and last regno for further use. */ |
561 int rb_callee_saved = cfun->machine->callee_saved_first_gpr_regno; | 1997 int rb_callee_saved = cfun->machine->callee_saved_first_gpr_regno; |
562 int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; | 1998 int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; |
563 | 1999 |
564 /* First we need to check if we are pushing argument registers not used | 2000 /* First we need to check if we are pushing argument registers not used |
574 /* We use output_asm_insn() to output assembly code by ourself. */ | 2010 /* We use output_asm_insn() to output assembly code by ourself. */ |
575 output_asm_insn (pattern, operands); | 2011 output_asm_insn (pattern, operands); |
576 return ""; | 2012 return ""; |
577 } | 2013 } |
578 | 2014 |
2015 /* If last_argument_regno is not mentioned in par_rtx, we can confirm that | |
2016 we do not need to push argument registers for variadic function. | |
2017 But we still need to check if we need to push exception handling | |
2018 data registers. */ | |
2019 if (reg_mentioned_p (gen_rtx_REG (SImode, first_eh_data_regno), par_rtx)) | |
2020 { | |
2021 /* Set operands[0] and operands[1]. */ | |
2022 operands[0] = gen_rtx_REG (SImode, rb_eh_data); | |
2023 operands[1] = gen_rtx_REG (SImode, re_eh_data); | |
2024 /* Create assembly code pattern: "Rb, Re, { }". */ | |
2025 snprintf (pattern, sizeof (pattern), "push.s\t%s", "%0, %1, { }"); | |
2026 /* We use output_asm_insn() to output assembly code by ourself. */ | |
2027 output_asm_insn (pattern, operands); | |
2028 return ""; | |
2029 } | |
2030 | |
579 /* If we step here, we are going to do v3push or multiple push operation. */ | 2031 /* If we step here, we are going to do v3push or multiple push operation. */ |
580 | 2032 |
581 /* The v3push/v3pop instruction should only be applied on | 2033 /* Refer to nds32.h, where we comment when push25/pop25 are available. */ |
582 none-isr and none-variadic function. */ | 2034 if (NDS32_V3PUSH_AVAILABLE_P) |
583 if (TARGET_V3PUSH | |
584 && !nds32_isr_function_p (current_function_decl) | |
585 && (cfun->machine->va_args_size == 0)) | |
586 { | 2035 { |
587 /* For stack v3push: | 2036 /* For stack v3push: |
588 operands[0]: Re | 2037 operands[0]: Re |
589 operands[1]: imm8u */ | 2038 operands[1]: imm8u */ |
590 | 2039 |
596 | 2045 |
597 /* Check if we can generate 'push25 Re,imm8u', | 2046 /* Check if we can generate 'push25 Re,imm8u', |
598 otherwise, generate 'push25 Re,0'. */ | 2047 otherwise, generate 'push25 Re,0'. */ |
599 sp_adjust = cfun->machine->local_size | 2048 sp_adjust = cfun->machine->local_size |
600 + cfun->machine->out_args_size | 2049 + cfun->machine->out_args_size |
601 + cfun->machine->callee_saved_area_gpr_padding_bytes; | 2050 + cfun->machine->callee_saved_area_gpr_padding_bytes |
2051 + cfun->machine->callee_saved_fpr_regs_size; | |
602 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust)) | 2052 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust)) |
603 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust)) | 2053 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust)) |
604 operands[1] = GEN_INT (sp_adjust); | 2054 operands[1] = GEN_INT (sp_adjust); |
605 else | 2055 else |
606 operands[1] = GEN_INT (0); | 2056 { |
2057 /* Allocate callee saved fpr space. */ | |
2058 if (cfun->machine->callee_saved_first_fpr_regno != SP_REGNUM) | |
2059 { | |
2060 sp_adjust = cfun->machine->callee_saved_area_gpr_padding_bytes | |
2061 + cfun->machine->callee_saved_fpr_regs_size; | |
2062 operands[1] = GEN_INT (sp_adjust); | |
2063 } | |
2064 else | |
2065 { | |
2066 operands[1] = GEN_INT (0); | |
2067 } | |
2068 } | |
607 | 2069 |
608 /* Create assembly code pattern. */ | 2070 /* Create assembly code pattern. */ |
609 snprintf (pattern, sizeof (pattern), "push25\t%%0, %%1"); | 2071 snprintf (pattern, sizeof (pattern), "push25\t%%0, %%1"); |
610 } | 2072 } |
611 else | 2073 else |
663 { | 2125 { |
664 /* A string pattern for output_asm_insn(). */ | 2126 /* A string pattern for output_asm_insn(). */ |
665 char pattern[100]; | 2127 char pattern[100]; |
666 /* The operands array which will be used in output_asm_insn(). */ | 2128 /* The operands array which will be used in output_asm_insn(). */ |
667 rtx operands[3]; | 2129 rtx operands[3]; |
2130 /* Pick up first and last eh data regno for further use. */ | |
2131 int rb_eh_data = cfun->machine->eh_return_data_first_regno; | |
2132 int re_eh_data = cfun->machine->eh_return_data_last_regno; | |
2133 int first_eh_data_regno = EH_RETURN_DATA_REGNO (0); | |
668 /* Pick up callee-saved first regno and last regno for further use. */ | 2134 /* Pick up callee-saved first regno and last regno for further use. */ |
669 int rb_callee_saved = cfun->machine->callee_saved_first_gpr_regno; | 2135 int rb_callee_saved = cfun->machine->callee_saved_first_gpr_regno; |
670 int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; | 2136 int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; |
671 | 2137 |
2138 /* We need to check if we need to push exception handling | |
2139 data registers. */ | |
2140 if (reg_mentioned_p (gen_rtx_REG (SImode, first_eh_data_regno), par_rtx)) | |
2141 { | |
2142 /* Set operands[0] and operands[1]. */ | |
2143 operands[0] = gen_rtx_REG (SImode, rb_eh_data); | |
2144 operands[1] = gen_rtx_REG (SImode, re_eh_data); | |
2145 /* Create assembly code pattern: "Rb, Re, { }". */ | |
2146 snprintf (pattern, sizeof (pattern), "pop.s\t%s", "%0, %1, { }"); | |
2147 /* We use output_asm_insn() to output assembly code by ourself. */ | |
2148 output_asm_insn (pattern, operands); | |
2149 return ""; | |
2150 } | |
2151 | |
672 /* If we step here, we are going to do v3pop or multiple pop operation. */ | 2152 /* If we step here, we are going to do v3pop or multiple pop operation. */ |
673 | 2153 |
674 /* The v3push/v3pop instruction should only be applied on | 2154 /* Refer to nds32.h, where we comment when push25/pop25 are available. */ |
675 none-isr and none-variadic function. */ | 2155 if (NDS32_V3PUSH_AVAILABLE_P) |
676 if (TARGET_V3PUSH | |
677 && !nds32_isr_function_p (current_function_decl) | |
678 && (cfun->machine->va_args_size == 0)) | |
679 { | 2156 { |
680 /* For stack v3pop: | 2157 /* For stack v3pop: |
681 operands[0]: Re | 2158 operands[0]: Re |
682 operands[1]: imm8u */ | 2159 operands[1]: imm8u */ |
683 | 2160 |
694 In that case, we cannot use 'pop25 Re,imm8u' directly. | 2171 In that case, we cannot use 'pop25 Re,imm8u' directly. |
695 We have to caculate stack pointer from frame pointer | 2172 We have to caculate stack pointer from frame pointer |
696 and then use 'pop25 Re,0'. */ | 2173 and then use 'pop25 Re,0'. */ |
697 sp_adjust = cfun->machine->local_size | 2174 sp_adjust = cfun->machine->local_size |
698 + cfun->machine->out_args_size | 2175 + cfun->machine->out_args_size |
699 + cfun->machine->callee_saved_area_gpr_padding_bytes; | 2176 + cfun->machine->callee_saved_area_gpr_padding_bytes |
2177 + cfun->machine->callee_saved_fpr_regs_size; | |
700 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust)) | 2178 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust)) |
701 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust) | 2179 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust) |
702 && !cfun->calls_alloca) | 2180 && !cfun->calls_alloca) |
703 operands[1] = GEN_INT (sp_adjust); | 2181 operands[1] = GEN_INT (sp_adjust); |
704 else | 2182 else |
705 operands[1] = GEN_INT (0); | 2183 { |
2184 if (cfun->machine->callee_saved_first_fpr_regno != SP_REGNUM) | |
2185 { | |
2186 /* If has fpr need to restore, the $sp on callee saved fpr | |
2187 position, so we need to consider gpr pading bytes and | |
2188 callee saved fpr size. */ | |
2189 sp_adjust = cfun->machine->callee_saved_area_gpr_padding_bytes | |
2190 + cfun->machine->callee_saved_fpr_regs_size; | |
2191 operands[1] = GEN_INT (sp_adjust); | |
2192 } | |
2193 else | |
2194 { | |
2195 operands[1] = GEN_INT (0); | |
2196 } | |
2197 } | |
706 | 2198 |
707 /* Create assembly code pattern. */ | 2199 /* Create assembly code pattern. */ |
708 snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1"); | 2200 snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1"); |
709 } | 2201 } |
710 else | 2202 else |
753 /* We use output_asm_insn() to output assembly code by ourself. */ | 2245 /* We use output_asm_insn() to output assembly code by ourself. */ |
754 output_asm_insn (pattern, operands); | 2246 output_asm_insn (pattern, operands); |
755 return ""; | 2247 return ""; |
756 } | 2248 } |
757 | 2249 |
758 /* Function to generate PC relative jump table. | 2250 /* Function to output return operation. */ |
759 Refer to nds32.md for more details. | |
760 | |
761 The following is the sample for the case that diff value | |
762 can be presented in '.short' size. | |
763 | |
764 addi $r1, $r1, -(case_lower_bound) | |
765 slti $ta, $r1, (case_number) | |
766 beqz $ta, .L_skip_label | |
767 | |
768 la $ta, .L35 ! get jump table address | |
769 lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry | |
770 addi $ta, $r1, $ta | |
771 jr5 $ta | |
772 | |
773 ! jump table entry | |
774 L35: | |
775 .short .L25-.L35 | |
776 .short .L26-.L35 | |
777 .short .L27-.L35 | |
778 .short .L28-.L35 | |
779 .short .L29-.L35 | |
780 .short .L30-.L35 | |
781 .short .L31-.L35 | |
782 .short .L32-.L35 | |
783 .short .L33-.L35 | |
784 .short .L34-.L35 */ | |
785 const char * | 2251 const char * |
786 nds32_output_casesi_pc_relative (rtx *operands) | 2252 nds32_output_return (void) |
787 { | 2253 { |
788 machine_mode mode; | 2254 /* A string pattern for output_asm_insn(). */ |
789 rtx diff_vec; | 2255 char pattern[100]; |
790 | 2256 /* The operands array which will be used in output_asm_insn(). */ |
791 diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[1]))); | 2257 rtx operands[2]; |
792 | 2258 /* For stack v3pop: |
793 gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC); | 2259 operands[0]: Re |
794 | 2260 operands[1]: imm8u */ |
795 /* Step C: "t <-- operands[1]". */ | 2261 int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; |
796 output_asm_insn ("la\t$ta, %l1", operands); | 2262 int sp_adjust; |
797 | 2263 |
798 /* Get the mode of each element in the difference vector. */ | 2264 /* Set operands[0]. */ |
799 mode = GET_MODE (diff_vec); | 2265 operands[0] = gen_rtx_REG (SImode, re_callee_saved); |
800 | 2266 |
801 /* Step D: "z <-- (mem (plus (operands[0] << m) t))", | 2267 /* Check if we can generate 'pop25 Re,imm8u', |
802 where m is 0, 1, or 2 to load address-diff value from table. */ | 2268 otherwise, generate 'pop25 Re,0'. |
803 switch (mode) | 2269 We have to consider alloca issue as well. |
804 { | 2270 If the function does call alloca(), the stack pointer is not fixed. |
805 case E_QImode: | 2271 In that case, we cannot use 'pop25 Re,imm8u' directly. |
806 output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands); | 2272 We have to caculate stack pointer from frame pointer |
807 break; | 2273 and then use 'pop25 Re,0'. */ |
808 case E_HImode: | 2274 sp_adjust = cfun->machine->local_size |
809 output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands); | 2275 + cfun->machine->out_args_size |
810 break; | 2276 + cfun->machine->callee_saved_area_gpr_padding_bytes |
811 case E_SImode: | 2277 + cfun->machine->callee_saved_fpr_regs_size; |
812 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands); | 2278 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust)) |
813 break; | 2279 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust) |
2280 && !cfun->calls_alloca) | |
2281 operands[1] = GEN_INT (sp_adjust); | |
2282 else | |
2283 operands[1] = GEN_INT (0); | |
2284 | |
2285 /* Create assembly code pattern. */ | |
2286 snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1"); | |
2287 /* We use output_asm_insn() to output assembly code by ourself. */ | |
2288 output_asm_insn (pattern, operands); | |
2289 return ""; | |
2290 } | |
2291 | |
2292 | |
2293 /* output a float load instruction */ | |
2294 const char * | |
2295 nds32_output_float_load (rtx *operands) | |
2296 { | |
2297 char buff[100]; | |
2298 const char *pattern; | |
2299 rtx addr, addr_op0, addr_op1; | |
2300 int dp = GET_MODE_SIZE (GET_MODE (operands[0])) == 8; | |
2301 addr = XEXP (operands[1], 0); | |
2302 switch (GET_CODE (addr)) | |
2303 { | |
2304 case REG: | |
2305 pattern = "fl%ci\t%%0, %%1"; | |
2306 break; | |
2307 | |
2308 case PLUS: | |
2309 addr_op0 = XEXP (addr, 0); | |
2310 addr_op1 = XEXP (addr, 1); | |
2311 | |
2312 if (REG_P (addr_op0) && REG_P (addr_op1)) | |
2313 pattern = "fl%c\t%%0, %%1"; | |
2314 else if (REG_P (addr_op0) && CONST_INT_P (addr_op1)) | |
2315 pattern = "fl%ci\t%%0, %%1"; | |
2316 else if (GET_CODE (addr_op0) == MULT && REG_P (addr_op1) | |
2317 && REG_P (XEXP (addr_op0, 0)) | |
2318 && CONST_INT_P (XEXP (addr_op0, 1))) | |
2319 pattern = "fl%c\t%%0, %%1"; | |
2320 else | |
2321 gcc_unreachable (); | |
2322 break; | |
2323 | |
2324 case POST_MODIFY: | |
2325 addr_op0 = XEXP (addr, 0); | |
2326 addr_op1 = XEXP (addr, 1); | |
2327 | |
2328 if (REG_P (addr_op0) && GET_CODE (addr_op1) == PLUS | |
2329 && REG_P (XEXP (addr_op1, 1))) | |
2330 pattern = "fl%c.bi\t%%0, %%1"; | |
2331 else if (REG_P (addr_op0) && GET_CODE (addr_op1) == PLUS | |
2332 && CONST_INT_P (XEXP (addr_op1, 1))) | |
2333 pattern = "fl%ci.bi\t%%0, %%1"; | |
2334 else | |
2335 gcc_unreachable (); | |
2336 break; | |
2337 | |
2338 case POST_INC: | |
2339 if (REG_P (XEXP (addr, 0))) | |
2340 { | |
2341 if (dp) | |
2342 pattern = "fl%ci.bi\t%%0, %%1, 8"; | |
2343 else | |
2344 pattern = "fl%ci.bi\t%%0, %%1, 4"; | |
2345 } | |
2346 else | |
2347 gcc_unreachable (); | |
2348 break; | |
2349 | |
2350 case POST_DEC: | |
2351 if (REG_P (XEXP (addr, 0))) | |
2352 { | |
2353 if (dp) | |
2354 pattern = "fl%ci.bi\t%%0, %%1, -8"; | |
2355 else | |
2356 pattern = "fl%ci.bi\t%%0, %%1, -4"; | |
2357 } | |
2358 else | |
2359 gcc_unreachable (); | |
2360 break; | |
2361 | |
814 default: | 2362 default: |
815 gcc_unreachable (); | 2363 gcc_unreachable (); |
816 } | 2364 } |
817 | 2365 |
818 /* Step E: "t <-- z + t". | 2366 sprintf (buff, pattern, dp ? 'd' : 's'); |
819 Add table label_ref with address-diff value to | 2367 output_asm_insn (buff, operands); |
820 obtain target case address. */ | 2368 return ""; |
821 output_asm_insn ("add\t$ta, %2, $ta", operands); | 2369 } |
822 | 2370 |
823 /* Step F: jump to target with register t. */ | 2371 /* output a float store instruction */ |
824 if (TARGET_16_BIT) | 2372 const char * |
825 return "jr5\t$ta"; | 2373 nds32_output_float_store (rtx *operands) |
2374 { | |
2375 char buff[100]; | |
2376 const char *pattern; | |
2377 rtx addr, addr_op0, addr_op1; | |
2378 int dp = GET_MODE_SIZE (GET_MODE (operands[0])) == 8; | |
2379 addr = XEXP (operands[0], 0); | |
2380 switch (GET_CODE (addr)) | |
2381 { | |
2382 case REG: | |
2383 pattern = "fs%ci\t%%1, %%0"; | |
2384 break; | |
2385 | |
2386 case PLUS: | |
2387 addr_op0 = XEXP (addr, 0); | |
2388 addr_op1 = XEXP (addr, 1); | |
2389 | |
2390 if (REG_P (addr_op0) && REG_P (addr_op1)) | |
2391 pattern = "fs%c\t%%1, %%0"; | |
2392 else if (REG_P (addr_op0) && CONST_INT_P (addr_op1)) | |
2393 pattern = "fs%ci\t%%1, %%0"; | |
2394 else if (GET_CODE (addr_op0) == MULT && REG_P (addr_op1) | |
2395 && REG_P (XEXP (addr_op0, 0)) | |
2396 && CONST_INT_P (XEXP (addr_op0, 1))) | |
2397 pattern = "fs%c\t%%1, %%0"; | |
2398 else | |
2399 gcc_unreachable (); | |
2400 break; | |
2401 | |
2402 case POST_MODIFY: | |
2403 addr_op0 = XEXP (addr, 0); | |
2404 addr_op1 = XEXP (addr, 1); | |
2405 | |
2406 if (REG_P (addr_op0) && GET_CODE (addr_op1) == PLUS | |
2407 && REG_P (XEXP (addr_op1, 1))) | |
2408 pattern = "fs%c.bi\t%%1, %%0"; | |
2409 else if (REG_P (addr_op0) && GET_CODE (addr_op1) == PLUS | |
2410 && CONST_INT_P (XEXP (addr_op1, 1))) | |
2411 pattern = "fs%ci.bi\t%%1, %%0"; | |
2412 else | |
2413 gcc_unreachable (); | |
2414 break; | |
2415 | |
2416 case POST_INC: | |
2417 if (REG_P (XEXP (addr, 0))) | |
2418 { | |
2419 if (dp) | |
2420 pattern = "fs%ci.bi\t%%1, %%0, 8"; | |
2421 else | |
2422 pattern = "fs%ci.bi\t%%1, %%0, 4"; | |
2423 } | |
2424 else | |
2425 gcc_unreachable (); | |
2426 break; | |
2427 | |
2428 case POST_DEC: | |
2429 if (REG_P (XEXP (addr, 0))) | |
2430 { | |
2431 if (dp) | |
2432 pattern = "fs%ci.bi\t%%1, %%0, -8"; | |
2433 else | |
2434 pattern = "fs%ci.bi\t%%1, %%0, -4"; | |
2435 } | |
2436 else | |
2437 gcc_unreachable (); | |
2438 break; | |
2439 | |
2440 default: | |
2441 gcc_unreachable (); | |
2442 } | |
2443 | |
2444 sprintf (buff, pattern, dp ? 'd' : 's'); | |
2445 output_asm_insn (buff, operands); | |
2446 return ""; | |
2447 } | |
2448 | |
2449 const char * | |
2450 nds32_output_smw_single_word (rtx *operands) | |
2451 { | |
2452 char buff[100]; | |
2453 unsigned regno; | |
2454 int enable4; | |
2455 bool update_base_p; | |
2456 rtx base_addr = operands[0]; | |
2457 rtx base_reg; | |
2458 rtx otherops[2]; | |
2459 | |
2460 if (REG_P (XEXP (base_addr, 0))) | |
2461 { | |
2462 update_base_p = false; | |
2463 base_reg = XEXP (base_addr, 0); | |
2464 } | |
826 else | 2465 else |
827 return "jr\t$ta"; | 2466 { |
828 } | 2467 update_base_p = true; |
829 | 2468 base_reg = XEXP (XEXP (base_addr, 0), 0); |
830 /* Function to generate normal jump table. */ | 2469 } |
2470 | |
2471 const char *update_base = update_base_p ? "m" : ""; | |
2472 | |
2473 regno = REGNO (operands[1]); | |
2474 | |
2475 otherops[0] = base_reg; | |
2476 otherops[1] = operands[1]; | |
2477 | |
2478 if (regno >= 28) | |
2479 { | |
2480 enable4 = nds32_regno_to_enable4 (regno); | |
2481 sprintf (buff, "smw.bi%s\t$sp, [%%0], $sp, %x", update_base, enable4); | |
2482 } | |
2483 else | |
2484 { | |
2485 sprintf (buff, "smw.bi%s\t%%1, [%%0], %%1", update_base); | |
2486 } | |
2487 output_asm_insn (buff, otherops); | |
2488 return ""; | |
2489 } | |
2490 | |
2491 /* ------------------------------------------------------------------------ */ | |
831 const char * | 2492 const char * |
832 nds32_output_casesi (rtx *operands) | 2493 nds32_output_smw_double_word (rtx *operands) |
833 { | 2494 { |
834 /* Step C: "t <-- operands[1]". */ | 2495 char buff[100]; |
835 output_asm_insn ("la\t$ta, %l1", operands); | 2496 unsigned regno; |
836 | 2497 int enable4; |
837 /* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */ | 2498 bool update_base_p; |
838 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands); | 2499 rtx base_addr = operands[0]; |
839 | 2500 rtx base_reg; |
840 /* No need to perform Step E, which is only used for | 2501 rtx otherops[3]; |
841 pc relative jump table. */ | 2502 |
842 | 2503 if (REG_P (XEXP (base_addr, 0))) |
843 /* Step F: jump to target with register z. */ | 2504 { |
844 if (TARGET_16_BIT) | 2505 update_base_p = false; |
845 return "jr5\t%2"; | 2506 base_reg = XEXP (base_addr, 0); |
2507 } | |
846 else | 2508 else |
847 return "jr\t%2"; | 2509 { |
2510 update_base_p = true; | |
2511 base_reg = XEXP (XEXP (base_addr, 0), 0); | |
2512 } | |
2513 | |
2514 const char *update_base = update_base_p ? "m" : ""; | |
2515 | |
2516 regno = REGNO (operands[1]); | |
2517 | |
2518 otherops[0] = base_reg; | |
2519 otherops[1] = operands[1]; | |
2520 otherops[2] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);; | |
2521 | |
2522 if (regno >= 28) | |
2523 { | |
2524 enable4 = nds32_regno_to_enable4 (regno) | |
2525 | nds32_regno_to_enable4 (regno + 1); | |
2526 sprintf (buff, "smw.bi%s\t$sp, [%%0], $sp, %x", update_base, enable4); | |
2527 } | |
2528 else if (regno == 27) | |
2529 { | |
2530 enable4 = nds32_regno_to_enable4 (regno + 1); | |
2531 sprintf (buff, "smw.bi%s\t%%1, [%%0], %%1, %x", update_base, enable4); | |
2532 } | |
2533 else | |
2534 { | |
2535 sprintf (buff, "smw.bi%s\t%%1, [%%0], %%2", update_base); | |
2536 } | |
2537 output_asm_insn (buff, otherops); | |
2538 return ""; | |
2539 } | |
2540 | |
2541 const char * | |
2542 nds32_output_lmw_single_word (rtx *operands) | |
2543 { | |
2544 char buff[100]; | |
2545 unsigned regno; | |
2546 bool update_base_p; | |
2547 int enable4; | |
2548 rtx base_addr = operands[1]; | |
2549 rtx base_reg; | |
2550 rtx otherops[2]; | |
2551 | |
2552 if (REG_P (XEXP (base_addr, 0))) | |
2553 { | |
2554 update_base_p = false; | |
2555 base_reg = XEXP (base_addr, 0); | |
2556 } | |
2557 else | |
2558 { | |
2559 update_base_p = true; | |
2560 base_reg = XEXP (XEXP (base_addr, 0), 0); | |
2561 } | |
2562 | |
2563 const char *update_base = update_base_p ? "m" : ""; | |
2564 | |
2565 regno = REGNO (operands[0]); | |
2566 | |
2567 otherops[0] = operands[0]; | |
2568 otherops[1] = base_reg; | |
2569 | |
2570 if (regno >= 28) | |
2571 { | |
2572 enable4 = nds32_regno_to_enable4 (regno); | |
2573 sprintf (buff, "lmw.bi%s\t$sp, [%%1], $sp, %x", update_base, enable4); | |
2574 } | |
2575 else | |
2576 { | |
2577 sprintf (buff, "lmw.bi%s\t%%0, [%%1], %%0", update_base); | |
2578 } | |
2579 output_asm_insn (buff, otherops); | |
2580 return ""; | |
2581 } | |
2582 | |
2583 void | |
2584 nds32_expand_unaligned_load (rtx *operands, enum machine_mode mode) | |
2585 { | |
2586 /* Initial memory offset. */ | |
2587 int offset = WORDS_BIG_ENDIAN ? GET_MODE_SIZE (mode) - 1 : 0; | |
2588 int offset_adj = WORDS_BIG_ENDIAN ? -1 : 1; | |
2589 /* Initial register shift byte. */ | |
2590 int shift = 0; | |
2591 /* The first load byte instruction is not the same. */ | |
2592 int width = GET_MODE_SIZE (mode) - 1; | |
2593 rtx mem[2]; | |
2594 rtx reg[2]; | |
2595 rtx sub_reg; | |
2596 rtx temp_reg, temp_sub_reg; | |
2597 int num_reg; | |
2598 | |
2599 /* Generating a series of load byte instructions. | |
2600 The first load byte instructions and other | |
2601 load byte instructions are not the same. like: | |
2602 First: | |
2603 lbi reg0, [mem] | |
2604 zeh reg0, reg0 | |
2605 Second: | |
2606 lbi temp_reg, [mem + offset] | |
2607 sll temp_reg, (8 * shift) | |
2608 ior reg0, temp_reg | |
2609 | |
2610 lbi temp_reg, [mem + (offset + 1)] | |
2611 sll temp_reg, (8 * (shift + 1)) | |
2612 ior reg0, temp_reg */ | |
2613 | |
2614 temp_reg = gen_reg_rtx (SImode); | |
2615 temp_sub_reg = gen_lowpart (QImode, temp_reg); | |
2616 | |
2617 if (mode == DImode) | |
2618 { | |
2619 /* Load doubleword, we need two registers to access. */ | |
2620 reg[0] = nds32_di_low_part_subreg (operands[0]); | |
2621 reg[1] = nds32_di_high_part_subreg (operands[0]); | |
2622 /* A register only store 4 byte. */ | |
2623 width = GET_MODE_SIZE (SImode) - 1; | |
2624 } | |
2625 else | |
2626 { | |
2627 if (VECTOR_MODE_P (mode)) | |
2628 reg[0] = gen_reg_rtx (SImode); | |
2629 else | |
2630 reg[0] = operands[0]; | |
2631 } | |
2632 | |
2633 for (num_reg = (mode == DImode) ? 2 : 1; num_reg > 0; num_reg--) | |
2634 { | |
2635 sub_reg = gen_lowpart (QImode, reg[0]); | |
2636 mem[0] = gen_rtx_MEM (QImode, plus_constant (Pmode, operands[1], offset)); | |
2637 | |
2638 /* Generating the first part instructions. | |
2639 lbi reg0, [mem] | |
2640 zeh reg0, reg0 */ | |
2641 emit_move_insn (sub_reg, mem[0]); | |
2642 emit_insn (gen_zero_extendqisi2 (reg[0], sub_reg)); | |
2643 | |
2644 while (width > 0) | |
2645 { | |
2646 offset = offset + offset_adj; | |
2647 shift++; | |
2648 width--; | |
2649 | |
2650 mem[1] = gen_rtx_MEM (QImode, plus_constant (Pmode, | |
2651 operands[1], | |
2652 offset)); | |
2653 /* Generating the second part instructions. | |
2654 lbi temp_reg, [mem + offset] | |
2655 sll temp_reg, (8 * shift) | |
2656 ior reg0, temp_reg */ | |
2657 emit_move_insn (temp_sub_reg, mem[1]); | |
2658 emit_insn (gen_ashlsi3 (temp_reg, temp_reg, | |
2659 GEN_INT (shift * 8))); | |
2660 emit_insn (gen_iorsi3 (reg[0], reg[0], temp_reg)); | |
2661 } | |
2662 | |
2663 if (mode == DImode) | |
2664 { | |
2665 /* Using the second register to load memory information. */ | |
2666 reg[0] = reg[1]; | |
2667 shift = 0; | |
2668 width = GET_MODE_SIZE (SImode) - 1; | |
2669 offset = offset + offset_adj; | |
2670 } | |
2671 } | |
2672 if (VECTOR_MODE_P (mode)) | |
2673 convert_move (operands[0], reg[0], false); | |
2674 } | |
2675 | |
2676 void | |
2677 nds32_expand_unaligned_store (rtx *operands, enum machine_mode mode) | |
2678 { | |
2679 /* Initial memory offset. */ | |
2680 int offset = WORDS_BIG_ENDIAN ? GET_MODE_SIZE (mode) - 1 : 0; | |
2681 int offset_adj = WORDS_BIG_ENDIAN ? -1 : 1; | |
2682 /* Initial register shift byte. */ | |
2683 int shift = 0; | |
2684 /* The first load byte instruction is not the same. */ | |
2685 int width = GET_MODE_SIZE (mode) - 1; | |
2686 rtx mem[2]; | |
2687 rtx reg[2]; | |
2688 rtx sub_reg; | |
2689 rtx temp_reg, temp_sub_reg; | |
2690 int num_reg; | |
2691 | |
2692 /* Generating a series of store byte instructions. | |
2693 The first store byte instructions and other | |
2694 load byte instructions are not the same. like: | |
2695 First: | |
2696 sbi reg0, [mem + 0] | |
2697 Second: | |
2698 srli temp_reg, reg0, (8 * shift) | |
2699 sbi temp_reg, [mem + offset] */ | |
2700 | |
2701 temp_reg = gen_reg_rtx (SImode); | |
2702 temp_sub_reg = gen_lowpart (QImode, temp_reg); | |
2703 | |
2704 if (mode == DImode) | |
2705 { | |
2706 /* Load doubleword, we need two registers to access. */ | |
2707 reg[0] = nds32_di_low_part_subreg (operands[1]); | |
2708 reg[1] = nds32_di_high_part_subreg (operands[1]); | |
2709 /* A register only store 4 byte. */ | |
2710 width = GET_MODE_SIZE (SImode) - 1; | |
2711 } | |
2712 else | |
2713 { | |
2714 if (VECTOR_MODE_P (mode)) | |
2715 { | |
2716 reg[0] = gen_reg_rtx (SImode); | |
2717 convert_move (reg[0], operands[1], false); | |
2718 } | |
2719 else | |
2720 reg[0] = operands[1]; | |
2721 } | |
2722 | |
2723 for (num_reg = (mode == DImode) ? 2 : 1; num_reg > 0; num_reg--) | |
2724 { | |
2725 sub_reg = gen_lowpart (QImode, reg[0]); | |
2726 mem[0] = gen_rtx_MEM (QImode, plus_constant (Pmode, operands[0], offset)); | |
2727 | |
2728 /* Generating the first part instructions. | |
2729 sbi reg0, [mem + 0] */ | |
2730 emit_move_insn (mem[0], sub_reg); | |
2731 | |
2732 while (width > 0) | |
2733 { | |
2734 offset = offset + offset_adj; | |
2735 shift++; | |
2736 width--; | |
2737 | |
2738 mem[1] = gen_rtx_MEM (QImode, plus_constant (Pmode, | |
2739 operands[0], | |
2740 offset)); | |
2741 /* Generating the second part instructions. | |
2742 srli temp_reg, reg0, (8 * shift) | |
2743 sbi temp_reg, [mem + offset] */ | |
2744 emit_insn (gen_lshrsi3 (temp_reg, reg[0], | |
2745 GEN_INT (shift * 8))); | |
2746 emit_move_insn (mem[1], temp_sub_reg); | |
2747 } | |
2748 | |
2749 if (mode == DImode) | |
2750 { | |
2751 /* Using the second register to load memory information. */ | |
2752 reg[0] = reg[1]; | |
2753 shift = 0; | |
2754 width = GET_MODE_SIZE (SImode) - 1; | |
2755 offset = offset + offset_adj; | |
2756 } | |
2757 } | |
2758 } | |
2759 | |
2760 /* Using multiple load/store instruction to output doubleword instruction. */ | |
2761 const char * | |
2762 nds32_output_double (rtx *operands, bool load_p) | |
2763 { | |
2764 char pattern[100]; | |
2765 int reg = load_p ? 0 : 1; | |
2766 int mem = load_p ? 1 : 0; | |
2767 rtx otherops[3]; | |
2768 rtx addr = XEXP (operands[mem], 0); | |
2769 | |
2770 otherops[0] = gen_rtx_REG (SImode, REGNO (operands[reg])); | |
2771 otherops[1] = gen_rtx_REG (SImode, REGNO (operands[reg]) + 1); | |
2772 | |
2773 if (GET_CODE (addr) == POST_INC) | |
2774 { | |
2775 /* (mem (post_inc (reg))) */ | |
2776 otherops[2] = XEXP (addr, 0); | |
2777 snprintf (pattern, sizeof (pattern), | |
2778 "%cmw.bim\t%%0, [%%2], %%1, 0", load_p ? 'l' : 's'); | |
2779 } | |
2780 else | |
2781 { | |
2782 /* (mem (reg)) */ | |
2783 otherops[2] = addr; | |
2784 snprintf (pattern, sizeof (pattern), | |
2785 "%cmw.bi\t%%0, [%%2], %%1, 0", load_p ? 'l' : 's'); | |
2786 | |
2787 } | |
2788 | |
2789 output_asm_insn (pattern, otherops); | |
2790 return ""; | |
2791 } | |
2792 | |
2793 const char * | |
2794 nds32_output_cbranchsi4_equality_zero (rtx_insn *insn, rtx *operands) | |
2795 { | |
2796 enum rtx_code code; | |
2797 bool long_jump_p = false; | |
2798 | |
2799 code = GET_CODE (operands[0]); | |
2800 | |
2801 /* This zero-comparison conditional branch has two forms: | |
2802 32-bit instruction => beqz/bnez imm16s << 1 | |
2803 16-bit instruction => beqzs8/bnezs8/beqz38/bnez38 imm8s << 1 | |
2804 | |
2805 For 32-bit case, | |
2806 we assume it is always reachable. (but check range -65500 ~ 65500) | |
2807 | |
2808 For 16-bit case, | |
2809 it must satisfy { 255 >= (label - pc) >= -256 } condition. | |
2810 However, since the $pc for nds32 is at the beginning of the instruction, | |
2811 we should leave some length space for current insn. | |
2812 So we use range -250 ~ 250. */ | |
2813 | |
2814 switch (get_attr_length (insn)) | |
2815 { | |
2816 case 8: | |
2817 long_jump_p = true; | |
2818 /* fall through */ | |
2819 case 2: | |
2820 if (which_alternative == 0) | |
2821 { | |
2822 /* constraint: t */ | |
2823 /* b<cond>zs8 .L0 | |
2824 or | |
2825 b<inverse_cond>zs8 .LCB0 | |
2826 j .L0 | |
2827 .LCB0: | |
2828 */ | |
2829 output_cond_branch_compare_zero (code, "s8", long_jump_p, | |
2830 operands, true); | |
2831 return ""; | |
2832 } | |
2833 else if (which_alternative == 1) | |
2834 { | |
2835 /* constraint: l */ | |
2836 /* b<cond>z38 $r0, .L0 | |
2837 or | |
2838 b<inverse_cond>z38 $r0, .LCB0 | |
2839 j .L0 | |
2840 .LCB0: | |
2841 */ | |
2842 output_cond_branch_compare_zero (code, "38", long_jump_p, | |
2843 operands, false); | |
2844 return ""; | |
2845 } | |
2846 else | |
2847 { | |
2848 /* constraint: r */ | |
2849 /* For which_alternative==2, it should not be here. */ | |
2850 gcc_unreachable (); | |
2851 } | |
2852 case 10: | |
2853 /* including constraints: t, l, and r */ | |
2854 long_jump_p = true; | |
2855 /* fall through */ | |
2856 case 4: | |
2857 /* including constraints: t, l, and r */ | |
2858 output_cond_branch_compare_zero (code, "", long_jump_p, operands, false); | |
2859 return ""; | |
2860 | |
2861 default: | |
2862 gcc_unreachable (); | |
2863 } | |
2864 } | |
2865 | |
2866 const char * | |
2867 nds32_output_cbranchsi4_equality_reg (rtx_insn *insn, rtx *operands) | |
2868 { | |
2869 enum rtx_code code; | |
2870 bool long_jump_p, r5_p; | |
2871 int insn_length; | |
2872 | |
2873 insn_length = get_attr_length (insn); | |
2874 | |
2875 long_jump_p = (insn_length == 10 || insn_length == 8) ? true : false; | |
2876 r5_p = (insn_length == 2 || insn_length == 8) ? true : false; | |
2877 | |
2878 code = GET_CODE (operands[0]); | |
2879 | |
2880 /* This register-comparison conditional branch has one form: | |
2881 32-bit instruction => beq/bne imm14s << 1 | |
2882 | |
2883 For 32-bit case, | |
2884 we assume it is always reachable. (but check range -16350 ~ 16350). */ | |
2885 | |
2886 switch (code) | |
2887 { | |
2888 case EQ: | |
2889 case NE: | |
2890 output_cond_branch (code, "", r5_p, long_jump_p, operands); | |
2891 return ""; | |
2892 | |
2893 default: | |
2894 gcc_unreachable (); | |
2895 } | |
2896 } | |
2897 | |
2898 const char * | |
2899 nds32_output_cbranchsi4_equality_reg_or_const_int (rtx_insn *insn, | |
2900 rtx *operands) | |
2901 { | |
2902 enum rtx_code code; | |
2903 bool long_jump_p, r5_p; | |
2904 int insn_length; | |
2905 | |
2906 insn_length = get_attr_length (insn); | |
2907 | |
2908 long_jump_p = (insn_length == 10 || insn_length == 8) ? true : false; | |
2909 r5_p = (insn_length == 2 || insn_length == 8) ? true : false; | |
2910 | |
2911 code = GET_CODE (operands[0]); | |
2912 | |
2913 /* This register-comparison conditional branch has one form: | |
2914 32-bit instruction => beq/bne imm14s << 1 | |
2915 32-bit instruction => beqc/bnec imm8s << 1 | |
2916 | |
2917 For 32-bit case, we assume it is always reachable. | |
2918 (but check range -16350 ~ 16350 and -250 ~ 250). */ | |
2919 | |
2920 switch (code) | |
2921 { | |
2922 case EQ: | |
2923 case NE: | |
2924 if (which_alternative == 2) | |
2925 { | |
2926 /* r, Is11 */ | |
2927 /* b<cond>c */ | |
2928 output_cond_branch (code, "c", r5_p, long_jump_p, operands); | |
2929 } | |
2930 else | |
2931 { | |
2932 /* r, r */ | |
2933 /* v, r */ | |
2934 output_cond_branch (code, "", r5_p, long_jump_p, operands); | |
2935 } | |
2936 return ""; | |
2937 default: | |
2938 gcc_unreachable (); | |
2939 } | |
2940 } | |
2941 | |
2942 const char * | |
2943 nds32_output_cbranchsi4_greater_less_zero (rtx_insn *insn, rtx *operands) | |
2944 { | |
2945 enum rtx_code code; | |
2946 bool long_jump_p; | |
2947 int insn_length; | |
2948 | |
2949 insn_length = get_attr_length (insn); | |
2950 | |
2951 gcc_assert (insn_length == 4 || insn_length == 10); | |
2952 | |
2953 long_jump_p = (insn_length == 10) ? true : false; | |
2954 | |
2955 code = GET_CODE (operands[0]); | |
2956 | |
2957 /* This zero-greater-less-comparison conditional branch has one form: | |
2958 32-bit instruction => bgtz/bgez/bltz/blez imm16s << 1 | |
2959 | |
2960 For 32-bit case, we assume it is always reachable. | |
2961 (but check range -65500 ~ 65500). */ | |
2962 | |
2963 switch (code) | |
2964 { | |
2965 case GT: | |
2966 case GE: | |
2967 case LT: | |
2968 case LE: | |
2969 output_cond_branch_compare_zero (code, "", long_jump_p, operands, false); | |
2970 break; | |
2971 default: | |
2972 gcc_unreachable (); | |
2973 } | |
2974 return ""; | |
2975 } | |
2976 | |
2977 const char * | |
2978 nds32_output_unpkd8 (rtx output, rtx input, | |
2979 rtx high_idx_rtx, rtx low_idx_rtx, | |
2980 bool signed_p) | |
2981 { | |
2982 char pattern[100]; | |
2983 rtx output_operands[2]; | |
2984 HOST_WIDE_INT high_idx, low_idx; | |
2985 high_idx = INTVAL (high_idx_rtx); | |
2986 low_idx = INTVAL (low_idx_rtx); | |
2987 | |
2988 gcc_assert (high_idx >= 0 && high_idx <= 3); | |
2989 gcc_assert (low_idx >= 0 && low_idx <= 3); | |
2990 | |
2991 /* We only have 10, 20, 30 and 31. */ | |
2992 if ((low_idx != 0 || high_idx == 0) && | |
2993 !(low_idx == 1 && high_idx == 3)) | |
2994 return "#"; | |
2995 | |
2996 char sign_char = signed_p ? 's' : 'z'; | |
2997 | |
2998 sprintf (pattern, | |
2999 "%cunpkd8" HOST_WIDE_INT_PRINT_DEC HOST_WIDE_INT_PRINT_DEC "\t%%0, %%1", | |
3000 sign_char, high_idx, low_idx); | |
3001 output_operands[0] = output; | |
3002 output_operands[1] = input; | |
3003 output_asm_insn (pattern, output_operands); | |
3004 return ""; | |
3005 } | |
3006 | |
3007 /* Return true if SYMBOL_REF X binds locally. */ | |
3008 | |
3009 static bool | |
3010 nds32_symbol_binds_local_p (const_rtx x) | |
3011 { | |
3012 return (SYMBOL_REF_DECL (x) | |
3013 ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) | |
3014 : SYMBOL_REF_LOCAL_P (x)); | |
3015 } | |
3016 | |
3017 const char * | |
3018 nds32_output_call (rtx insn, rtx *operands, rtx symbol, const char *long_call, | |
3019 const char *call, bool align_p) | |
3020 { | |
3021 char pattern[100]; | |
3022 bool noreturn_p; | |
3023 | |
3024 if (nds32_long_call_p (symbol)) | |
3025 strcpy (pattern, long_call); | |
3026 else | |
3027 strcpy (pattern, call); | |
3028 | |
3029 if (flag_pic && CONSTANT_P (symbol) | |
3030 && !nds32_symbol_binds_local_p (symbol)) | |
3031 strcat (pattern, "@PLT"); | |
3032 | |
3033 if (align_p) | |
3034 strcat (pattern, "\n\t.align 2"); | |
3035 | |
3036 noreturn_p = find_reg_note (insn, REG_NORETURN, NULL_RTX) != NULL_RTX; | |
3037 | |
3038 if (noreturn_p) | |
3039 { | |
3040 if (TARGET_16_BIT) | |
3041 strcat (pattern, "\n\tnop16"); | |
3042 else | |
3043 strcat (pattern, "\n\tnop"); | |
3044 } | |
3045 | |
3046 output_asm_insn (pattern, operands); | |
3047 return ""; | |
3048 } | |
3049 | |
3050 bool | |
3051 nds32_need_split_sms_p (rtx in0_idx0, rtx in1_idx0, | |
3052 rtx in0_idx1, rtx in1_idx1) | |
3053 { | |
3054 /* smds or smdrs. */ | |
3055 if (INTVAL (in0_idx0) == INTVAL (in1_idx0) | |
3056 && INTVAL (in0_idx1) == INTVAL (in1_idx1) | |
3057 && INTVAL (in0_idx0) != INTVAL (in0_idx1)) | |
3058 return false; | |
3059 | |
3060 /* smxds. */ | |
3061 if (INTVAL (in0_idx0) != INTVAL (in0_idx1) | |
3062 && INTVAL (in1_idx0) != INTVAL (in1_idx1)) | |
3063 return false; | |
3064 | |
3065 return true; | |
3066 } | |
3067 | |
3068 const char * | |
3069 nds32_output_sms (rtx in0_idx0, rtx in1_idx0, | |
3070 rtx in0_idx1, rtx in1_idx1) | |
3071 { | |
3072 if (nds32_need_split_sms_p (in0_idx0, in1_idx0, | |
3073 in0_idx1, in1_idx1)) | |
3074 return "#"; | |
3075 /* out = in0[in0_idx0] * in1[in1_idx0] - in0[in0_idx1] * in1[in1_idx1] */ | |
3076 | |
3077 /* smds or smdrs. */ | |
3078 if (INTVAL (in0_idx0) == INTVAL (in1_idx0) | |
3079 && INTVAL (in0_idx1) == INTVAL (in1_idx1) | |
3080 && INTVAL (in0_idx0) != INTVAL (in0_idx1)) | |
3081 { | |
3082 if (INTVAL (in0_idx0) == 0) | |
3083 { | |
3084 if (TARGET_BIG_ENDIAN) | |
3085 return "smds\t%0, %1, %2"; | |
3086 else | |
3087 return "smdrs\t%0, %1, %2"; | |
3088 } | |
3089 else | |
3090 { | |
3091 if (TARGET_BIG_ENDIAN) | |
3092 return "smdrs\t%0, %1, %2"; | |
3093 else | |
3094 return "smds\t%0, %1, %2"; | |
3095 } | |
3096 } | |
3097 | |
3098 if (INTVAL (in0_idx0) != INTVAL (in0_idx1) | |
3099 && INTVAL (in1_idx0) != INTVAL (in1_idx1)) | |
3100 { | |
3101 if (INTVAL (in0_idx0) == 1) | |
3102 { | |
3103 if (TARGET_BIG_ENDIAN) | |
3104 return "smxds\t%0, %2, %1"; | |
3105 else | |
3106 return "smxds\t%0, %1, %2"; | |
3107 } | |
3108 else | |
3109 { | |
3110 if (TARGET_BIG_ENDIAN) | |
3111 return "smxds\t%0, %1, %2"; | |
3112 else | |
3113 return "smxds\t%0, %2, %1"; | |
3114 } | |
3115 } | |
3116 | |
3117 gcc_unreachable (); | |
3118 return ""; | |
3119 } | |
3120 | |
3121 void | |
3122 nds32_split_sms (rtx out, rtx in0, rtx in1, | |
3123 rtx in0_idx0, rtx in1_idx0, | |
3124 rtx in0_idx1, rtx in1_idx1) | |
3125 { | |
3126 rtx result0 = gen_reg_rtx (SImode); | |
3127 rtx result1 = gen_reg_rtx (SImode); | |
3128 emit_insn (gen_mulhisi3v (result0, in0, in1, | |
3129 in0_idx0, in1_idx0)); | |
3130 emit_insn (gen_mulhisi3v (result1, in0, in1, | |
3131 in0_idx1, in1_idx1)); | |
3132 emit_insn (gen_subsi3 (out, result0, result1)); | |
3133 } | |
3134 | |
3135 /* Spilt a doubleword instrucion to two single word instructions. */ | |
3136 void | |
3137 nds32_spilt_doubleword (rtx *operands, bool load_p) | |
3138 { | |
3139 int reg = load_p ? 0 : 1; | |
3140 int mem = load_p ? 1 : 0; | |
3141 rtx reg_rtx = load_p ? operands[0] : operands[1]; | |
3142 rtx mem_rtx = load_p ? operands[1] : operands[0]; | |
3143 rtx low_part[2], high_part[2]; | |
3144 rtx sub_mem = XEXP (mem_rtx, 0); | |
3145 | |
3146 /* Generate low_part and high_part register pattern. | |
3147 i.e. register pattern like: | |
3148 (reg:DI) -> (subreg:SI (reg:DI)) | |
3149 (subreg:SI (reg:DI)) */ | |
3150 low_part[reg] = simplify_gen_subreg (SImode, reg_rtx, GET_MODE (reg_rtx), 0); | |
3151 high_part[reg] = simplify_gen_subreg (SImode, reg_rtx, GET_MODE (reg_rtx), 4); | |
3152 | |
3153 /* Generate low_part and high_part memory pattern. | |
3154 Memory format is (post_dec) will generate: | |
3155 low_part: lwi.bi reg, [mem], 4 | |
3156 high_part: lwi.bi reg, [mem], -12 */ | |
3157 if (GET_CODE (sub_mem) == POST_DEC) | |
3158 { | |
3159 /* memory format is (post_dec (reg)), | |
3160 so that extract (reg) from the (post_dec (reg)) pattern. */ | |
3161 sub_mem = XEXP (sub_mem, 0); | |
3162 | |
3163 /* generate low_part and high_part memory format: | |
3164 low_part: (post_modify ((reg) (plus (reg) (const 4))) | |
3165 high_part: (post_modify ((reg) (plus (reg) (const -12))) */ | |
3166 low_part[mem] = gen_frame_mem (SImode, | |
3167 gen_rtx_POST_MODIFY (Pmode, sub_mem, | |
3168 gen_rtx_PLUS (Pmode, | |
3169 sub_mem, | |
3170 GEN_INT (4)))); | |
3171 high_part[mem] = gen_frame_mem (SImode, | |
3172 gen_rtx_POST_MODIFY (Pmode, sub_mem, | |
3173 gen_rtx_PLUS (Pmode, | |
3174 sub_mem, | |
3175 GEN_INT (-12)))); | |
3176 } | |
3177 else if (GET_CODE (sub_mem) == POST_MODIFY) | |
3178 { | |
3179 /* Memory format is (post_modify (reg) (plus (reg) (const))), | |
3180 so that extract (reg) from the post_modify pattern. */ | |
3181 rtx post_mem = XEXP (sub_mem, 0); | |
3182 | |
3183 /* Extract (const) from the (post_modify (reg) (plus (reg) (const))) | |
3184 pattern. */ | |
3185 | |
3186 rtx plus_op = XEXP (sub_mem, 1); | |
3187 rtx post_val = XEXP (plus_op, 1); | |
3188 | |
3189 /* Generate low_part and high_part memory format: | |
3190 low_part: (post_modify ((reg) (plus (reg) (const))) | |
3191 high_part: ((plus (reg) (const 4))) */ | |
3192 low_part[mem] = gen_frame_mem (SImode, | |
3193 gen_rtx_POST_MODIFY (Pmode, post_mem, | |
3194 gen_rtx_PLUS (Pmode, | |
3195 post_mem, | |
3196 post_val))); | |
3197 high_part[mem] = gen_frame_mem (SImode, plus_constant (Pmode, | |
3198 post_mem, | |
3199 4)); | |
3200 } | |
3201 else | |
3202 { | |
3203 /* memory format: (symbol_ref), (const), (reg + const_int). */ | |
3204 low_part[mem] = adjust_address (mem_rtx, SImode, 0); | |
3205 high_part[mem] = adjust_address (mem_rtx, SImode, 4); | |
3206 } | |
3207 | |
3208 /* After reload completed, we have dependent issue by low part register and | |
3209 higt part memory. i.e. we cannot split a sequence | |
3210 like: | |
3211 load $r0, [%r1] | |
3212 spilt to | |
3213 lw $r0, [%r0] | |
3214 lwi $r1, [%r0 + 4] | |
3215 swap position | |
3216 lwi $r1, [%r0 + 4] | |
3217 lw $r0, [%r0] | |
3218 For store instruction we don't have a problem. | |
3219 | |
3220 When memory format is [post_modify], we need to emit high part instruction, | |
3221 before low part instruction. | |
3222 expamle: | |
3223 load $r0, [%r2], post_val | |
3224 spilt to | |
3225 load $r1, [%r2 + 4] | |
3226 load $r0, [$r2], post_val. */ | |
3227 if ((load_p && reg_overlap_mentioned_p (low_part[0], high_part[1])) | |
3228 || GET_CODE (sub_mem) == POST_MODIFY) | |
3229 { | |
3230 operands[2] = high_part[0]; | |
3231 operands[3] = high_part[1]; | |
3232 operands[4] = low_part[0]; | |
3233 operands[5] = low_part[1]; | |
3234 } | |
3235 else | |
3236 { | |
3237 operands[2] = low_part[0]; | |
3238 operands[3] = low_part[1]; | |
3239 operands[4] = high_part[0]; | |
3240 operands[5] = high_part[1]; | |
3241 } | |
3242 } | |
3243 | |
3244 void | |
3245 nds32_split_ashiftdi3 (rtx dst, rtx src, rtx shiftamount) | |
3246 { | |
3247 rtx src_high_part, src_low_part; | |
3248 rtx dst_high_part, dst_low_part; | |
3249 | |
3250 dst_high_part = nds32_di_high_part_subreg (dst); | |
3251 dst_low_part = nds32_di_low_part_subreg (dst); | |
3252 | |
3253 src_high_part = nds32_di_high_part_subreg (src); | |
3254 src_low_part = nds32_di_low_part_subreg (src); | |
3255 | |
3256 /* We need to handle shift more than 32 bit!!!! */ | |
3257 if (CONST_INT_P (shiftamount)) | |
3258 { | |
3259 if (INTVAL (shiftamount) < 32) | |
3260 { | |
3261 rtx ext_start; | |
3262 ext_start = gen_int_mode(32 - INTVAL (shiftamount), SImode); | |
3263 | |
3264 emit_insn (gen_wext (dst_high_part, src, ext_start)); | |
3265 emit_insn (gen_ashlsi3 (dst_low_part, src_low_part, shiftamount)); | |
3266 } | |
3267 else | |
3268 { | |
3269 rtx new_shift_amout = gen_int_mode(INTVAL (shiftamount) - 32, SImode); | |
3270 | |
3271 emit_insn (gen_ashlsi3 (dst_high_part, src_low_part, | |
3272 new_shift_amout)); | |
3273 | |
3274 emit_move_insn (dst_low_part, GEN_INT (0)); | |
3275 } | |
3276 } | |
3277 else | |
3278 { | |
3279 rtx dst_low_part_l32, dst_high_part_l32; | |
3280 rtx dst_low_part_g32, dst_high_part_g32; | |
3281 rtx new_shift_amout, select_reg; | |
3282 dst_low_part_l32 = gen_reg_rtx (SImode); | |
3283 dst_high_part_l32 = gen_reg_rtx (SImode); | |
3284 dst_low_part_g32 = gen_reg_rtx (SImode); | |
3285 dst_high_part_g32 = gen_reg_rtx (SImode); | |
3286 new_shift_amout = gen_reg_rtx (SImode); | |
3287 select_reg = gen_reg_rtx (SImode); | |
3288 | |
3289 rtx ext_start; | |
3290 ext_start = gen_reg_rtx (SImode); | |
3291 | |
3292 /* | |
3293 if (shiftamount < 32) | |
3294 dst_low_part = src_low_part << shiftamout | |
3295 dst_high_part = wext (src, 32 - shiftamount) | |
3296 # wext can't handle wext (src, 32) since it's only take rb[0:4] | |
3297 # for extract. | |
3298 dst_high_part = shiftamount == 0 ? src_high_part : dst_high_part | |
3299 else | |
3300 dst_low_part = 0 | |
3301 dst_high_part = src_low_part << shiftamount & 0x1f | |
3302 */ | |
3303 | |
3304 emit_insn (gen_subsi3 (ext_start, | |
3305 gen_int_mode (32, SImode), | |
3306 shiftamount)); | |
3307 emit_insn (gen_wext (dst_high_part_l32, src, ext_start)); | |
3308 | |
3309 /* Handle for shiftamout == 0. */ | |
3310 emit_insn (gen_cmovzsi (dst_high_part_l32, shiftamount, | |
3311 src_high_part, dst_high_part_l32)); | |
3312 | |
3313 emit_insn (gen_ashlsi3 (dst_low_part_l32, src_low_part, shiftamount)); | |
3314 | |
3315 emit_move_insn (dst_low_part_g32, const0_rtx); | |
3316 emit_insn (gen_andsi3 (new_shift_amout, shiftamount, GEN_INT (0x1f))); | |
3317 emit_insn (gen_ashlsi3 (dst_high_part_g32, src_low_part, | |
3318 new_shift_amout)); | |
3319 | |
3320 emit_insn (gen_slt_compare (select_reg, shiftamount, GEN_INT (32))); | |
3321 | |
3322 emit_insn (gen_cmovnsi (dst_low_part, select_reg, | |
3323 dst_low_part_l32, dst_low_part_g32)); | |
3324 emit_insn (gen_cmovnsi (dst_high_part, select_reg, | |
3325 dst_high_part_l32, dst_high_part_g32)); | |
3326 } | |
3327 } | |
3328 | |
3329 void | |
3330 nds32_split_ashiftrtdi3 (rtx dst, rtx src, rtx shiftamount) | |
3331 { | |
3332 nds32_split_shiftrtdi3 (dst, src, shiftamount, false); | |
3333 } | |
3334 | |
3335 void | |
3336 nds32_split_lshiftrtdi3 (rtx dst, rtx src, rtx shiftamount) | |
3337 { | |
3338 nds32_split_shiftrtdi3 (dst, src, shiftamount, true); | |
3339 } | |
3340 | |
3341 void | |
3342 nds32_split_rotatertdi3 (rtx dst, rtx src, rtx shiftamount) | |
3343 { | |
3344 rtx dst_low_part_l32, dst_high_part_l32; | |
3345 rtx dst_low_part_g32, dst_high_part_g32; | |
3346 rtx select_reg, low5bit, low5bit_inv, minus32sa; | |
3347 rtx dst_low_part_g32_tmph; | |
3348 rtx dst_low_part_g32_tmpl; | |
3349 rtx dst_high_part_l32_tmph; | |
3350 rtx dst_high_part_l32_tmpl; | |
3351 | |
3352 rtx src_low_part, src_high_part; | |
3353 rtx dst_high_part, dst_low_part; | |
3354 | |
3355 shiftamount = force_reg (SImode, shiftamount); | |
3356 | |
3357 emit_insn (gen_andsi3 (shiftamount, | |
3358 shiftamount, | |
3359 gen_int_mode (0x3f, SImode))); | |
3360 | |
3361 dst_high_part = nds32_di_high_part_subreg (dst); | |
3362 dst_low_part = nds32_di_low_part_subreg (dst); | |
3363 | |
3364 src_high_part = nds32_di_high_part_subreg (src); | |
3365 src_low_part = nds32_di_low_part_subreg (src); | |
3366 | |
3367 dst_low_part_l32 = gen_reg_rtx (SImode); | |
3368 dst_high_part_l32 = gen_reg_rtx (SImode); | |
3369 dst_low_part_g32 = gen_reg_rtx (SImode); | |
3370 dst_high_part_g32 = gen_reg_rtx (SImode); | |
3371 low5bit = gen_reg_rtx (SImode); | |
3372 low5bit_inv = gen_reg_rtx (SImode); | |
3373 minus32sa = gen_reg_rtx (SImode); | |
3374 select_reg = gen_reg_rtx (SImode); | |
3375 | |
3376 dst_low_part_g32_tmph = gen_reg_rtx (SImode); | |
3377 dst_low_part_g32_tmpl = gen_reg_rtx (SImode); | |
3378 | |
3379 dst_high_part_l32_tmph = gen_reg_rtx (SImode); | |
3380 dst_high_part_l32_tmpl = gen_reg_rtx (SImode); | |
3381 | |
3382 emit_insn (gen_slt_compare (select_reg, shiftamount, GEN_INT (32))); | |
3383 | |
3384 /* if shiftamount < 32 | |
3385 dst_low_part = wext(src, shiftamount) | |
3386 else | |
3387 dst_low_part = ((src_high_part >> (shiftamount & 0x1f)) | |
3388 | (src_low_part << (32 - (shiftamount & 0x1f)))) | |
3389 */ | |
3390 emit_insn (gen_andsi3 (low5bit, shiftamount, gen_int_mode (0x1f, SImode))); | |
3391 emit_insn (gen_subsi3 (low5bit_inv, gen_int_mode (32, SImode), low5bit)); | |
3392 | |
3393 emit_insn (gen_wext (dst_low_part_l32, src, shiftamount)); | |
3394 | |
3395 emit_insn (gen_lshrsi3 (dst_low_part_g32_tmpl, src_high_part, low5bit)); | |
3396 emit_insn (gen_ashlsi3 (dst_low_part_g32_tmph, src_low_part, low5bit_inv)); | |
3397 | |
3398 emit_insn (gen_iorsi3 (dst_low_part_g32, | |
3399 dst_low_part_g32_tmpl, | |
3400 dst_low_part_g32_tmph)); | |
3401 | |
3402 emit_insn (gen_cmovnsi (dst_low_part, select_reg, | |
3403 dst_low_part_l32, dst_low_part_g32)); | |
3404 | |
3405 /* if shiftamount < 32 | |
3406 dst_high_part = ((src_high_part >> shiftamount) | |
3407 | (src_low_part << (32 - shiftamount))) | |
3408 dst_high_part = shiftamount == 0 ? src_high_part : dst_high_part | |
3409 else | |
3410 dst_high_part = wext(src, shiftamount & 0x1f) | |
3411 */ | |
3412 | |
3413 emit_insn (gen_subsi3 (minus32sa, gen_int_mode (32, SImode), shiftamount)); | |
3414 | |
3415 emit_insn (gen_lshrsi3 (dst_high_part_l32_tmpl, src_high_part, shiftamount)); | |
3416 emit_insn (gen_ashlsi3 (dst_high_part_l32_tmph, src_low_part, minus32sa)); | |
3417 | |
3418 emit_insn (gen_iorsi3 (dst_high_part_l32, | |
3419 dst_high_part_l32_tmpl, | |
3420 dst_high_part_l32_tmph)); | |
3421 | |
3422 emit_insn (gen_cmovzsi (dst_high_part_l32, shiftamount, | |
3423 src_high_part, dst_high_part_l32)); | |
3424 | |
3425 emit_insn (gen_wext (dst_high_part_g32, src, low5bit)); | |
3426 | |
3427 emit_insn (gen_cmovnsi (dst_high_part, select_reg, | |
3428 dst_high_part_l32, dst_high_part_g32)); | |
3429 } | |
3430 | |
3431 /* Return true if OP contains a symbol reference. */ | |
3432 bool | |
3433 symbolic_reference_mentioned_p (rtx op) | |
3434 { | |
3435 const char *fmt; | |
3436 int i; | |
3437 | |
3438 if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) | |
3439 return true; | |
3440 | |
3441 fmt = GET_RTX_FORMAT (GET_CODE (op)); | |
3442 for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) | |
3443 { | |
3444 if (fmt[i] == 'E') | |
3445 { | |
3446 int j; | |
3447 | |
3448 for (j = XVECLEN (op, i) - 1; j >= 0; j--) | |
3449 if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) | |
3450 return true; | |
3451 } | |
3452 | |
3453 else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) | |
3454 return true; | |
3455 } | |
3456 | |
3457 return false; | |
3458 } | |
3459 | |
3460 /* Expand PIC code for @GOTOFF and @GOT. | |
3461 | |
3462 Example for @GOTOFF: | |
3463 | |
3464 la $r0, symbol@GOTOFF | |
3465 -> sethi $ta, hi20(symbol@GOTOFF) | |
3466 ori $ta, $ta, lo12(symbol@GOTOFF) | |
3467 add $r0, $ta, $gp | |
3468 | |
3469 Example for @GOT: | |
3470 | |
3471 la $r0, symbol@GOT | |
3472 -> sethi $ta, hi20(symbol@GOT) | |
3473 ori $ta, $ta, lo12(symbol@GOT) | |
3474 lw $r0, [$ta + $gp] | |
3475 */ | |
3476 rtx | |
3477 nds32_legitimize_pic_address (rtx x) | |
3478 { | |
3479 rtx addr = x; | |
3480 rtx reg = gen_reg_rtx (Pmode); | |
3481 rtx pat; | |
3482 | |
3483 if (GET_CODE (x) == LABEL_REF | |
3484 || (GET_CODE (x) == SYMBOL_REF | |
3485 && (CONSTANT_POOL_ADDRESS_P (x) | |
3486 || SYMBOL_REF_LOCAL_P (x)))) | |
3487 { | |
3488 addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_GOTOFF); | |
3489 addr = gen_rtx_CONST (SImode, addr); | |
3490 emit_insn (gen_sethi (reg, addr)); | |
3491 emit_insn (gen_lo_sum (reg, reg, addr)); | |
3492 x = gen_rtx_PLUS (Pmode, reg, pic_offset_table_rtx); | |
3493 } | |
3494 else if (GET_CODE (x) == SYMBOL_REF) | |
3495 { | |
3496 addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_GOT); | |
3497 addr = gen_rtx_CONST (SImode, addr); | |
3498 emit_insn (gen_sethi (reg, addr)); | |
3499 emit_insn (gen_lo_sum (reg, reg, addr)); | |
3500 | |
3501 x = gen_const_mem (SImode, gen_rtx_PLUS (Pmode, pic_offset_table_rtx, | |
3502 reg)); | |
3503 } | |
3504 else if (GET_CODE (x) == CONST) | |
3505 { | |
3506 /* We don't split constant in expand_pic_move because GOTOFF can combine | |
3507 the addend with the symbol. */ | |
3508 addr = XEXP (x, 0); | |
3509 gcc_assert (GET_CODE (addr) == PLUS); | |
3510 | |
3511 rtx op0 = XEXP (addr, 0); | |
3512 rtx op1 = XEXP (addr, 1); | |
3513 | |
3514 if ((GET_CODE (op0) == LABEL_REF | |
3515 || (GET_CODE (op0) == SYMBOL_REF | |
3516 && (CONSTANT_POOL_ADDRESS_P (op0) | |
3517 || SYMBOL_REF_LOCAL_P (op0)))) | |
3518 && GET_CODE (op1) == CONST_INT) | |
3519 { | |
3520 pat = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), UNSPEC_GOTOFF); | |
3521 pat = gen_rtx_PLUS (Pmode, pat, op1); | |
3522 pat = gen_rtx_CONST (Pmode, pat); | |
3523 emit_insn (gen_sethi (reg, pat)); | |
3524 emit_insn (gen_lo_sum (reg, reg, pat)); | |
3525 x = gen_rtx_PLUS (Pmode, reg, pic_offset_table_rtx); | |
3526 } | |
3527 else if (GET_CODE (op0) == SYMBOL_REF | |
3528 && GET_CODE (op1) == CONST_INT) | |
3529 { | |
3530 /* This is a constant offset from a @GOT symbol reference. */ | |
3531 addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, op0), UNSPEC_GOT); | |
3532 addr = gen_rtx_CONST (SImode, addr); | |
3533 emit_insn (gen_sethi (reg, addr)); | |
3534 emit_insn (gen_lo_sum (reg, reg, addr)); | |
3535 addr = gen_const_mem (SImode, gen_rtx_PLUS (Pmode, | |
3536 pic_offset_table_rtx, | |
3537 reg)); | |
3538 emit_move_insn (reg, addr); | |
3539 if (satisfies_constraint_Is15 (op1)) | |
3540 x = gen_rtx_PLUS (Pmode, reg, op1); | |
3541 else | |
3542 { | |
3543 rtx tmp_reg = gen_reg_rtx (SImode); | |
3544 emit_insn (gen_movsi (tmp_reg, op1)); | |
3545 x = gen_rtx_PLUS (Pmode, reg, tmp_reg); | |
3546 } | |
3547 } | |
3548 else | |
3549 { | |
3550 /* Don't handle this pattern. */ | |
3551 debug_rtx (x); | |
3552 gcc_unreachable (); | |
3553 } | |
3554 } | |
3555 return x; | |
3556 } | |
3557 | |
3558 void | |
3559 nds32_expand_pic_move (rtx *operands) | |
3560 { | |
3561 rtx src; | |
3562 | |
3563 src = nds32_legitimize_pic_address (operands[1]); | |
3564 emit_move_insn (operands[0], src); | |
3565 } | |
3566 | |
3567 /* Expand ICT symbol. | |
3568 Example for @ICT and ICT model=large: | |
3569 | |
3570 la $r0, symbol@ICT | |
3571 -> sethi $rt, hi20(symbol@ICT) | |
3572 lwi $r0, [$rt + lo12(symbol@ICT)] | |
3573 | |
3574 */ | |
3575 rtx | |
3576 nds32_legitimize_ict_address (rtx x) | |
3577 { | |
3578 rtx symbol = x; | |
3579 rtx addr = x; | |
3580 rtx reg = gen_reg_rtx (Pmode); | |
3581 gcc_assert (GET_CODE (x) == SYMBOL_REF | |
3582 && nds32_indirect_call_referenced_p (x)); | |
3583 | |
3584 addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, symbol), UNSPEC_ICT); | |
3585 addr = gen_rtx_CONST (SImode, addr); | |
3586 emit_insn (gen_sethi (reg, addr)); | |
3587 | |
3588 x = gen_const_mem (SImode, gen_rtx_LO_SUM (Pmode, reg, addr)); | |
3589 | |
3590 return x; | |
3591 } | |
3592 | |
3593 void | |
3594 nds32_expand_ict_move (rtx *operands) | |
3595 { | |
3596 rtx src = operands[1]; | |
3597 | |
3598 src = nds32_legitimize_ict_address (src); | |
3599 | |
3600 emit_move_insn (operands[0], src); | |
3601 } | |
3602 | |
3603 /* Return true X is a indirect call symbol. */ | |
3604 bool | |
3605 nds32_indirect_call_referenced_p (rtx x) | |
3606 { | |
3607 if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_ICT) | |
3608 x = XVECEXP (x, 0, 0); | |
3609 | |
3610 if (GET_CODE (x) == SYMBOL_REF) | |
3611 { | |
3612 tree decl = SYMBOL_REF_DECL (x); | |
3613 | |
3614 return decl | |
3615 && (lookup_attribute("indirect_call", | |
3616 DECL_ATTRIBUTES(decl)) | |
3617 != NULL); | |
3618 } | |
3619 | |
3620 return false; | |
3621 } | |
3622 | |
3623 /* Return true X is need use long call. */ | |
3624 bool | |
3625 nds32_long_call_p (rtx symbol) | |
3626 { | |
3627 if (nds32_indirect_call_referenced_p (symbol)) | |
3628 return TARGET_ICT_MODEL_LARGE; | |
3629 else | |
3630 return TARGET_CMODEL_LARGE; | |
3631 } | |
3632 | |
3633 /* Return true if X contains a thread-local symbol. */ | |
3634 bool | |
3635 nds32_tls_referenced_p (rtx x) | |
3636 { | |
3637 if (!targetm.have_tls) | |
3638 return false; | |
3639 | |
3640 if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS) | |
3641 x = XEXP (XEXP (x, 0), 0); | |
3642 | |
3643 if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x)) | |
3644 return true; | |
3645 | |
3646 return false; | |
3647 } | |
3648 | |
3649 /* ADDR contains a thread-local SYMBOL_REF. Generate code to compute | |
3650 this (thread-local) address. */ | |
3651 rtx | |
3652 nds32_legitimize_tls_address (rtx x) | |
3653 { | |
3654 rtx tmp_reg; | |
3655 rtx tp_reg = gen_rtx_REG (Pmode, TP_REGNUM); | |
3656 rtx pat, insns, reg0; | |
3657 | |
3658 if (GET_CODE (x) == SYMBOL_REF) | |
3659 switch (SYMBOL_REF_TLS_MODEL (x)) | |
3660 { | |
3661 case TLS_MODEL_GLOBAL_DYNAMIC: | |
3662 case TLS_MODEL_LOCAL_DYNAMIC: | |
3663 /* Emit UNSPEC_TLS_DESC rather than expand rtl directly because spill | |
3664 may destroy the define-use chain anylysis to insert relax_hint. */ | |
3665 if (SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_GLOBAL_DYNAMIC) | |
3666 pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSGD); | |
3667 else | |
3668 pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSLD); | |
3669 | |
3670 pat = gen_rtx_CONST (SImode, pat); | |
3671 reg0 = gen_rtx_REG (Pmode, 0); | |
3672 /* If we can confirm all clobber reigsters, it doesn't have to use call | |
3673 instruction. */ | |
3674 insns = emit_call_insn (gen_tls_desc (pat, GEN_INT (0))); | |
3675 use_reg (&CALL_INSN_FUNCTION_USAGE (insns), pic_offset_table_rtx); | |
3676 RTL_CONST_CALL_P (insns) = 1; | |
3677 tmp_reg = gen_reg_rtx (SImode); | |
3678 emit_move_insn (tmp_reg, reg0); | |
3679 x = tmp_reg; | |
3680 break; | |
3681 | |
3682 case TLS_MODEL_INITIAL_EXEC: | |
3683 pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSIE); | |
3684 tmp_reg = gen_reg_rtx (SImode); | |
3685 pat = gen_rtx_CONST (SImode, pat); | |
3686 emit_insn (gen_tls_ie (tmp_reg, pat, GEN_INT (0))); | |
3687 if (flag_pic) | |
3688 emit_use (pic_offset_table_rtx); | |
3689 x = gen_rtx_PLUS (Pmode, tmp_reg, tp_reg); | |
3690 break; | |
3691 | |
3692 case TLS_MODEL_LOCAL_EXEC: | |
3693 /* Expand symbol_ref@TPOFF': | |
3694 sethi $ta, hi20(symbol_ref@TPOFF) | |
3695 ori $ta, $ta, lo12(symbol_ref@TPOFF) | |
3696 add $r0, $ta, $tp */ | |
3697 tmp_reg = gen_reg_rtx (SImode); | |
3698 pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSLE); | |
3699 pat = gen_rtx_CONST (SImode, pat); | |
3700 emit_insn (gen_sethi (tmp_reg, pat)); | |
3701 emit_insn (gen_lo_sum (tmp_reg, tmp_reg, pat)); | |
3702 x = gen_rtx_PLUS (Pmode, tmp_reg, tp_reg); | |
3703 break; | |
3704 | |
3705 default: | |
3706 gcc_unreachable (); | |
3707 } | |
3708 else if (GET_CODE (x) == CONST) | |
3709 { | |
3710 rtx base, addend; | |
3711 split_const (x, &base, &addend); | |
3712 | |
3713 if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_LOCAL_EXEC) | |
3714 { | |
3715 /* Expand symbol_ref@TPOFF': | |
3716 sethi $ta, hi20(symbol_ref@TPOFF + addend) | |
3717 ori $ta, $ta, lo12(symbol_ref@TPOFF + addend) | |
3718 add $r0, $ta, $tp */ | |
3719 tmp_reg = gen_reg_rtx (SImode); | |
3720 pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, base), UNSPEC_TLSLE); | |
3721 pat = gen_rtx_PLUS (SImode, pat, addend); | |
3722 pat = gen_rtx_CONST (SImode, pat); | |
3723 emit_insn (gen_sethi (tmp_reg, pat)); | |
3724 emit_insn (gen_lo_sum (tmp_reg, tmp_reg, pat)); | |
3725 x = gen_rtx_PLUS (Pmode, tmp_reg, tp_reg); | |
3726 } | |
3727 } | |
3728 | |
3729 return x; | |
3730 } | |
3731 | |
3732 void | |
3733 nds32_expand_tls_move (rtx *operands) | |
3734 { | |
3735 rtx src = operands[1]; | |
3736 rtx base, addend; | |
3737 | |
3738 if (CONSTANT_P (src)) | |
3739 split_const (src, &base, &addend); | |
3740 | |
3741 if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_LOCAL_EXEC) | |
3742 src = nds32_legitimize_tls_address (src); | |
3743 else | |
3744 { | |
3745 src = nds32_legitimize_tls_address (base); | |
3746 if (addend != const0_rtx) | |
3747 { | |
3748 src = gen_rtx_PLUS (SImode, src, addend); | |
3749 src = force_operand (src, operands[0]); | |
3750 } | |
3751 } | |
3752 | |
3753 emit_move_insn (operands[0], src); | |
3754 } | |
3755 | |
3756 void | |
3757 nds32_expand_constant (machine_mode mode, HOST_WIDE_INT val, | |
3758 rtx target, rtx source) | |
3759 { | |
3760 rtx temp = gen_reg_rtx (mode); | |
3761 int clear_sign_bit_copies = 0; | |
3762 int clear_zero_bit_copies = 0; | |
3763 unsigned HOST_WIDE_INT remainder = val & 0xffffffffUL; | |
3764 | |
3765 /* Count number of leading zeros. */ | |
3766 clear_sign_bit_copies = __builtin_clz (remainder); | |
3767 /* Count number of trailing zeros. */ | |
3768 clear_zero_bit_copies = __builtin_ctz (remainder); | |
3769 | |
3770 HOST_WIDE_INT sign_shift_mask = ((0xffffffffUL | |
3771 << (32 - clear_sign_bit_copies)) | |
3772 & 0xffffffffUL); | |
3773 HOST_WIDE_INT zero_shift_mask = (1 << clear_zero_bit_copies) - 1; | |
3774 | |
3775 if (clear_sign_bit_copies > 0 && clear_sign_bit_copies < 17 | |
3776 && (remainder | sign_shift_mask) == 0xffffffffUL) | |
3777 { | |
3778 /* Transfer AND to two shifts, example: | |
3779 a = b & 0x7fffffff => (b << 1) >> 1 */ | |
3780 rtx shift = GEN_INT (clear_sign_bit_copies); | |
3781 | |
3782 emit_insn (gen_ashlsi3 (temp, source, shift)); | |
3783 emit_insn (gen_lshrsi3 (target, temp, shift)); | |
3784 } | |
3785 else if (clear_zero_bit_copies > 0 && clear_sign_bit_copies < 17 | |
3786 && (remainder | zero_shift_mask) == 0xffffffffUL) | |
3787 { | |
3788 /* Transfer AND to two shifts, example: | |
3789 a = b & 0xfff00000 => (b >> 20) << 20 */ | |
3790 rtx shift = GEN_INT (clear_zero_bit_copies); | |
3791 | |
3792 emit_insn (gen_lshrsi3 (temp, source, shift)); | |
3793 emit_insn (gen_ashlsi3 (target, temp, shift)); | |
3794 } | |
3795 else | |
3796 { | |
3797 emit_move_insn (temp, GEN_INT (val)); | |
3798 emit_move_insn (target, gen_rtx_fmt_ee (AND, mode, source, temp)); | |
3799 } | |
3800 } | |
3801 | |
3802 /* Auxiliary functions for lwm/smw. */ | |
3803 bool | |
3804 nds32_valid_smw_lwm_base_p (rtx op) | |
3805 { | |
3806 rtx base_addr; | |
3807 | |
3808 if (!MEM_P (op)) | |
3809 return false; | |
3810 | |
3811 base_addr = XEXP (op, 0); | |
3812 | |
3813 if (REG_P (base_addr)) | |
3814 return true; | |
3815 else | |
3816 { | |
3817 if (GET_CODE (base_addr) == POST_INC | |
3818 && REG_P (XEXP (base_addr, 0))) | |
3819 return true; | |
3820 } | |
3821 | |
3822 return false; | |
3823 } | |
3824 | |
3825 /* Auxiliary functions for manipulation DI mode. */ | |
3826 rtx nds32_di_high_part_subreg(rtx reg) | |
3827 { | |
3828 unsigned high_part_offset = subreg_highpart_offset (SImode, DImode); | |
3829 | |
3830 return simplify_gen_subreg ( | |
3831 SImode, reg, | |
3832 DImode, high_part_offset); | |
3833 } | |
3834 | |
3835 rtx nds32_di_low_part_subreg(rtx reg) | |
3836 { | |
3837 unsigned low_part_offset = subreg_lowpart_offset (SImode, DImode); | |
3838 | |
3839 return simplify_gen_subreg ( | |
3840 SImode, reg, | |
3841 DImode, low_part_offset); | |
848 } | 3842 } |
849 | 3843 |
850 /* ------------------------------------------------------------------------ */ | 3844 /* ------------------------------------------------------------------------ */ |
3845 | |
3846 /* Auxiliary function for output TLS patterns. */ | |
3847 | |
3848 const char * | |
3849 nds32_output_tls_desc (rtx *operands) | |
3850 { | |
3851 char pattern[1000]; | |
3852 | |
3853 if (TARGET_RELAX_HINT) | |
3854 snprintf (pattern, sizeof (pattern), | |
3855 ".relax_hint %%1\n\tsethi $r0, hi20(%%0)\n\t" | |
3856 ".relax_hint %%1\n\tori $r0, $r0, lo12(%%0)\n\t" | |
3857 ".relax_hint %%1\n\tlw $r15, [$r0 + $gp]\n\t" | |
3858 ".relax_hint %%1\n\tadd $r0, $r0, $gp\n\t" | |
3859 ".relax_hint %%1\n\tjral $r15"); | |
3860 else | |
3861 snprintf (pattern, sizeof (pattern), | |
3862 "sethi $r0, hi20(%%0)\n\t" | |
3863 "ori $r0, $r0, lo12(%%0)\n\t" | |
3864 "lw $r15, [$r0 + $gp]\n\t" | |
3865 "add $r0, $r0, $gp\n\t" | |
3866 "jral $r15"); | |
3867 output_asm_insn (pattern, operands); | |
3868 return ""; | |
3869 } | |
3870 | |
3871 const char * | |
3872 nds32_output_tls_ie (rtx *operands) | |
3873 { | |
3874 char pattern[1000]; | |
3875 | |
3876 if (flag_pic) | |
3877 { | |
3878 if (TARGET_RELAX_HINT) | |
3879 snprintf (pattern, sizeof (pattern), | |
3880 ".relax_hint %%2\n\tsethi %%0, hi20(%%1)\n\t" | |
3881 ".relax_hint %%2\n\tori %%0, %%0, lo12(%%1)\n\t" | |
3882 ".relax_hint %%2\n\tlw %%0, [%%0 + $gp]"); | |
3883 else | |
3884 snprintf (pattern, sizeof (pattern), | |
3885 "sethi %%0, hi20(%%1)\n\t" | |
3886 "ori %%0, %%0, lo12(%%1)\n\t" | |
3887 "lw %%0, [%%0 + $gp]"); | |
3888 } | |
3889 else | |
3890 { | |
3891 if (TARGET_RELAX_HINT) | |
3892 snprintf (pattern, sizeof (pattern), | |
3893 ".relax_hint %%2\n\tsethi %%0, hi20(%%1)\n\t" | |
3894 ".relax_hint %%2\n\tlwi %%0, [%%0 + lo12(%%1)]"); | |
3895 else | |
3896 snprintf (pattern, sizeof (pattern), | |
3897 "sethi %%0, hi20(%%1)\n\t" | |
3898 "lwi %%0, [%%0 + lo12(%%1)]"); | |
3899 } | |
3900 output_asm_insn (pattern, operands); | |
3901 return ""; | |
3902 } |