]>
Commit | Line | Data |
---|---|---|
b6616c9c | 1 | /* Target Code for ft32 |
aad93da1 | 2 | Copyright (C) 2015-2017 Free Software Foundation, Inc. |
b6616c9c | 3 | Contributed by FTDI <support@ftdi.com> |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU General Public License as published | |
9 | by the Free Software Foundation; either version 3, or (at your | |
10 | option) any later version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT | |
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
15 | License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "config.h" | |
22 | #include "system.h" | |
23 | #include "coretypes.h" | |
9ef16211 | 24 | #include "backend.h" |
c1eb80de | 25 | #include "target.h" |
b6616c9c | 26 | #include "rtl.h" |
c1eb80de | 27 | #include "tree.h" |
9ef16211 | 28 | #include "df.h" |
ad7b10a2 | 29 | #include "memmodel.h" |
c1eb80de | 30 | #include "tm_p.h" |
b6616c9c | 31 | #include "regs.h" |
c1eb80de | 32 | #include "emit-rtl.h" |
b6616c9c | 33 | #include "diagnostic-core.h" |
c1eb80de | 34 | #include "output.h" |
b6616c9c | 35 | #include "stor-layout.h" |
36 | #include "calls.h" | |
37 | #include "expr.h" | |
b6616c9c | 38 | #include "builtins.h" |
44c5a84b | 39 | #include "print-tree.h" |
b6616c9c | 40 | |
0c71fb4f | 41 | /* This file should be included last. */ |
42 | #include "target-def.h" | |
43 | ||
b6616c9c | 44 | #include <stdint.h> |
45 | ||
46 | #define LOSE_AND_RETURN(msgid, x) \ | |
47 | do \ | |
48 | { \ | |
49 | ft32_operand_lossage (msgid, x); \ | |
50 | return; \ | |
51 | } while (0) | |
52 | ||
53 | /* Worker function for TARGET_RETURN_IN_MEMORY. */ | |
54 | ||
55 | static bool | |
56 | ft32_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) | |
57 | { | |
58 | const HOST_WIDE_INT size = int_size_in_bytes (type); | |
59 | return (size == -1 || size > 2 * UNITS_PER_WORD); | |
60 | } | |
61 | ||
62 | /* Define how to find the value returned by a function. | |
63 | VALTYPE is the data type of the value (as a tree). | |
64 | If the precise function being called is known, FUNC is its | |
65 | FUNCTION_DECL; otherwise, FUNC is 0. | |
66 | ||
67 | We always return values in register $r0 for ft32. */ | |
68 | ||
69 | static rtx | |
70 | ft32_function_value (const_tree valtype, | |
71 | const_tree fntype_or_decl ATTRIBUTE_UNUSED, | |
72 | bool outgoing ATTRIBUTE_UNUSED) | |
73 | { | |
74 | return gen_rtx_REG (TYPE_MODE (valtype), FT32_R0); | |
75 | } | |
76 | ||
77 | /* Define how to find the value returned by a library function. | |
78 | ||
79 | We always return values in register $r0 for ft32. */ | |
80 | ||
81 | static rtx | |
582adad1 | 82 | ft32_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED) |
b6616c9c | 83 | { |
84 | return gen_rtx_REG (mode, FT32_R0); | |
85 | } | |
86 | ||
87 | /* Handle TARGET_FUNCTION_VALUE_REGNO_P. | |
88 | ||
89 | We always return values in register $r0 for ft32. */ | |
90 | ||
91 | static bool | |
92 | ft32_function_value_regno_p (const unsigned int regno) | |
93 | { | |
94 | return (regno == FT32_R0); | |
95 | } | |
96 | ||
97 | /* Emit an error message when we're in an asm, and a fatal error for | |
98 | "normal" insns. Formatted output isn't easily implemented, since we | |
99 | use output_operand_lossage to output the actual message and handle the | |
100 | categorization of the error. */ | |
101 | ||
102 | static void | |
103 | ft32_operand_lossage (const char *msgid, rtx op) | |
104 | { | |
105 | debug_rtx (op); | |
106 | output_operand_lossage ("%s", msgid); | |
107 | } | |
108 | ||
109 | /* The PRINT_OPERAND_ADDRESS worker. */ | |
110 | ||
111 | void | |
112 | ft32_print_operand_address (FILE * file, rtx x) | |
113 | { | |
114 | switch (GET_CODE (x)) | |
115 | { | |
116 | case REG: | |
117 | fprintf (file, "%s,0", reg_names[REGNO (x)]); | |
118 | break; | |
119 | ||
120 | case PLUS: | |
121 | switch (GET_CODE (XEXP (x, 1))) | |
122 | { | |
123 | case CONST_INT: | |
124 | fprintf (file, "%s,%ld", | |
125 | reg_names[REGNO (XEXP (x, 0))], INTVAL (XEXP (x, 1))); | |
126 | break; | |
127 | case SYMBOL_REF: | |
128 | output_addr_const (file, XEXP (x, 1)); | |
129 | fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]); | |
130 | break; | |
131 | case CONST: | |
132 | { | |
133 | rtx plus = XEXP (XEXP (x, 1), 0); | |
134 | if (GET_CODE (XEXP (plus, 0)) == SYMBOL_REF | |
135 | && CONST_INT_P (XEXP (plus, 1))) | |
136 | { | |
137 | output_addr_const (file, XEXP (plus, 0)); | |
138 | fprintf (file, "+%ld(%s)", INTVAL (XEXP (plus, 1)), | |
139 | reg_names[REGNO (XEXP (x, 0))]); | |
140 | } | |
141 | else | |
142 | abort (); | |
143 | } | |
144 | break; | |
145 | default: | |
146 | abort (); | |
147 | } | |
148 | break; | |
149 | ||
150 | default: | |
151 | output_addr_const (file, x); | |
152 | break; | |
153 | } | |
154 | } | |
155 | ||
156 | /* The PRINT_OPERAND worker. */ | |
157 | ||
158 | void | |
159 | ft32_print_operand (FILE * file, rtx x, int code) | |
160 | { | |
161 | rtx operand = x; | |
162 | ||
163 | /* New code entries should just be added to the switch below. If | |
164 | handling is finished, just return. If handling was just a | |
165 | modification of the operand, the modified operand should be put in | |
166 | "operand", and then do a break to let default handling | |
167 | (zero-modifier) output the operand. */ | |
168 | ||
169 | switch (code) | |
170 | { | |
171 | case 0: | |
172 | /* No code, print as usual. */ | |
173 | break; | |
174 | ||
175 | case 'h': | |
176 | if (GET_CODE (operand) != REG) | |
177 | internal_error ("'h' applied to non-register operand"); | |
178 | fprintf (file, "%s", reg_names[REGNO (operand) + 1]); | |
179 | return; | |
180 | ||
181 | case 'm': | |
cc16f5e2 | 182 | fprintf (file, "%ld", (long) (- INTVAL(x))); |
b6616c9c | 183 | return; |
184 | ||
185 | case 'd': // a DW spec, from an integer alignment (for BLKmode insns) | |
186 | { | |
187 | int i = INTVAL (x); | |
188 | char dwspec; | |
189 | switch (i) | |
190 | { | |
191 | case 1: | |
192 | dwspec = 'b'; | |
193 | break; | |
194 | case 2: | |
195 | dwspec = 's'; | |
196 | break; | |
197 | case 4: | |
198 | dwspec = 'l'; | |
199 | break; | |
200 | default: | |
201 | if ((i % 4) != 0) | |
202 | internal_error ("bad alignment: %d", i); | |
203 | else | |
204 | dwspec = 'l'; | |
205 | break; | |
206 | } | |
207 | fprintf (file, "%c", dwspec); | |
208 | return; | |
209 | } | |
210 | ||
211 | case 'f': | |
212 | { | |
213 | int bf = ft32_as_bitfield (INTVAL (x)); | |
214 | fprintf (file, "512|(%d<<5)|%d", bf >> 5, bf & 31); | |
215 | return; | |
216 | } | |
217 | ||
218 | case 'g': | |
219 | { | |
220 | int bf = ft32_as_bitfield (0xffffffff ^ INTVAL (x)); | |
221 | fprintf (file, "(%d<<5)|%d", bf >> 5, bf & 31); | |
222 | return; | |
223 | } | |
224 | ||
225 | case 'b': | |
226 | { | |
227 | ft32_print_operand (file, XEXP (x, 0), 0); | |
228 | return; | |
229 | } | |
230 | ||
231 | default: | |
232 | LOSE_AND_RETURN ("invalid operand modifier letter", x); | |
233 | } | |
234 | ||
235 | /* Print an operand as without a modifier letter. */ | |
236 | switch (GET_CODE (operand)) | |
237 | { | |
238 | case REG: | |
239 | fprintf (file, "%s", reg_names[REGNO (operand)]); | |
240 | return; | |
241 | ||
242 | case MEM: | |
b408dd85 | 243 | output_address (GET_MODE (XEXP (operand, 0)), XEXP (operand, 0)); |
b6616c9c | 244 | return; |
245 | ||
246 | default: | |
247 | /* No need to handle all strange variants, let output_addr_const | |
248 | do it for us. */ | |
249 | if (CONSTANT_P (operand)) | |
250 | { | |
251 | output_addr_const (file, operand); | |
252 | return; | |
253 | } | |
254 | ||
255 | LOSE_AND_RETURN ("unexpected operand", x); | |
256 | } | |
257 | } | |
258 | ||
259 | const char * | |
260 | ft32_load_immediate (rtx dst, int32_t i) | |
261 | { | |
262 | char pattern[100]; | |
263 | ||
264 | if ((-524288 <= i) && (i <= 524287)) | |
265 | { | |
266 | sprintf (pattern, "ldk.l %%0,%d", i); | |
267 | output_asm_insn (pattern, &dst); | |
268 | } | |
269 | else if ((-536870912 <= i) && (i <= 536870911)) | |
270 | { | |
271 | ft32_load_immediate (dst, i >> 10); | |
272 | sprintf (pattern, "ldl.l %%0,%%0,%d", i & 1023); | |
273 | output_asm_insn (pattern, &dst); | |
274 | } | |
275 | else | |
276 | { | |
277 | int rd; // rotate distance | |
278 | uint32_t u = i; | |
279 | for (rd = 1; rd < 32; rd++) | |
280 | { | |
281 | u = ((u >> 31) & 1) | (u << 1); | |
282 | if ((-524288 <= (int32_t) u) && ((int32_t) u <= 524287)) | |
283 | { | |
284 | ft32_load_immediate (dst, (int32_t) u); | |
285 | sprintf (pattern, "ror.l %%0,%%0,%d", rd); | |
286 | output_asm_insn (pattern, &dst); | |
287 | return ""; | |
288 | } | |
289 | } | |
290 | ft32_load_immediate (dst, i >> 10); | |
291 | sprintf (pattern, "ldl.l %%0,%%0,%d", i & 1023); | |
292 | output_asm_insn (pattern, &dst); | |
293 | } | |
294 | ||
295 | return ""; | |
296 | } | |
297 | ||
298 | // x is a bit mask, for example: | |
299 | // 00000000000000000000001111111110 | |
300 | // If x contains a single bit mask, return the bitfield spec. | |
301 | // in the above case it returns ((9 << 5) | 1) | |
302 | // Otherwise return -1. | |
303 | // | |
304 | ||
305 | #define NBITS(n) ((1U << (n)) - 1U) | |
306 | ||
307 | int | |
308 | ft32_as_bitfield (unsigned int x) | |
309 | { | |
310 | int lobit, hibit; | |
311 | ||
312 | if (x == 0) | |
313 | return -1; | |
314 | ||
315 | for (lobit = 0; lobit < 32; lobit++) | |
316 | if (x & (1 << lobit)) | |
317 | break; | |
318 | for (hibit = 31; hibit >= 0; hibit--) | |
319 | if (x & (1 << hibit)) | |
320 | break; | |
321 | ||
322 | int width = 1 + hibit - lobit; | |
323 | if (width > 16) | |
324 | return -1; | |
325 | ||
326 | if (x != (NBITS (width) << lobit)) | |
327 | return -1; // not a clean bitfield | |
328 | ||
329 | return ((width & 15) << 5) | lobit; | |
330 | } | |
331 | ||
332 | /* Per-function machine data. */ | |
333 | struct GTY (()) machine_function | |
334 | { | |
335 | /* Number of bytes saved on the stack for callee saved registers. */ | |
336 | int callee_saved_reg_size; | |
337 | ||
338 | /* Number of bytes saved on the stack for local variables. */ | |
339 | int local_vars_size; | |
340 | ||
341 | /* The sum of 2 sizes: locals vars and padding byte for saving the | |
342 | * registers. Used in expand_prologue () and expand_epilogue (). */ | |
343 | int size_for_adjusting_sp; | |
344 | }; | |
345 | ||
346 | /* Zero initialization is OK for all current fields. */ | |
347 | ||
348 | static struct machine_function * | |
349 | ft32_init_machine_status (void) | |
350 | { | |
351 | return ggc_cleared_alloc < machine_function > (); | |
352 | } | |
353 | ||
354 | ||
355 | /* The TARGET_OPTION_OVERRIDE worker. | |
356 | All this curently does is set init_machine_status. */ | |
357 | static void | |
358 | ft32_option_override (void) | |
359 | { | |
360 | /* Set the per-function-data initializer. */ | |
361 | init_machine_status = ft32_init_machine_status; | |
362 | } | |
363 | ||
364 | /* Implement targetm.select_section. */ | |
365 | static section * | |
366 | ft32_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) | |
367 | { | |
368 | /* Variables and constants defined in the __ea address space | |
369 | go into a special section named "._ea". */ | |
370 | if (TREE_TYPE (decl) != error_mark_node | |
371 | && TYPE_ADDR_SPACE (TREE_TYPE (decl)) == ADDR_SPACE_PM) | |
372 | { | |
373 | /* We might get called with string constants, but get_named_section | |
374 | doesn't like them as they are not DECLs. Also, we need to set | |
375 | flags in that case. */ | |
376 | if (!DECL_P (decl)) | |
377 | return get_section ("._pm", SECTION_WRITE | SECTION_DEBUG, NULL); | |
378 | ||
379 | return get_named_section (decl, "._pm", reloc); | |
380 | } | |
381 | ||
382 | return default_elf_select_section (decl, reloc, align); | |
383 | } | |
384 | ||
385 | /* Compute the size of the local area and the size to be adjusted by the | |
386 | * prologue and epilogue. */ | |
387 | ||
388 | static void | |
389 | ft32_compute_frame (void) | |
390 | { | |
391 | /* For aligning the local variables. */ | |
392 | int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT; | |
393 | int padding_locals; | |
394 | int regno; | |
395 | ||
396 | /* Padding needed for each element of the frame. */ | |
397 | cfun->machine->local_vars_size = get_frame_size (); | |
398 | ||
399 | /* Align to the stack alignment. */ | |
400 | padding_locals = cfun->machine->local_vars_size % stack_alignment; | |
401 | if (padding_locals) | |
402 | padding_locals = stack_alignment - padding_locals; | |
403 | ||
404 | cfun->machine->local_vars_size += padding_locals; | |
405 | ||
406 | cfun->machine->callee_saved_reg_size = 0; | |
407 | ||
408 | /* Save callee-saved registers. */ | |
409 | for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) | |
410 | if (df_regs_ever_live_p (regno) && (!call_used_regs[regno])) | |
411 | cfun->machine->callee_saved_reg_size += 4; | |
412 | ||
413 | cfun->machine->size_for_adjusting_sp = | |
da2a0082 | 414 | 0 // crtl->args.pretend_args_size |
b6616c9c | 415 | + cfun->machine->local_vars_size |
416 | + (ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0); | |
417 | } | |
418 | ||
419 | // Must use LINK/UNLINK when... | |
420 | // the frame is bigger than 512 bytes so cannot just "SUB" from SP | |
421 | // the function actually uses $fp | |
422 | ||
423 | static int | |
424 | must_link (void) | |
425 | { | |
426 | int bigframe = (cfun->machine->size_for_adjusting_sp >= 512); | |
427 | return (bigframe || frame_pointer_needed || df_regs_ever_live_p (FT32_FP) | |
428 | || df_regs_ever_live_p (FT32_FP)); | |
429 | } | |
430 | ||
431 | void | |
432 | ft32_expand_prologue (void) | |
433 | { | |
434 | int regno; | |
435 | rtx insn; | |
436 | ||
437 | ft32_compute_frame (); | |
438 | ||
da2a0082 | 439 | int args_to_push = crtl->args.pretend_args_size; |
440 | if (args_to_push) | |
441 | { | |
442 | int i; | |
443 | ||
444 | insn = emit_insn (gen_movsi_pop ((gen_rtx_REG (Pmode, FT32_R29)))); | |
445 | ||
446 | for (i = 0; i < (args_to_push / 4); i++) | |
447 | { | |
448 | insn = | |
449 | emit_insn (gen_movsi_push ((gen_rtx_REG (Pmode, FT32_R5 - i)))); | |
450 | RTX_FRAME_RELATED_P (insn) = 1; | |
451 | } | |
452 | ||
453 | insn = emit_insn (gen_movsi_push ((gen_rtx_REG (Pmode, FT32_R29)))); | |
454 | } | |
455 | ||
cc16f5e2 | 456 | if (flag_stack_usage_info) |
457 | current_function_static_stack_size = cfun->machine->size_for_adjusting_sp; | |
458 | ||
b6616c9c | 459 | if (!must_link () && (cfun->machine->callee_saved_reg_size == 4)) |
460 | { | |
461 | insn = | |
da2a0082 | 462 | emit_insn (gen_link |
463 | (gen_rtx_REG (Pmode, FT32_R13), | |
464 | GEN_INT (-cfun->machine->size_for_adjusting_sp))); | |
b6616c9c | 465 | RTX_FRAME_RELATED_P (insn) = 1; |
466 | return; | |
467 | } | |
468 | /* Save callee-saved registers. */ | |
469 | if (optimize_size) | |
470 | { | |
471 | for (regno = FIRST_PSEUDO_REGISTER; regno-- > 0;) | |
da2a0082 | 472 | { |
473 | if (!fixed_regs[regno] && !call_used_regs[regno] | |
474 | && df_regs_ever_live_p (regno)) | |
475 | { | |
476 | rtx preg = gen_rtx_REG (Pmode, regno); | |
477 | emit_insn (gen_call_prolog (preg)); | |
478 | break; | |
479 | } | |
480 | } | |
b6616c9c | 481 | } |
482 | else | |
483 | { | |
484 | for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) | |
da2a0082 | 485 | { |
486 | if (!fixed_regs[regno] && df_regs_ever_live_p (regno) | |
487 | && !call_used_regs[regno]) | |
488 | { | |
489 | insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); | |
490 | RTX_FRAME_RELATED_P (insn) = 1; | |
491 | } | |
492 | } | |
b6616c9c | 493 | } |
494 | ||
495 | if (65536 <= cfun->machine->size_for_adjusting_sp) | |
496 | { | |
497 | error ("stack frame must be smaller than 64K"); | |
498 | return; | |
499 | } | |
500 | if (must_link ()) | |
501 | { | |
502 | insn = | |
da2a0082 | 503 | emit_insn (gen_link |
504 | (gen_rtx_REG (Pmode, FT32_FP), | |
505 | GEN_INT (-cfun->machine->size_for_adjusting_sp))); | |
b6616c9c | 506 | RTX_FRAME_RELATED_P (insn) = 1; |
507 | } | |
508 | else if (cfun->machine->size_for_adjusting_sp > 0) | |
509 | { | |
da2a0082 | 510 | int adj = cfun->machine->size_for_adjusting_sp; |
b6616c9c | 511 | insn = emit_insn (gen_addsi3 (gen_rtx_REG (SImode, FT32_SP), |
da2a0082 | 512 | gen_rtx_REG (SImode, FT32_SP), |
513 | GEN_INT (-adj))); | |
b6616c9c | 514 | RTX_FRAME_RELATED_P (insn) = 1; |
515 | } | |
516 | } | |
517 | ||
518 | void | |
519 | ft32_expand_epilogue (void) | |
520 | { | |
521 | int regno; | |
da2a0082 | 522 | int pretend = crtl->args.pretend_args_size; |
b6616c9c | 523 | |
524 | if (!must_link () | |
525 | && (cfun->machine->size_for_adjusting_sp == 24) | |
526 | && (cfun->machine->callee_saved_reg_size == 0)) | |
527 | { | |
528 | emit_jump_insn (gen_returner24 ()); | |
529 | return; | |
530 | } | |
531 | ||
532 | // Set when the epilog code will also add 24 to $sp | |
533 | int epilog24 = (!must_link () | |
534 | && (cfun->machine->size_for_adjusting_sp == 24) | |
535 | && optimize_size); | |
536 | ||
537 | if (must_link ()) | |
538 | { | |
539 | emit_insn (gen_unlink ()); | |
540 | } | |
541 | else if (!epilog24 && (cfun->machine->size_for_adjusting_sp > 0)) | |
542 | { | |
543 | emit_insn (gen_addsi3 (gen_rtx_REG (SImode, FT32_SP), | |
544 | gen_rtx_REG (SImode, FT32_SP), | |
545 | GEN_INT (cfun->machine->size_for_adjusting_sp))); | |
546 | } | |
547 | ||
548 | if (cfun->machine->callee_saved_reg_size != 0) | |
549 | { | |
550 | for (regno = FIRST_PSEUDO_REGISTER; regno-- > 0;) | |
551 | { | |
552 | if (!fixed_regs[regno] && !call_used_regs[regno] | |
553 | && df_regs_ever_live_p (regno)) | |
554 | { | |
555 | rtx preg = gen_rtx_REG (Pmode, regno); | |
da2a0082 | 556 | if (optimize_size && (pretend == 0)) |
b6616c9c | 557 | { |
558 | if (epilog24) | |
559 | emit_insn (gen_jump_epilog24 (preg)); | |
560 | else | |
561 | emit_insn (gen_jump_epilog (preg)); | |
562 | return; | |
563 | } | |
564 | emit_insn (gen_movsi_pop (preg)); | |
565 | } | |
566 | } | |
567 | } | |
568 | ||
da2a0082 | 569 | if (pretend != 0) |
570 | emit_jump_insn (gen_pretend_returner (GEN_INT (pretend))); | |
571 | else | |
572 | emit_jump_insn (gen_returner ()); | |
b6616c9c | 573 | } |
574 | ||
575 | #undef TARGET_FRAME_POINTER_REQUIRED | |
576 | #define TARGET_FRAME_POINTER_REQUIRED ft32_frame_pointer_required | |
577 | static bool | |
578 | ft32_frame_pointer_required (void) | |
579 | { | |
580 | return cfun->calls_alloca; | |
581 | } | |
582 | ||
583 | #undef TARGET_CAN_ELIMINATE | |
584 | #define TARGET_CAN_ELIMINATE ft32_can_eliminate | |
585 | ||
586 | /* Return true if register FROM can be eliminated via register TO. */ | |
587 | ||
588 | static bool | |
589 | ft32_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) | |
590 | { | |
591 | return 1; | |
592 | return (to == FRAME_POINTER_REGNUM) || !ft32_frame_pointer_required (); | |
593 | } | |
594 | ||
595 | /* Implements the macro INITIAL_ELIMINATION_OFFSET, return the OFFSET. */ | |
596 | ||
597 | int | |
598 | ft32_initial_elimination_offset (int from, int to) | |
599 | { | |
600 | ft32_compute_frame (); | |
601 | ||
602 | if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) | |
603 | { | |
604 | return cfun->machine->callee_saved_reg_size + 2 * UNITS_PER_WORD; | |
605 | } | |
606 | ||
607 | if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) | |
608 | { | |
609 | int arg_offset; | |
610 | arg_offset = must_link ()? 2 : 1; | |
611 | return ((cfun->machine->callee_saved_reg_size | |
612 | + arg_offset * UNITS_PER_WORD) | |
613 | + cfun->machine->size_for_adjusting_sp); | |
614 | } | |
615 | ||
616 | if ((from == FRAME_POINTER_REGNUM) && (to == STACK_POINTER_REGNUM)) | |
617 | { | |
618 | return cfun->machine->size_for_adjusting_sp; | |
619 | } | |
620 | ||
621 | gcc_unreachable (); | |
622 | } | |
623 | ||
624 | /* Worker function for TARGET_SETUP_INCOMING_VARARGS. */ | |
625 | ||
626 | static void | |
627 | ft32_setup_incoming_varargs (cumulative_args_t cum_v, | |
582adad1 | 628 | machine_mode mode, |
da2a0082 | 629 | tree type ATTRIBUTE_UNUSED, |
630 | int *pretend_size, int no_rtl ATTRIBUTE_UNUSED) | |
b6616c9c | 631 | { |
632 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
da2a0082 | 633 | int named_size = |
634 | GET_MODE_SIZE (SImode) * (*cum - FT32_R0) + GET_MODE_SIZE (mode); | |
b6616c9c | 635 | |
da2a0082 | 636 | if (named_size < 24) |
637 | *pretend_size = 24 - named_size; | |
638 | else | |
639 | *pretend_size = 0; | |
b6616c9c | 640 | } |
641 | ||
b6616c9c | 642 | /* Return the fixed registers used for condition codes. */ |
643 | ||
644 | static bool | |
645 | ft32_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) | |
646 | { | |
647 | *p1 = CC_REG; | |
648 | *p2 = INVALID_REGNUM; | |
649 | return true; | |
650 | } | |
651 | ||
652 | /* Return the next register to be used to hold a function argument or | |
653 | NULL_RTX if there's no more space. */ | |
654 | ||
655 | static rtx | |
582adad1 | 656 | ft32_function_arg (cumulative_args_t cum_v, machine_mode mode, |
b6616c9c | 657 | const_tree type ATTRIBUTE_UNUSED, |
658 | bool named ATTRIBUTE_UNUSED) | |
659 | { | |
660 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
661 | ||
662 | if (*cum < 8) | |
663 | return gen_rtx_REG (mode, *cum); | |
664 | else | |
665 | return NULL_RTX; | |
666 | } | |
667 | ||
668 | #define FT32_FUNCTION_ARG_SIZE(MODE, TYPE) \ | |
669 | ((MODE) != BLKmode ? GET_MODE_SIZE (MODE) \ | |
670 | : (unsigned) int_size_in_bytes (TYPE)) | |
671 | ||
672 | static void | |
582adad1 | 673 | ft32_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, |
b6616c9c | 674 | const_tree type, bool named ATTRIBUTE_UNUSED) |
675 | { | |
676 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
677 | ||
678 | *cum = (*cum < FT32_R6 | |
679 | ? *cum + ((3 + FT32_FUNCTION_ARG_SIZE (mode, type)) / 4) : *cum); | |
680 | } | |
681 | ||
682 | /* Return non-zero if the function argument described by TYPE is to be | |
683 | passed by reference. */ | |
684 | ||
685 | static bool | |
686 | ft32_pass_by_reference (cumulative_args_t cum ATTRIBUTE_UNUSED, | |
582adad1 | 687 | machine_mode mode, const_tree type, |
b6616c9c | 688 | bool named ATTRIBUTE_UNUSED) |
689 | { | |
690 | unsigned HOST_WIDE_INT size; | |
691 | ||
692 | if (type) | |
693 | { | |
694 | if (AGGREGATE_TYPE_P (type)) | |
695 | return true; | |
696 | size = int_size_in_bytes (type); | |
697 | } | |
698 | else | |
699 | size = GET_MODE_SIZE (mode); | |
700 | ||
701 | return size > 4 * 6; | |
702 | } | |
703 | ||
704 | /* Some function arguments will only partially fit in the registers | |
705 | that hold arguments. Given a new arg, return the number of bytes | |
706 | that fit in argument passing registers. */ | |
707 | ||
708 | static int | |
709 | ft32_arg_partial_bytes (cumulative_args_t cum_v, | |
582adad1 | 710 | machine_mode mode, tree type, bool named) |
b6616c9c | 711 | { |
712 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
713 | int bytes_left, size; | |
714 | ||
715 | if (*cum >= 8) | |
716 | return 0; | |
717 | ||
718 | if (ft32_pass_by_reference (cum_v, mode, type, named)) | |
719 | size = 4; | |
720 | else if (type) | |
721 | { | |
722 | if (AGGREGATE_TYPE_P (type)) | |
723 | return 0; | |
724 | size = int_size_in_bytes (type); | |
725 | } | |
726 | else | |
727 | size = GET_MODE_SIZE (mode); | |
728 | ||
729 | bytes_left = (4 * 6) - ((*cum - 2) * 4); | |
730 | ||
731 | if (size > bytes_left) | |
732 | return bytes_left; | |
733 | else | |
734 | return 0; | |
735 | } | |
736 | ||
737 | /* Used by constraints.md to distinguish between GENERIC and PM | |
738 | memory addresses. */ | |
739 | ||
740 | int | |
741 | ft32_is_mem_pm (rtx o) | |
742 | { | |
c9d11eee | 743 | return (MEM_P (o) |
744 | && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (o))); | |
b6616c9c | 745 | } |
746 | ||
747 | /* The Global `targetm' Variable. */ | |
748 | ||
749 | /* Initialize the GCC target structure. */ | |
750 | ||
751 | #undef TARGET_PROMOTE_PROTOTYPES | |
752 | #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true | |
753 | ||
754 | #undef TARGET_RETURN_IN_MEMORY | |
755 | #define TARGET_RETURN_IN_MEMORY ft32_return_in_memory | |
756 | #undef TARGET_MUST_PASS_IN_STACK | |
757 | #define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size | |
758 | #undef TARGET_PASS_BY_REFERENCE | |
759 | #define TARGET_PASS_BY_REFERENCE ft32_pass_by_reference | |
760 | #undef TARGET_ARG_PARTIAL_BYTES | |
761 | #define TARGET_ARG_PARTIAL_BYTES ft32_arg_partial_bytes | |
762 | #undef TARGET_FUNCTION_ARG | |
763 | #define TARGET_FUNCTION_ARG ft32_function_arg | |
764 | #undef TARGET_FUNCTION_ARG_ADVANCE | |
765 | #define TARGET_FUNCTION_ARG_ADVANCE ft32_function_arg_advance | |
766 | ||
767 | ||
768 | #undef TARGET_SETUP_INCOMING_VARARGS | |
769 | #define TARGET_SETUP_INCOMING_VARARGS ft32_setup_incoming_varargs | |
770 | ||
771 | #undef TARGET_FIXED_CONDITION_CODE_REGS | |
772 | #define TARGET_FIXED_CONDITION_CODE_REGS ft32_fixed_condition_code_regs | |
773 | ||
774 | /* Define this to return an RTX representing the place where a | |
775 | function returns or receives a value of data type RET_TYPE, a tree | |
47ae02b7 | 776 | node representing a data type. */ |
b6616c9c | 777 | #undef TARGET_FUNCTION_VALUE |
778 | #define TARGET_FUNCTION_VALUE ft32_function_value | |
779 | #undef TARGET_LIBCALL_VALUE | |
780 | #define TARGET_LIBCALL_VALUE ft32_libcall_value | |
781 | #undef TARGET_FUNCTION_VALUE_REGNO_P | |
782 | #define TARGET_FUNCTION_VALUE_REGNO_P ft32_function_value_regno_p | |
783 | ||
784 | #undef TARGET_OPTION_OVERRIDE | |
785 | #define TARGET_OPTION_OVERRIDE ft32_option_override | |
786 | ||
787 | #undef TARGET_ASM_SELECT_SECTION | |
788 | #define TARGET_ASM_SELECT_SECTION ft32_select_section | |
789 | ||
790 | #undef TARGET_VALID_POINTER_MODE | |
791 | #define TARGET_VALID_POINTER_MODE ft32_valid_pointer_mode | |
792 | static bool | |
582adad1 | 793 | ft32_valid_pointer_mode (machine_mode mode) |
b6616c9c | 794 | { |
795 | if (mode == SImode) | |
796 | return 1; | |
797 | return 0; | |
798 | } | |
799 | ||
800 | #undef TARGET_ADDR_SPACE_POINTER_MODE | |
801 | #define TARGET_ADDR_SPACE_POINTER_MODE ft32_addr_space_pointer_mode | |
582adad1 | 802 | static machine_mode |
b6616c9c | 803 | ft32_addr_space_pointer_mode (addr_space_t addrspace ATTRIBUTE_UNUSED) |
804 | { | |
805 | return Pmode; | |
806 | } | |
807 | ||
808 | #undef TARGET_ADDR_SPACE_ADDRESS_MODE | |
809 | #define TARGET_ADDR_SPACE_ADDRESS_MODE ft32_addr_space_address_mode | |
582adad1 | 810 | static machine_mode |
b6616c9c | 811 | ft32_addr_space_address_mode (addr_space_t addrspace ATTRIBUTE_UNUSED) |
812 | { | |
813 | return Pmode; | |
814 | } | |
815 | ||
816 | #undef TARGET_ADDR_SPACE_SUBSET_P | |
817 | #define TARGET_ADDR_SPACE_SUBSET_P ft32_addr_space_subset_p | |
818 | static bool | |
819 | ft32_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED, | |
820 | addr_space_t superset ATTRIBUTE_UNUSED) | |
821 | { | |
822 | return false; | |
823 | } | |
824 | ||
825 | #undef TARGET_CASE_VALUES_THRESHOLD | |
826 | #define TARGET_CASE_VALUES_THRESHOLD ft32_target_case_values_threshold | |
827 | ||
828 | static unsigned int | |
829 | ft32_target_case_values_threshold (void) | |
830 | { | |
831 | return 4; | |
832 | } | |
833 | ||
834 | #undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P | |
835 | #define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \ | |
836 | ft32_addr_space_legitimate_address_p | |
837 | ||
838 | ||
839 | // Enabling LRA gives the infamous | |
840 | // internal compiler error: Max. number of generated reload insns per insn is achieved (90) | |
841 | // errors e.g. when compiling sieve.c | |
842 | ||
843 | static bool | |
844 | ft32_lra_p (void) | |
845 | { | |
846 | return ft32_lra_flag; | |
847 | } | |
848 | ||
849 | #undef TARGET_LRA_P | |
850 | #define TARGET_LRA_P ft32_lra_p | |
851 | ||
852 | static bool | |
853 | reg_ok_for_base_p (rtx r, bool strict) | |
854 | { | |
855 | int NUM = REGNO (r); | |
856 | if (strict) | |
857 | return (HARD_REGNO_OK_FOR_BASE_P (NUM) | |
858 | || HARD_REGNO_OK_FOR_BASE_P (reg_renumber[(NUM)])); | |
859 | else | |
860 | return ((NUM) >= FIRST_PSEUDO_REGISTER || HARD_REGNO_OK_FOR_BASE_P (NUM)); | |
861 | } | |
862 | ||
863 | static bool | |
582adad1 | 864 | ft32_addr_space_legitimate_address_p (machine_mode mode, rtx x, bool strict, |
b6616c9c | 865 | addr_space_t as ATTRIBUTE_UNUSED) |
866 | { | |
867 | if (mode != BLKmode) | |
868 | { | |
869 | if (GET_CODE (x) == PLUS) | |
870 | { | |
871 | rtx op1, op2; | |
872 | op1 = XEXP (x, 0); | |
873 | op2 = XEXP (x, 1); | |
874 | if (GET_CODE (op1) == REG | |
875 | && CONST_INT_P (op2) | |
876 | && INTVAL (op2) >= -128 | |
877 | && INTVAL (op2) < 128 && reg_ok_for_base_p (op1, strict)) | |
878 | goto yes; | |
879 | if (GET_CODE (op1) == SYMBOL_REF && CONST_INT_P (op2)) | |
880 | goto yes; | |
881 | } | |
882 | if (REG_P (x) && reg_ok_for_base_p (x, strict)) | |
883 | goto yes; | |
884 | if (GET_CODE (x) == SYMBOL_REF | |
885 | || GET_CODE (x) == LABEL_REF || CONST_INT_P (x)) | |
886 | goto yes; | |
887 | } | |
888 | else | |
889 | { | |
890 | if (REG_P (x) && reg_ok_for_base_p (x, strict)) | |
891 | goto yes; | |
892 | } | |
893 | ||
894 | return 0; | |
895 | yes: | |
896 | return 1; | |
897 | } | |
898 | ||
44c5a84b | 899 | #undef TARGET_ENCODE_SECTION_INFO |
900 | #define TARGET_ENCODE_SECTION_INFO ft32_elf_encode_section_info | |
901 | ||
902 | void | |
903 | ft32_elf_encode_section_info (tree decl, rtx rtl, int first) | |
904 | { | |
905 | enum tree_code code; | |
906 | rtx symbol; | |
907 | ||
908 | /* Careful not to prod global register variables. */ | |
909 | if (!MEM_P (rtl)) | |
910 | return; | |
911 | symbol = XEXP (rtl, 0); | |
912 | if (GET_CODE (symbol) != SYMBOL_REF) | |
913 | return; | |
914 | ||
915 | default_encode_section_info (decl, rtl, first); | |
916 | ||
917 | code = TREE_CODE (decl); | |
918 | switch (TREE_CODE_CLASS (code)) | |
919 | { | |
920 | case tcc_declaration: | |
921 | { | |
922 | tree type = TREE_TYPE (decl); | |
923 | int is_flash = (type && TYPE_P (type) | |
924 | && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))); | |
925 | if ((code == VAR_DECL) && !is_flash) | |
926 | SYMBOL_REF_FLAGS (symbol) |= 0x1000; | |
927 | } | |
928 | break; | |
929 | ||
930 | case tcc_constant: | |
931 | case tcc_exceptional: | |
932 | if (code == STRING_CST) | |
933 | SYMBOL_REF_FLAGS (symbol) |= 0x1000; | |
934 | break; | |
935 | ||
936 | default: | |
937 | break; | |
938 | } | |
939 | } | |
940 | ||
b6616c9c | 941 | struct gcc_target targetm = TARGET_INITIALIZER; |
942 | ||
943 | #include "gt-ft32.h" |