]>
Commit | Line | Data |
---|---|---|
90e7678c | 1 | /* Subroutines for insn-output.c for ATMEL AVR micro controllers |
5a7e237c | 2 | Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, |
68fd7765 | 3 | 2009, 2010, 2011 Free Software Foundation, Inc. |
92bffc14 | 4 | Contributed by Denis Chertykov (chertykov@gmail.com) |
90e7678c | 5 | |
7ec022b2 | 6 | This file is part of GCC. |
90e7678c | 7 | |
7ec022b2 | 8 | GCC is free software; you can redistribute it and/or modify |
90e7678c | 9 | it under the terms of the GNU General Public License as published by |
2f83c7d6 | 10 | the Free Software Foundation; either version 3, or (at your option) |
90e7678c DC |
11 | any later version. |
12 | ||
7ec022b2 | 13 | GCC is distributed in the hope that it will be useful, |
90e7678c DC |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
2f83c7d6 NC |
19 | along with GCC; see the file COPYING3. If not see |
20 | <http://www.gnu.org/licenses/>. */ | |
90e7678c DC |
21 | |
22 | #include "config.h" | |
b8fa3ed6 | 23 | #include "system.h" |
4977bab6 ZW |
24 | #include "coretypes.h" |
25 | #include "tm.h" | |
90e7678c DC |
26 | #include "rtl.h" |
27 | #include "regs.h" | |
28 | #include "hard-reg-set.h" | |
90e7678c DC |
29 | #include "insn-config.h" |
30 | #include "conditions.h" | |
90e7678c | 31 | #include "insn-attr.h" |
43ea6502 | 32 | #include "insn-codes.h" |
90e7678c DC |
33 | #include "flags.h" |
34 | #include "reload.h" | |
35 | #include "tree.h" | |
3eaf7a3c | 36 | #include "output.h" |
90e7678c | 37 | #include "expr.h" |
718f9c0f | 38 | #include "diagnostic-core.h" |
90e7678c DC |
39 | #include "obstack.h" |
40 | #include "function.h" | |
41 | #include "recog.h" | |
43ea6502 | 42 | #include "optabs.h" |
baac771a | 43 | #include "ggc.h" |
43ea6502 | 44 | #include "langhooks.h" |
90e7678c | 45 | #include "tm_p.h" |
672a6f42 NB |
46 | #include "target.h" |
47 | #include "target-def.h" | |
3898bf50 | 48 | #include "params.h" |
4e63b362 | 49 | #include "df.h" |
90e7678c | 50 | |
3454eb73 DC |
51 | /* Maximal allowed offset for an address in the LD command */ |
52 | #define MAX_LD_OFFSET(MODE) (64 - (signed)GET_MODE_SIZE (MODE)) | |
90e7678c | 53 | |
c5387660 | 54 | static void avr_option_override (void); |
269e3795 SB |
55 | static int avr_naked_function_p (tree); |
56 | static int interrupt_function_p (tree); | |
57 | static int signal_function_p (tree); | |
96ac4c9b | 58 | static int avr_OS_task_function_p (tree); |
9b678d96 | 59 | static int avr_OS_main_function_p (tree); |
269e3795 | 60 | static int avr_regs_to_save (HARD_REG_SET *); |
97822902 | 61 | static int get_sequence_length (rtx insns); |
269e3795 SB |
62 | static int sequent_regs_live (void); |
63 | static const char *ptrreg_to_str (int); | |
64 | static const char *cond_string (enum rtx_code); | |
3f02a5f3 | 65 | static int avr_num_arg_regs (enum machine_mode, const_tree); |
4fc2b4ff | 66 | |
269e3795 | 67 | static RTX_CODE compare_condition (rtx insn); |
506d7b68 | 68 | static rtx avr_legitimize_address (rtx, rtx, enum machine_mode); |
269e3795 SB |
69 | static int compare_sign_p (rtx insn); |
70 | static tree avr_handle_progmem_attribute (tree *, tree, tree, int, bool *); | |
71 | static tree avr_handle_fndecl_attribute (tree *, tree, tree, int, bool *); | |
44190aed | 72 | static tree avr_handle_fntype_attribute (tree *, tree, tree, int, bool *); |
269e3795 SB |
73 | static bool avr_assemble_integer (rtx, unsigned int, int); |
74 | static void avr_file_start (void); | |
75 | static void avr_file_end (void); | |
c6c3dba9 | 76 | static bool avr_legitimate_address_p (enum machine_mode, rtx, bool); |
4fc2b4ff AH |
77 | static void avr_asm_function_end_prologue (FILE *); |
78 | static void avr_asm_function_begin_epilogue (FILE *); | |
6609216e | 79 | static bool avr_cannot_modify_jumps_p (void); |
f9f6caf5 | 80 | static rtx avr_function_value (const_tree, const_tree, bool); |
68fd7765 AS |
81 | static rtx avr_libcall_value (enum machine_mode, const_rtx); |
82 | static bool avr_function_value_regno_p (const unsigned int); | |
269e3795 | 83 | static void avr_insert_attributes (tree, tree *); |
d6b5193b | 84 | static void avr_asm_init_sections (void); |
269e3795 SB |
85 | static unsigned int avr_section_type_flags (tree, const char *, int); |
86 | ||
87 | static void avr_reorg (void); | |
88 | static void avr_asm_out_ctor (rtx, int); | |
89 | static void avr_asm_out_dtor (rtx, int); | |
5f892aa0 GJL |
90 | static int avr_register_move_cost (enum machine_mode, reg_class_t, reg_class_t); |
91 | static int avr_memory_move_cost (enum machine_mode, reg_class_t, bool); | |
1f494b6d | 92 | static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code, bool); |
f40751dd JH |
93 | static bool avr_rtx_costs (rtx, int, int, int *, bool); |
94 | static int avr_address_cost (rtx, bool); | |
586de218 | 95 | static bool avr_return_in_memory (const_tree, const_tree); |
4fc2b4ff | 96 | static struct machine_function * avr_init_machine_status (void); |
43ea6502 AS |
97 | static void avr_init_builtins (void); |
98 | static rtx avr_expand_builtin (tree, rtx, rtx, enum machine_mode, int); | |
1bf29643 | 99 | static rtx avr_builtin_setjmp_frame_value (void); |
1cf0a7f1 | 100 | static bool avr_hard_regno_scratch_ok (unsigned int); |
e6ff3083 | 101 | static unsigned int avr_case_values_threshold (void); |
b52b1749 | 102 | static bool avr_frame_pointer_required_p (void); |
7b5cbb57 | 103 | static bool avr_can_eliminate (const int, const int); |
c21136ee | 104 | static bool avr_class_likely_spilled_p (reg_class_t c); |
733bdfbd NF |
105 | static rtx avr_function_arg (CUMULATIVE_ARGS *, enum machine_mode, |
106 | const_tree, bool); | |
107 | static void avr_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, | |
108 | const_tree, bool); | |
593fca4b | 109 | static void avr_help (void); |
980a0ff4 | 110 | static bool avr_function_ok_for_sibcall (tree, tree); |
516edfdd | 111 | static void avr_asm_named_section (const char *name, unsigned int flags, tree decl); |
1bf29643 | 112 | |
2c338472 | 113 | /* Allocate registers from r25 to r8 for parameters for function calls. */ |
90e7678c DC |
114 | #define FIRST_CUM_REG 26 |
115 | ||
f1c25d3b | 116 | /* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */ |
baac771a | 117 | static GTY(()) rtx tmp_reg_rtx; |
90e7678c | 118 | |
f1c25d3b | 119 | /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */ |
baac771a | 120 | static GTY(()) rtx zero_reg_rtx; |
6bec29c9 | 121 | |
90e7678c | 122 | /* AVR register names {"r0", "r1", ..., "r31"} */ |
27c38fbe | 123 | static const char *const avr_regnames[] = REGISTER_NAMES; |
90e7678c | 124 | |
92c392e6 | 125 | /* Preprocessor macros to define depending on MCU type. */ |
17fad361 | 126 | const char *avr_extra_arch_macro; |
92c392e6 | 127 | |
19298da4 AS |
128 | /* Current architecture. */ |
129 | const struct base_arch_s *avr_current_arch; | |
130 | ||
59ab92d2 AS |
131 | /* Current device. */ |
132 | const struct mcu_type_s *avr_current_device; | |
6bec29c9 | 133 | |
59ab92d2 | 134 | section *progmem_section; |
90e7678c | 135 | |
516edfdd GJL |
136 | /* To track if code will use .bss and/or .data. */ |
137 | bool avr_need_clear_bss_p = false; | |
138 | bool avr_need_copy_data_p = false; | |
139 | ||
5a82ecd9 ILT |
140 | /* AVR attributes. */ |
141 | static const struct attribute_spec avr_attribute_table[] = | |
142 | { | |
62d784f7 KT |
143 | /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, |
144 | affects_type_identity } */ | |
145 | { "progmem", 0, 0, false, false, false, avr_handle_progmem_attribute, | |
146 | false }, | |
147 | { "signal", 0, 0, true, false, false, avr_handle_fndecl_attribute, | |
148 | false }, | |
149 | { "interrupt", 0, 0, true, false, false, avr_handle_fndecl_attribute, | |
150 | false }, | |
151 | { "naked", 0, 0, false, true, true, avr_handle_fntype_attribute, | |
152 | false }, | |
153 | { "OS_task", 0, 0, false, true, true, avr_handle_fntype_attribute, | |
154 | false }, | |
155 | { "OS_main", 0, 0, false, true, true, avr_handle_fntype_attribute, | |
156 | false }, | |
157 | { NULL, 0, 0, false, false, false, NULL, false } | |
5a82ecd9 | 158 | }; |
672a6f42 NB |
159 | \f |
160 | /* Initialize the GCC target structure. */ | |
301d03af RS |
161 | #undef TARGET_ASM_ALIGNED_HI_OP |
162 | #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" | |
10252a62 BH |
163 | #undef TARGET_ASM_ALIGNED_SI_OP |
164 | #define TARGET_ASM_ALIGNED_SI_OP "\t.long\t" | |
165 | #undef TARGET_ASM_UNALIGNED_HI_OP | |
166 | #define TARGET_ASM_UNALIGNED_HI_OP "\t.word\t" | |
167 | #undef TARGET_ASM_UNALIGNED_SI_OP | |
168 | #define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t" | |
301d03af RS |
169 | #undef TARGET_ASM_INTEGER |
170 | #define TARGET_ASM_INTEGER avr_assemble_integer | |
1bc7c5b6 ZW |
171 | #undef TARGET_ASM_FILE_START |
172 | #define TARGET_ASM_FILE_START avr_file_start | |
173 | #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE | |
174 | #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true | |
a5fe455b ZW |
175 | #undef TARGET_ASM_FILE_END |
176 | #define TARGET_ASM_FILE_END avr_file_end | |
301d03af | 177 | |
4fc2b4ff AH |
178 | #undef TARGET_ASM_FUNCTION_END_PROLOGUE |
179 | #define TARGET_ASM_FUNCTION_END_PROLOGUE avr_asm_function_end_prologue | |
180 | #undef TARGET_ASM_FUNCTION_BEGIN_EPILOGUE | |
181 | #define TARGET_ASM_FUNCTION_BEGIN_EPILOGUE avr_asm_function_begin_epilogue | |
68fd7765 | 182 | |
f9f6caf5 AS |
183 | #undef TARGET_FUNCTION_VALUE |
184 | #define TARGET_FUNCTION_VALUE avr_function_value | |
68fd7765 AS |
185 | #undef TARGET_LIBCALL_VALUE |
186 | #define TARGET_LIBCALL_VALUE avr_libcall_value | |
187 | #undef TARGET_FUNCTION_VALUE_REGNO_P | |
188 | #define TARGET_FUNCTION_VALUE_REGNO_P avr_function_value_regno_p | |
189 | ||
91d231cb JM |
190 | #undef TARGET_ATTRIBUTE_TABLE |
191 | #define TARGET_ATTRIBUTE_TABLE avr_attribute_table | |
ab5c8549 JJ |
192 | #undef TARGET_ASM_FUNCTION_RODATA_SECTION |
193 | #define TARGET_ASM_FUNCTION_RODATA_SECTION default_no_function_rodata_section | |
74b66b3c RH |
194 | #undef TARGET_INSERT_ATTRIBUTES |
195 | #define TARGET_INSERT_ATTRIBUTES avr_insert_attributes | |
c16e5a35 MM |
196 | #undef TARGET_SECTION_TYPE_FLAGS |
197 | #define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags | |
516edfdd GJL |
198 | |
199 | /* `TARGET_ASM_NAMED_SECTION' must be defined in avr.h. */ | |
200 | ||
201 | #undef TARGET_ASM_INIT_SECTIONS | |
202 | #define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections | |
203 | ||
5f892aa0 GJL |
204 | #undef TARGET_REGISTER_MOVE_COST |
205 | #define TARGET_REGISTER_MOVE_COST avr_register_move_cost | |
206 | #undef TARGET_MEMORY_MOVE_COST | |
207 | #define TARGET_MEMORY_MOVE_COST avr_memory_move_cost | |
3c50106f RH |
208 | #undef TARGET_RTX_COSTS |
209 | #define TARGET_RTX_COSTS avr_rtx_costs | |
dcefdf67 RH |
210 | #undef TARGET_ADDRESS_COST |
211 | #define TARGET_ADDRESS_COST avr_address_cost | |
18dbd950 RS |
212 | #undef TARGET_MACHINE_DEPENDENT_REORG |
213 | #define TARGET_MACHINE_DEPENDENT_REORG avr_reorg | |
733bdfbd NF |
214 | #undef TARGET_FUNCTION_ARG |
215 | #define TARGET_FUNCTION_ARG avr_function_arg | |
216 | #undef TARGET_FUNCTION_ARG_ADVANCE | |
217 | #define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance | |
672a6f42 | 218 | |
506d7b68 PB |
219 | #undef TARGET_LEGITIMIZE_ADDRESS |
220 | #define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address | |
221 | ||
b069302c KH |
222 | #undef TARGET_RETURN_IN_MEMORY |
223 | #define TARGET_RETURN_IN_MEMORY avr_return_in_memory | |
224 | ||
225 | #undef TARGET_STRICT_ARGUMENT_NAMING | |
226 | #define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true | |
227 | ||
1bf29643 AH |
228 | #undef TARGET_BUILTIN_SETJMP_FRAME_VALUE |
229 | #define TARGET_BUILTIN_SETJMP_FRAME_VALUE avr_builtin_setjmp_frame_value | |
230 | ||
1cf0a7f1 AS |
231 | #undef TARGET_HARD_REGNO_SCRATCH_OK |
232 | #define TARGET_HARD_REGNO_SCRATCH_OK avr_hard_regno_scratch_ok | |
e6ff3083 AS |
233 | #undef TARGET_CASE_VALUES_THRESHOLD |
234 | #define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold | |
1cf0a7f1 | 235 | |
c6c3dba9 PB |
236 | #undef TARGET_LEGITIMATE_ADDRESS_P |
237 | #define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p | |
238 | ||
b52b1749 AS |
239 | #undef TARGET_FRAME_POINTER_REQUIRED |
240 | #define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p | |
7b5cbb57 AS |
241 | #undef TARGET_CAN_ELIMINATE |
242 | #define TARGET_CAN_ELIMINATE avr_can_eliminate | |
b52b1749 | 243 | |
c21136ee AS |
244 | #undef TARGET_CLASS_LIKELY_SPILLED_P |
245 | #define TARGET_CLASS_LIKELY_SPILLED_P avr_class_likely_spilled_p | |
246 | ||
c5387660 JM |
247 | #undef TARGET_OPTION_OVERRIDE |
248 | #define TARGET_OPTION_OVERRIDE avr_option_override | |
249 | ||
6609216e GJL |
250 | #undef TARGET_CANNOT_MODIFY_JUMPS_P |
251 | #define TARGET_CANNOT_MODIFY_JUMPS_P avr_cannot_modify_jumps_p | |
252 | ||
593fca4b GJL |
253 | #undef TARGET_HELP |
254 | #define TARGET_HELP avr_help | |
255 | ||
980a0ff4 GJL |
256 | #undef TARGET_FUNCTION_OK_FOR_SIBCALL |
257 | #define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall | |
258 | ||
43ea6502 AS |
259 | #undef TARGET_INIT_BUILTINS |
260 | #define TARGET_INIT_BUILTINS avr_init_builtins | |
261 | ||
262 | #undef TARGET_EXPAND_BUILTIN | |
263 | #define TARGET_EXPAND_BUILTIN avr_expand_builtin | |
264 | ||
265 | ||
f6897b10 | 266 | struct gcc_target targetm = TARGET_INITIALIZER; |
672a6f42 | 267 | \f |
c5387660 JM |
268 | static void |
269 | avr_option_override (void) | |
90e7678c | 270 | { |
6bec29c9 DC |
271 | const struct mcu_type_s *t; |
272 | ||
6c7ac15d AS |
273 | flag_delete_null_pointer_checks = 0; |
274 | ||
6bec29c9 DC |
275 | for (t = avr_mcu_types; t->name; t++) |
276 | if (strcmp (t->name, avr_mcu_name) == 0) | |
90e7678c | 277 | break; |
6bec29c9 DC |
278 | |
279 | if (!t->name) | |
90e7678c | 280 | { |
593fca4b GJL |
281 | error ("unrecognized argument to -mmcu= option: %qs", avr_mcu_name); |
282 | inform (input_location, "See --target-help for supported MCUs"); | |
6bec29c9 DC |
283 | } |
284 | ||
164709cf AS |
285 | avr_current_device = t; |
286 | avr_current_arch = &avr_arch_types[avr_current_device->arch]; | |
287 | avr_extra_arch_macro = avr_current_device->macro; | |
1268b05f | 288 | |
baac771a KH |
289 | tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); |
290 | zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO); | |
4fc2b4ff AH |
291 | |
292 | init_machine_status = avr_init_machine_status; | |
90e7678c DC |
293 | } |
294 | ||
593fca4b GJL |
295 | /* Implement TARGET_HELP */ |
296 | /* Report extra information for --target-help */ | |
297 | ||
298 | static void | |
299 | avr_help (void) | |
300 | { | |
301 | const struct mcu_type_s *t; | |
302 | const char * const indent = " "; | |
303 | int len; | |
304 | ||
305 | /* Give a list of MCUs that are accepted by -mmcu=* . | |
306 | Note that MCUs supported by the compiler might differ from | |
307 | MCUs supported by binutils. */ | |
308 | ||
309 | len = strlen (indent); | |
310 | printf ("Known MCU names:\n%s", indent); | |
311 | ||
312 | /* Print a blank-separated list of all supported MCUs */ | |
313 | ||
314 | for (t = avr_mcu_types; t->name; t++) | |
315 | { | |
316 | printf ("%s ", t->name); | |
317 | len += 1 + strlen (t->name); | |
318 | ||
319 | /* Break long lines */ | |
320 | ||
321 | if (len > 66 && (t+1)->name) | |
322 | { | |
323 | printf ("\n%s", indent); | |
324 | len = strlen (indent); | |
325 | } | |
326 | } | |
327 | ||
328 | printf ("\n\n"); | |
329 | } | |
330 | ||
2c338472 | 331 | /* return register class from register number. */ |
90e7678c | 332 | |
5a82ecd9 | 333 | static const enum reg_class reg_class_tab[]={ |
90e7678c DC |
334 | GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS, |
335 | GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS, | |
336 | GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS, | |
337 | GENERAL_REGS, /* r0 - r15 */ | |
338 | LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS, | |
339 | LD_REGS, /* r16 - 23 */ | |
340 | ADDW_REGS,ADDW_REGS, /* r24,r25 */ | |
341 | POINTER_X_REGS,POINTER_X_REGS, /* r26,27 */ | |
342 | POINTER_Y_REGS,POINTER_Y_REGS, /* r28,r29 */ | |
343 | POINTER_Z_REGS,POINTER_Z_REGS, /* r30,r31 */ | |
344 | STACK_REG,STACK_REG /* SPL,SPH */ | |
345 | }; | |
346 | ||
4fc2b4ff AH |
347 | /* Function to set up the backend function structure. */ |
348 | ||
349 | static struct machine_function * | |
350 | avr_init_machine_status (void) | |
351 | { | |
a9429e29 | 352 | return ggc_alloc_cleared_machine_function (); |
4fc2b4ff AH |
353 | } |
354 | ||
2c338472 | 355 | /* Return register class for register R. */ |
90e7678c DC |
356 | |
357 | enum reg_class | |
269e3795 | 358 | avr_regno_reg_class (int r) |
90e7678c DC |
359 | { |
360 | if (r <= 33) | |
361 | return reg_class_tab[r]; | |
362 | return ALL_REGS; | |
363 | } | |
364 | ||
980a0ff4 GJL |
365 | /* A helper for the subsequent function attribute used to dig for |
366 | attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */ | |
367 | ||
368 | static inline int | |
369 | avr_lookup_function_attribute1 (const_tree func, const char *name) | |
370 | { | |
371 | if (FUNCTION_DECL == TREE_CODE (func)) | |
372 | { | |
373 | if (NULL_TREE != lookup_attribute (name, DECL_ATTRIBUTES (func))) | |
374 | { | |
375 | return true; | |
376 | } | |
377 | ||
378 | func = TREE_TYPE (func); | |
379 | } | |
380 | ||
381 | gcc_assert (TREE_CODE (func) == FUNCTION_TYPE | |
382 | || TREE_CODE (func) == METHOD_TYPE); | |
383 | ||
384 | return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func)); | |
385 | } | |
386 | ||
825dda42 | 387 | /* Return nonzero if FUNC is a naked function. */ |
90e7678c DC |
388 | |
389 | static int | |
269e3795 | 390 | avr_naked_function_p (tree func) |
90e7678c | 391 | { |
980a0ff4 | 392 | return avr_lookup_function_attribute1 (func, "naked"); |
90e7678c DC |
393 | } |
394 | ||
395 | /* Return nonzero if FUNC is an interrupt function as specified | |
396 | by the "interrupt" attribute. */ | |
397 | ||
398 | static int | |
269e3795 | 399 | interrupt_function_p (tree func) |
90e7678c | 400 | { |
980a0ff4 | 401 | return avr_lookup_function_attribute1 (func, "interrupt"); |
90e7678c DC |
402 | } |
403 | ||
3454eb73 | 404 | /* Return nonzero if FUNC is a signal function as specified |
90e7678c DC |
405 | by the "signal" attribute. */ |
406 | ||
407 | static int | |
269e3795 | 408 | signal_function_p (tree func) |
90e7678c | 409 | { |
980a0ff4 | 410 | return avr_lookup_function_attribute1 (func, "signal"); |
90e7678c DC |
411 | } |
412 | ||
96ac4c9b AS |
413 | /* Return nonzero if FUNC is a OS_task function. */ |
414 | ||
415 | static int | |
416 | avr_OS_task_function_p (tree func) | |
417 | { | |
980a0ff4 | 418 | return avr_lookup_function_attribute1 (func, "OS_task"); |
96ac4c9b AS |
419 | } |
420 | ||
9b678d96 AS |
421 | /* Return nonzero if FUNC is a OS_main function. */ |
422 | ||
423 | static int | |
424 | avr_OS_main_function_p (tree func) | |
425 | { | |
980a0ff4 | 426 | return avr_lookup_function_attribute1 (func, "OS_main"); |
9b678d96 AS |
427 | } |
428 | ||
93febe68 MM |
429 | /* Return the number of hard registers to push/pop in the prologue/epilogue |
430 | of the current function, and optionally store these registers in SET. */ | |
431 | ||
432 | static int | |
269e3795 | 433 | avr_regs_to_save (HARD_REG_SET *set) |
93febe68 MM |
434 | { |
435 | int reg, count; | |
436 | int int_or_sig_p = (interrupt_function_p (current_function_decl) | |
437 | || signal_function_p (current_function_decl)); | |
4c75f709 | 438 | |
93febe68 MM |
439 | if (set) |
440 | CLEAR_HARD_REG_SET (*set); | |
441 | count = 0; | |
e52b6b63 | 442 | |
96ac4c9b | 443 | /* No need to save any registers if the function never returns or |
9b678d96 | 444 | is have "OS_task" or "OS_main" attribute. */ |
96ac4c9b | 445 | if (TREE_THIS_VOLATILE (current_function_decl) |
9b678d96 AS |
446 | || cfun->machine->is_OS_task |
447 | || cfun->machine->is_OS_main) | |
e52b6b63 MM |
448 | return 0; |
449 | ||
93febe68 MM |
450 | for (reg = 0; reg < 32; reg++) |
451 | { | |
452 | /* Do not push/pop __tmp_reg__, __zero_reg__, as well as | |
453 | any global register variables. */ | |
454 | if (fixed_regs[reg]) | |
455 | continue; | |
456 | ||
9ed74235 | 457 | if ((int_or_sig_p && !current_function_is_leaf && call_used_regs[reg]) |
6fb5fa3c | 458 | || (df_regs_ever_live_p (reg) |
93febe68 MM |
459 | && (int_or_sig_p || !call_used_regs[reg]) |
460 | && !(frame_pointer_needed | |
461 | && (reg == REG_Y || reg == (REG_Y+1))))) | |
462 | { | |
463 | if (set) | |
464 | SET_HARD_REG_BIT (*set, reg); | |
465 | count++; | |
466 | } | |
467 | } | |
468 | return count; | |
469 | } | |
470 | ||
c21ca196 AS |
471 | /* Return true if register FROM can be eliminated via register TO. */ |
472 | ||
473 | bool | |
7b5cbb57 | 474 | avr_can_eliminate (const int from, const int to) |
c21ca196 AS |
475 | { |
476 | return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) | |
477 | || ((from == FRAME_POINTER_REGNUM | |
478 | || from == FRAME_POINTER_REGNUM + 1) | |
479 | && !frame_pointer_needed)); | |
480 | } | |
481 | ||
2c338472 | 482 | /* Compute offset between arg_pointer and frame_pointer. */ |
90e7678c DC |
483 | |
484 | int | |
c21ca196 | 485 | avr_initial_elimination_offset (int from, int to) |
90e7678c | 486 | { |
3454eb73 DC |
487 | if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
488 | return 0; | |
489 | else | |
490 | { | |
93febe68 | 491 | int offset = frame_pointer_needed ? 2 : 0; |
693092fb | 492 | int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2; |
3454eb73 | 493 | |
93febe68 | 494 | offset += avr_regs_to_save (NULL); |
693092fb | 495 | return get_frame_size () + (avr_pc_size) + 1 + offset; |
90e7678c | 496 | } |
90e7678c DC |
497 | } |
498 | ||
1bf29643 AH |
499 | /* Actual start of frame is virtual_stack_vars_rtx this is offset from |
500 | frame pointer by +STARTING_FRAME_OFFSET. | |
501 | Using saved frame = virtual_stack_vars_rtx - STARTING_FRAME_OFFSET | |
502 | avoids creating add/sub of offset in nonlocal goto and setjmp. */ | |
503 | ||
504 | rtx avr_builtin_setjmp_frame_value (void) | |
505 | { | |
506 | return gen_rtx_MINUS (Pmode, virtual_stack_vars_rtx, | |
507 | gen_int_mode (STARTING_FRAME_OFFSET, Pmode)); | |
508 | } | |
509 | ||
a212a5d4 AH |
510 | /* Return contents of MEM at frame pointer + stack size + 1 (+2 if 3 byte PC). |
511 | This is return address of function. */ | |
512 | rtx | |
3f02a5f3 | 513 | avr_return_addr_rtx (int count, rtx tem) |
a212a5d4 AH |
514 | { |
515 | rtx r; | |
516 | ||
517 | /* Can only return this functions return address. Others not supported. */ | |
518 | if (count) | |
519 | return NULL; | |
520 | ||
521 | if (AVR_3_BYTE_PC) | |
522 | { | |
523 | r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+2"); | |
524 | warning (0, "'builtin_return_address' contains only 2 bytes of address"); | |
525 | } | |
526 | else | |
527 | r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+1"); | |
528 | ||
529 | r = gen_rtx_PLUS (Pmode, tem, r); | |
530 | r = gen_frame_mem (Pmode, memory_address (Pmode, r)); | |
531 | r = gen_rtx_ROTATE (HImode, r, GEN_INT (8)); | |
532 | return r; | |
533 | } | |
534 | ||
126dbce0 MM |
535 | /* Return 1 if the function epilogue is just a single "ret". */ |
536 | ||
537 | int | |
269e3795 | 538 | avr_simple_epilogue (void) |
126dbce0 MM |
539 | { |
540 | return (! frame_pointer_needed | |
541 | && get_frame_size () == 0 | |
542 | && avr_regs_to_save (NULL) == 0 | |
543 | && ! interrupt_function_p (current_function_decl) | |
544 | && ! signal_function_p (current_function_decl) | |
545 | && ! avr_naked_function_p (current_function_decl) | |
126dbce0 MM |
546 | && ! TREE_THIS_VOLATILE (current_function_decl)); |
547 | } | |
548 | ||
2c338472 | 549 | /* This function checks sequence of live registers. */ |
90e7678c DC |
550 | |
551 | static int | |
269e3795 | 552 | sequent_regs_live (void) |
90e7678c DC |
553 | { |
554 | int reg; | |
555 | int live_seq=0; | |
556 | int cur_seq=0; | |
557 | ||
558 | for (reg = 0; reg < 18; ++reg) | |
559 | { | |
560 | if (!call_used_regs[reg]) | |
561 | { | |
6fb5fa3c | 562 | if (df_regs_ever_live_p (reg)) |
90e7678c DC |
563 | { |
564 | ++live_seq; | |
565 | ++cur_seq; | |
566 | } | |
567 | else | |
568 | cur_seq = 0; | |
569 | } | |
570 | } | |
571 | ||
572 | if (!frame_pointer_needed) | |
573 | { | |
6fb5fa3c | 574 | if (df_regs_ever_live_p (REG_Y)) |
90e7678c DC |
575 | { |
576 | ++live_seq; | |
577 | ++cur_seq; | |
578 | } | |
579 | else | |
580 | cur_seq = 0; | |
581 | ||
6fb5fa3c | 582 | if (df_regs_ever_live_p (REG_Y+1)) |
90e7678c DC |
583 | { |
584 | ++live_seq; | |
585 | ++cur_seq; | |
586 | } | |
587 | else | |
588 | cur_seq = 0; | |
589 | } | |
590 | else | |
591 | { | |
592 | cur_seq += 2; | |
593 | live_seq += 2; | |
594 | } | |
595 | return (cur_seq == live_seq) ? live_seq : 0; | |
596 | } | |
597 | ||
97822902 AS |
598 | /* Obtain the length sequence of insns. */ |
599 | ||
600 | int | |
601 | get_sequence_length (rtx insns) | |
602 | { | |
603 | rtx insn; | |
604 | int length; | |
605 | ||
606 | for (insn = insns, length = 0; insn; insn = NEXT_INSN (insn)) | |
607 | length += get_attr_length (insn); | |
608 | ||
609 | return length; | |
610 | } | |
611 | ||
bdfe906f RH |
612 | /* Implement INCOMING_RETURN_ADDR_RTX. */ |
613 | ||
614 | rtx | |
615 | avr_incoming_return_addr_rtx (void) | |
616 | { | |
617 | /* The return address is at the top of the stack. Note that the push | |
618 | was via post-decrement, which means the actual address is off by one. */ | |
619 | return gen_frame_mem (HImode, plus_constant (stack_pointer_rtx, 1)); | |
620 | } | |
621 | ||
622 | /* Helper for expand_prologue. Emit a push of a byte register. */ | |
623 | ||
624 | static void | |
625 | emit_push_byte (unsigned regno, bool frame_related_p) | |
626 | { | |
627 | rtx mem, reg, insn; | |
628 | ||
629 | mem = gen_rtx_POST_DEC (HImode, stack_pointer_rtx); | |
630 | mem = gen_frame_mem (QImode, mem); | |
631 | reg = gen_rtx_REG (QImode, regno); | |
632 | ||
633 | insn = emit_insn (gen_rtx_SET (VOIDmode, mem, reg)); | |
634 | if (frame_related_p) | |
635 | RTX_FRAME_RELATED_P (insn) = 1; | |
636 | ||
637 | cfun->machine->stack_usage++; | |
638 | } | |
639 | ||
640 | ||
4fc2b4ff | 641 | /* Output function prologue. */ |
90e7678c | 642 | |
4fc2b4ff AH |
643 | void |
644 | expand_prologue (void) | |
78cf8279 | 645 | { |
4fc2b4ff | 646 | int live_seq; |
d6f77715 | 647 | HARD_REG_SET set; |
4fc2b4ff AH |
648 | int minimize; |
649 | HOST_WIDE_INT size = get_frame_size(); | |
4fc2b4ff | 650 | rtx insn; |
4fc2b4ff AH |
651 | |
652 | /* Init cfun->machine. */ | |
4fc2b4ff AH |
653 | cfun->machine->is_naked = avr_naked_function_p (current_function_decl); |
654 | cfun->machine->is_interrupt = interrupt_function_p (current_function_decl); | |
655 | cfun->machine->is_signal = signal_function_p (current_function_decl); | |
96ac4c9b | 656 | cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl); |
9b678d96 | 657 | cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl); |
a212a5d4 | 658 | cfun->machine->stack_usage = 0; |
4fc2b4ff AH |
659 | |
660 | /* Prologue: naked. */ | |
661 | if (cfun->machine->is_naked) | |
78cf8279 | 662 | { |
4fc2b4ff | 663 | return; |
78cf8279 | 664 | } |
78cf8279 | 665 | |
d6f77715 | 666 | avr_regs_to_save (&set); |
4fc2b4ff AH |
667 | live_seq = sequent_regs_live (); |
668 | minimize = (TARGET_CALL_PROLOGUES | |
0d40cc60 AS |
669 | && !cfun->machine->is_interrupt |
670 | && !cfun->machine->is_signal | |
671 | && !cfun->machine->is_OS_task | |
9b678d96 | 672 | && !cfun->machine->is_OS_main |
4fc2b4ff | 673 | && live_seq); |
78cf8279 | 674 | |
4fc2b4ff | 675 | if (cfun->machine->is_interrupt || cfun->machine->is_signal) |
78cf8279 | 676 | { |
bdfe906f | 677 | /* Enable interrupts. */ |
4fc2b4ff | 678 | if (cfun->machine->is_interrupt) |
bdfe906f | 679 | emit_insn (gen_enable_interrupt ()); |
4fc2b4ff AH |
680 | |
681 | /* Push zero reg. */ | |
bdfe906f | 682 | emit_push_byte (ZERO_REGNO, true); |
4fc2b4ff AH |
683 | |
684 | /* Push tmp reg. */ | |
bdfe906f | 685 | emit_push_byte (TMP_REGNO, true); |
4fc2b4ff AH |
686 | |
687 | /* Push SREG. */ | |
bdfe906f RH |
688 | /* ??? There's no dwarf2 column reserved for SREG. */ |
689 | emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR))); | |
690 | emit_push_byte (TMP_REGNO, false); | |
d6f77715 AS |
691 | |
692 | /* Push RAMPZ. */ | |
bdfe906f RH |
693 | /* ??? There's no dwarf2 column reserved for RAMPZ. */ |
694 | if (AVR_HAVE_RAMPZ | |
695 | && TEST_HARD_REG_BIT (set, REG_Z) | |
696 | && TEST_HARD_REG_BIT (set, REG_Z + 1)) | |
d6f77715 | 697 | { |
bdfe906f RH |
698 | emit_move_insn (tmp_reg_rtx, |
699 | gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR))); | |
700 | emit_push_byte (TMP_REGNO, false); | |
d6f77715 AS |
701 | } |
702 | ||
4fc2b4ff | 703 | /* Clear zero reg. */ |
bdfe906f | 704 | emit_move_insn (zero_reg_rtx, const0_rtx); |
78cf8279 | 705 | |
4fc2b4ff | 706 | /* Prevent any attempt to delete the setting of ZERO_REG! */ |
c41c1387 | 707 | emit_use (zero_reg_rtx); |
78cf8279 | 708 | } |
4d492420 TG |
709 | if (minimize && (frame_pointer_needed |
710 | || (AVR_2_BYTE_PC && live_seq > 6) | |
711 | || live_seq > 7)) | |
78cf8279 | 712 | { |
bdfe906f RH |
713 | int first_reg, reg, offset; |
714 | ||
715 | emit_move_insn (gen_rtx_REG (HImode, REG_X), | |
716 | gen_int_mode (size, HImode)); | |
4e63b362 | 717 | |
bdfe906f RH |
718 | insn = emit_insn (gen_call_prologue_saves |
719 | (gen_int_mode (live_seq, HImode), | |
720 | gen_int_mode (size + live_seq, HImode))); | |
4fc2b4ff | 721 | RTX_FRAME_RELATED_P (insn) = 1; |
bdfe906f RH |
722 | |
723 | /* Describe the effect of the unspec_volatile call to prologue_saves. | |
724 | Note that this formulation assumes that add_reg_note pushes the | |
725 | notes to the front. Thus we build them in the reverse order of | |
726 | how we want dwarf2out to process them. */ | |
727 | ||
728 | /* The function does always set frame_pointer_rtx, but whether that | |
729 | is going to be permanent in the function is frame_pointer_needed. */ | |
730 | add_reg_note (insn, REG_CFA_ADJUST_CFA, | |
731 | gen_rtx_SET (VOIDmode, | |
732 | (frame_pointer_needed | |
733 | ? frame_pointer_rtx : stack_pointer_rtx), | |
734 | plus_constant (stack_pointer_rtx, | |
735 | -(size + live_seq)))); | |
736 | ||
737 | /* Note that live_seq always contains r28+r29, but the other | |
738 | registers to be saved are all below 18. */ | |
739 | first_reg = 18 - (live_seq - 2); | |
740 | ||
741 | for (reg = 29, offset = -live_seq + 1; | |
742 | reg >= first_reg; | |
743 | reg = (reg == 28 ? 17 : reg - 1), ++offset) | |
744 | { | |
745 | rtx m, r; | |
746 | ||
747 | m = gen_rtx_MEM (QImode, plus_constant (stack_pointer_rtx, offset)); | |
748 | r = gen_rtx_REG (QImode, reg); | |
749 | add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r)); | |
750 | } | |
751 | ||
a212a5d4 | 752 | cfun->machine->stack_usage += size + live_seq; |
78cf8279 | 753 | } |
4fc2b4ff | 754 | else |
78cf8279 | 755 | { |
4fc2b4ff AH |
756 | int reg; |
757 | for (reg = 0; reg < 32; ++reg) | |
bdfe906f RH |
758 | if (TEST_HARD_REG_BIT (set, reg)) |
759 | emit_push_byte (reg, true); | |
760 | ||
4fc2b4ff AH |
761 | if (frame_pointer_needed) |
762 | { | |
9b678d96 | 763 | if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main)) |
0d40cc60 | 764 | { |
bdfe906f RH |
765 | /* Push frame pointer. Always be consistent about the |
766 | ordering of pushes -- epilogue_restores expects the | |
767 | register pair to be pushed low byte first. */ | |
768 | emit_push_byte (REG_Y, true); | |
769 | emit_push_byte (REG_Y + 1, true); | |
0d40cc60 AS |
770 | } |
771 | ||
4fc2b4ff AH |
772 | if (!size) |
773 | { | |
774 | insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); | |
775 | RTX_FRAME_RELATED_P (insn) = 1; | |
776 | } | |
777 | else | |
778 | { | |
779 | /* Creating a frame can be done by direct manipulation of the | |
780 | stack or via the frame pointer. These two methods are: | |
781 | fp=sp | |
782 | fp-=size | |
783 | sp=fp | |
784 | OR | |
785 | sp-=size | |
786 | fp=sp | |
787 | the optimum method depends on function type, stack and frame size. | |
788 | To avoid a complex logic, both methods are tested and shortest | |
789 | is selected. */ | |
790 | rtx myfp; | |
97822902 | 791 | rtx fp_plus_insns; |
97822902 | 792 | |
164709cf | 793 | if (AVR_HAVE_8BIT_SP) |
4fc2b4ff | 794 | { |
bdfe906f RH |
795 | /* The high byte (r29) doesn't change. Prefer 'subi' |
796 | (1 cycle) over 'sbiw' (2 cycles, same size). */ | |
797 | myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM); | |
4fc2b4ff AH |
798 | } |
799 | else | |
800 | { | |
801 | /* Normal sized addition. */ | |
802 | myfp = frame_pointer_rtx; | |
803 | } | |
97822902 AS |
804 | |
805 | /* Method 1-Adjust frame pointer. */ | |
806 | start_sequence (); | |
807 | ||
bdfe906f RH |
808 | /* Normally the dwarf2out frame-related-expr interpreter does |
809 | not expect to have the CFA change once the frame pointer is | |
810 | set up. Thus we avoid marking the move insn below and | |
811 | instead indicate that the entire operation is complete after | |
812 | the frame pointer subtraction is done. */ | |
97822902 | 813 | |
bdfe906f | 814 | emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); |
97822902 | 815 | |
bdfe906f RH |
816 | insn = emit_move_insn (myfp, plus_constant (myfp, -size)); |
817 | RTX_FRAME_RELATED_P (insn) = 1; | |
818 | add_reg_note (insn, REG_CFA_ADJUST_CFA, | |
819 | gen_rtx_SET (VOIDmode, frame_pointer_rtx, | |
820 | plus_constant (stack_pointer_rtx, | |
821 | -size))); | |
822 | ||
823 | /* Copy to stack pointer. Note that since we've already | |
824 | changed the CFA to the frame pointer this operation | |
825 | need not be annotated at all. */ | |
164709cf | 826 | if (AVR_HAVE_8BIT_SP) |
794cb45e | 827 | { |
bdfe906f | 828 | emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); |
794cb45e AS |
829 | } |
830 | else if (TARGET_NO_INTERRUPTS | |
831 | || cfun->machine->is_signal | |
832 | || cfun->machine->is_OS_main) | |
833 | { | |
bdfe906f RH |
834 | emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, |
835 | frame_pointer_rtx)); | |
794cb45e AS |
836 | } |
837 | else if (cfun->machine->is_interrupt) | |
838 | { | |
bdfe906f RH |
839 | emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, |
840 | frame_pointer_rtx)); | |
794cb45e AS |
841 | } |
842 | else | |
843 | { | |
bdfe906f | 844 | emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); |
794cb45e | 845 | } |
97822902 AS |
846 | |
847 | fp_plus_insns = get_insns (); | |
848 | end_sequence (); | |
849 | ||
4fc2b4ff | 850 | /* Method 2-Adjust Stack pointer. */ |
4fc2b4ff AH |
851 | if (size <= 6) |
852 | { | |
bdfe906f RH |
853 | rtx sp_plus_insns; |
854 | ||
97822902 AS |
855 | start_sequence (); |
856 | ||
bdfe906f RH |
857 | insn = plus_constant (stack_pointer_rtx, -size); |
858 | insn = emit_move_insn (stack_pointer_rtx, insn); | |
97822902 AS |
859 | RTX_FRAME_RELATED_P (insn) = 1; |
860 | ||
bdfe906f | 861 | insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); |
97822902 AS |
862 | RTX_FRAME_RELATED_P (insn) = 1; |
863 | ||
864 | sp_plus_insns = get_insns (); | |
865 | end_sequence (); | |
97822902 | 866 | |
bdfe906f RH |
867 | /* Use shortest method. */ |
868 | if (get_sequence_length (sp_plus_insns) | |
869 | < get_sequence_length (fp_plus_insns)) | |
870 | emit_insn (sp_plus_insns); | |
871 | else | |
872 | emit_insn (fp_plus_insns); | |
873 | } | |
874 | else | |
97822902 | 875 | emit_insn (fp_plus_insns); |
bdfe906f | 876 | |
a212a5d4 | 877 | cfun->machine->stack_usage += size; |
4fc2b4ff AH |
878 | } |
879 | } | |
78cf8279 | 880 | } |
247df3b6 | 881 | |
a11e0df4 | 882 | if (flag_stack_usage_info) |
247df3b6 | 883 | current_function_static_stack_size = cfun->machine->stack_usage; |
78cf8279 MM |
884 | } |
885 | ||
4fc2b4ff | 886 | /* Output summary at end of function prologue. */ |
90e7678c | 887 | |
08c148a8 | 888 | static void |
4fc2b4ff | 889 | avr_asm_function_end_prologue (FILE *file) |
90e7678c | 890 | { |
4fc2b4ff | 891 | if (cfun->machine->is_naked) |
90e7678c | 892 | { |
e52b6b63 | 893 | fputs ("/* prologue: naked */\n", file); |
90e7678c | 894 | } |
4fc2b4ff | 895 | else |
90e7678c | 896 | { |
4fc2b4ff AH |
897 | if (cfun->machine->is_interrupt) |
898 | { | |
899 | fputs ("/* prologue: Interrupt */\n", file); | |
900 | } | |
901 | else if (cfun->machine->is_signal) | |
902 | { | |
903 | fputs ("/* prologue: Signal */\n", file); | |
904 | } | |
90e7678c | 905 | else |
4fc2b4ff | 906 | fputs ("/* prologue: function */\n", file); |
90e7678c | 907 | } |
4fc2b4ff AH |
908 | fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n", |
909 | get_frame_size()); | |
a212a5d4 AH |
910 | fprintf (file, "/* stack size = %d */\n", |
911 | cfun->machine->stack_usage); | |
912 | /* Create symbol stack offset here so all functions have it. Add 1 to stack | |
913 | usage for offset so that SP + .L__stack_offset = return address. */ | |
914 | fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage); | |
4fc2b4ff | 915 | } |
93febe68 | 916 | |
908c8c7e | 917 | |
4fc2b4ff | 918 | /* Implement EPILOGUE_USES. */ |
e52b6b63 | 919 | |
4fc2b4ff AH |
920 | int |
921 | avr_epilogue_uses (int regno ATTRIBUTE_UNUSED) | |
922 | { | |
923 | if (reload_completed | |
924 | && cfun->machine | |
925 | && (cfun->machine->is_interrupt || cfun->machine->is_signal)) | |
926 | return 1; | |
927 | return 0; | |
90e7678c DC |
928 | } |
929 | ||
bdfe906f RH |
930 | /* Helper for expand_epilogue. Emit a pop of a byte register. */ |
931 | ||
932 | static void | |
933 | emit_pop_byte (unsigned regno) | |
934 | { | |
935 | rtx mem, reg; | |
936 | ||
937 | mem = gen_rtx_PRE_INC (HImode, stack_pointer_rtx); | |
938 | mem = gen_frame_mem (QImode, mem); | |
939 | reg = gen_rtx_REG (QImode, regno); | |
940 | ||
941 | emit_insn (gen_rtx_SET (VOIDmode, reg, mem)); | |
942 | } | |
943 | ||
4fc2b4ff | 944 | /* Output RTL epilogue. */ |
90e7678c | 945 | |
4fc2b4ff | 946 | void |
980a0ff4 | 947 | expand_epilogue (bool sibcall_p) |
90e7678c DC |
948 | { |
949 | int reg; | |
90e7678c | 950 | int live_seq; |
d6f77715 | 951 | HARD_REG_SET set; |
90e7678c | 952 | int minimize; |
4fc2b4ff | 953 | HOST_WIDE_INT size = get_frame_size(); |
4fc2b4ff AH |
954 | |
955 | /* epilogue: naked */ | |
956 | if (cfun->machine->is_naked) | |
e52b6b63 | 957 | { |
980a0ff4 GJL |
958 | gcc_assert (!sibcall_p); |
959 | ||
8819eb78 | 960 | emit_jump_insn (gen_return ()); |
4fc2b4ff | 961 | return; |
90e7678c DC |
962 | } |
963 | ||
d6f77715 | 964 | avr_regs_to_save (&set); |
90e7678c DC |
965 | live_seq = sequent_regs_live (); |
966 | minimize = (TARGET_CALL_PROLOGUES | |
0d40cc60 AS |
967 | && !cfun->machine->is_interrupt |
968 | && !cfun->machine->is_signal | |
969 | && !cfun->machine->is_OS_task | |
9b678d96 | 970 | && !cfun->machine->is_OS_main |
4fc2b4ff | 971 | && live_seq); |
90e7678c | 972 | |
96ac4c9b | 973 | if (minimize && (frame_pointer_needed || live_seq > 4)) |
90e7678c | 974 | { |
90e7678c DC |
975 | if (frame_pointer_needed) |
976 | { | |
4fc2b4ff | 977 | /* Get rid of frame. */ |
8819eb78 AS |
978 | emit_move_insn(frame_pointer_rtx, |
979 | gen_rtx_PLUS (HImode, frame_pointer_rtx, | |
980 | gen_int_mode (size, HImode))); | |
90e7678c DC |
981 | } |
982 | else | |
983 | { | |
8819eb78 | 984 | emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); |
90e7678c | 985 | } |
4fc2b4ff | 986 | |
8819eb78 | 987 | emit_insn (gen_epilogue_restores (gen_int_mode (live_seq, HImode))); |
90e7678c DC |
988 | } |
989 | else | |
990 | { | |
991 | if (frame_pointer_needed) | |
992 | { | |
993 | if (size) | |
994 | { | |
4fc2b4ff | 995 | /* Try two methods to adjust stack and select shortest. */ |
97822902 AS |
996 | rtx myfp; |
997 | rtx fp_plus_insns; | |
bdfe906f | 998 | |
164709cf | 999 | if (AVR_HAVE_8BIT_SP) |
97822902 AS |
1000 | { |
1001 | /* The high byte (r29) doesn't change - prefer 'subi' | |
1002 | (1 cycle) over 'sbiw' (2 cycles, same size). */ | |
bdfe906f | 1003 | myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM); |
97822902 AS |
1004 | } |
1005 | else | |
1006 | { | |
1007 | /* Normal sized addition. */ | |
1008 | myfp = frame_pointer_rtx; | |
1009 | } | |
1010 | ||
4fc2b4ff | 1011 | /* Method 1-Adjust frame pointer. */ |
97822902 AS |
1012 | start_sequence (); |
1013 | ||
bdfe906f | 1014 | emit_move_insn (myfp, plus_constant (myfp, size)); |
97822902 AS |
1015 | |
1016 | /* Copy to stack pointer. */ | |
164709cf | 1017 | if (AVR_HAVE_8BIT_SP) |
794cb45e AS |
1018 | { |
1019 | emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); | |
1020 | } | |
1021 | else if (TARGET_NO_INTERRUPTS | |
1022 | || cfun->machine->is_signal) | |
1023 | { | |
1024 | emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, | |
1025 | frame_pointer_rtx)); | |
1026 | } | |
1027 | else if (cfun->machine->is_interrupt) | |
1028 | { | |
1029 | emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, | |
1030 | frame_pointer_rtx)); | |
1031 | } | |
1032 | else | |
1033 | { | |
1034 | emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); | |
1035 | } | |
97822902 AS |
1036 | |
1037 | fp_plus_insns = get_insns (); | |
1038 | end_sequence (); | |
1039 | ||
4fc2b4ff | 1040 | /* Method 2-Adjust Stack pointer. */ |
4fc2b4ff AH |
1041 | if (size <= 5) |
1042 | { | |
bdfe906f RH |
1043 | rtx sp_plus_insns; |
1044 | ||
97822902 AS |
1045 | start_sequence (); |
1046 | ||
1047 | emit_move_insn (stack_pointer_rtx, | |
bdfe906f | 1048 | plus_constant (stack_pointer_rtx, size)); |
97822902 AS |
1049 | |
1050 | sp_plus_insns = get_insns (); | |
1051 | end_sequence (); | |
97822902 | 1052 | |
bdfe906f RH |
1053 | /* Use shortest method. */ |
1054 | if (get_sequence_length (sp_plus_insns) | |
1055 | < get_sequence_length (fp_plus_insns)) | |
1056 | emit_insn (sp_plus_insns); | |
1057 | else | |
1058 | emit_insn (fp_plus_insns); | |
1059 | } | |
1060 | else | |
97822902 | 1061 | emit_insn (fp_plus_insns); |
4fc2b4ff | 1062 | } |
9b678d96 | 1063 | if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main)) |
0d40cc60 | 1064 | { |
bdfe906f RH |
1065 | /* Restore previous frame_pointer. See expand_prologue for |
1066 | rationale for not using pophi. */ | |
1067 | emit_pop_byte (REG_Y + 1); | |
1068 | emit_pop_byte (REG_Y); | |
0d40cc60 | 1069 | } |
90e7678c | 1070 | } |
bdfe906f | 1071 | |
4fc2b4ff | 1072 | /* Restore used registers. */ |
90e7678c | 1073 | for (reg = 31; reg >= 0; --reg) |
bdfe906f RH |
1074 | if (TEST_HARD_REG_BIT (set, reg)) |
1075 | emit_pop_byte (reg); | |
1076 | ||
4fc2b4ff AH |
1077 | if (cfun->machine->is_interrupt || cfun->machine->is_signal) |
1078 | { | |
d6f77715 | 1079 | /* Restore RAMPZ using tmp reg as scratch. */ |
bdfe906f RH |
1080 | if (AVR_HAVE_RAMPZ |
1081 | && TEST_HARD_REG_BIT (set, REG_Z) | |
1082 | && TEST_HARD_REG_BIT (set, REG_Z + 1)) | |
d6f77715 | 1083 | { |
bdfe906f RH |
1084 | emit_pop_byte (TMP_REGNO); |
1085 | emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)), | |
d6f77715 AS |
1086 | tmp_reg_rtx); |
1087 | } | |
93febe68 | 1088 | |
4fc2b4ff | 1089 | /* Restore SREG using tmp reg as scratch. */ |
bdfe906f | 1090 | emit_pop_byte (TMP_REGNO); |
4fc2b4ff | 1091 | |
bdfe906f | 1092 | emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)), |
8819eb78 | 1093 | tmp_reg_rtx); |
4fc2b4ff AH |
1094 | |
1095 | /* Restore tmp REG. */ | |
bdfe906f | 1096 | emit_pop_byte (TMP_REGNO); |
4fc2b4ff AH |
1097 | |
1098 | /* Restore zero REG. */ | |
bdfe906f | 1099 | emit_pop_byte (ZERO_REGNO); |
4fc2b4ff | 1100 | } |
e52b6b63 | 1101 | |
980a0ff4 GJL |
1102 | if (!sibcall_p) |
1103 | emit_jump_insn (gen_return ()); | |
4fc2b4ff | 1104 | } |
90e7678c DC |
1105 | } |
1106 | ||
4fc2b4ff AH |
1107 | /* Output summary messages at beginning of function epilogue. */ |
1108 | ||
1109 | static void | |
1110 | avr_asm_function_begin_epilogue (FILE *file) | |
1111 | { | |
1112 | fprintf (file, "/* epilogue start */\n"); | |
1113 | } | |
90e7678c | 1114 | |
6609216e GJL |
1115 | |
1116 | /* Implement TARGET_CANNOT_MODITY_JUMPS_P */ | |
1117 | ||
1118 | static bool | |
1119 | avr_cannot_modify_jumps_p (void) | |
1120 | { | |
1121 | ||
1122 | /* Naked Functions must not have any instructions after | |
1123 | their epilogue, see PR42240 */ | |
1124 | ||
1125 | if (reload_completed | |
1126 | && cfun->machine | |
1127 | && cfun->machine->is_naked) | |
1128 | { | |
1129 | return true; | |
1130 | } | |
1131 | ||
1132 | return false; | |
1133 | } | |
1134 | ||
1135 | ||
90e7678c DC |
1136 | /* Return nonzero if X (an RTX) is a legitimate memory address on the target |
1137 | machine for a memory operand of mode MODE. */ | |
1138 | ||
c6c3dba9 PB |
1139 | bool |
1140 | avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) | |
90e7678c | 1141 | { |
48ccfb40 DC |
1142 | enum reg_class r = NO_REGS; |
1143 | ||
90e7678c DC |
1144 | if (TARGET_ALL_DEBUG) |
1145 | { | |
1146 | fprintf (stderr, "mode: (%s) %s %s %s %s:", | |
1147 | GET_MODE_NAME(mode), | |
1148 | strict ? "(strict)": "", | |
1149 | reload_completed ? "(reload_completed)": "", | |
1150 | reload_in_progress ? "(reload_in_progress)": "", | |
1151 | reg_renumber ? "(reg_renumber)" : ""); | |
1152 | if (GET_CODE (x) == PLUS | |
1153 | && REG_P (XEXP (x, 0)) | |
1154 | && GET_CODE (XEXP (x, 1)) == CONST_INT | |
1155 | && INTVAL (XEXP (x, 1)) >= 0 | |
3454eb73 | 1156 | && INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode) |
90e7678c DC |
1157 | && reg_renumber |
1158 | ) | |
1159 | fprintf (stderr, "(r%d ---> r%d)", REGNO (XEXP (x, 0)), | |
1160 | true_regnum (XEXP (x, 0))); | |
1161 | debug_rtx (x); | |
1162 | } | |
9215065f AH |
1163 | if (!strict && GET_CODE (x) == SUBREG) |
1164 | x = SUBREG_REG (x); | |
90e7678c DC |
1165 | if (REG_P (x) && (strict ? REG_OK_FOR_BASE_STRICT_P (x) |
1166 | : REG_OK_FOR_BASE_NOSTRICT_P (x))) | |
48ccfb40 | 1167 | r = POINTER_REGS; |
90e7678c | 1168 | else if (CONSTANT_ADDRESS_P (x)) |
48ccfb40 | 1169 | r = ALL_REGS; |
90e7678c DC |
1170 | else if (GET_CODE (x) == PLUS |
1171 | && REG_P (XEXP (x, 0)) | |
1172 | && GET_CODE (XEXP (x, 1)) == CONST_INT | |
1173 | && INTVAL (XEXP (x, 1)) >= 0) | |
1174 | { | |
3454eb73 | 1175 | int fit = INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode); |
90e7678c DC |
1176 | if (fit) |
1177 | { | |
1178 | if (! strict | |
9215065f | 1179 | || REGNO (XEXP (x,0)) == REG_X |
6bec29c9 DC |
1180 | || REGNO (XEXP (x,0)) == REG_Y |
1181 | || REGNO (XEXP (x,0)) == REG_Z) | |
48ccfb40 | 1182 | r = BASE_POINTER_REGS; |
90e7678c DC |
1183 | if (XEXP (x,0) == frame_pointer_rtx |
1184 | || XEXP (x,0) == arg_pointer_rtx) | |
48ccfb40 | 1185 | r = BASE_POINTER_REGS; |
90e7678c DC |
1186 | } |
1187 | else if (frame_pointer_needed && XEXP (x,0) == frame_pointer_rtx) | |
48ccfb40 | 1188 | r = POINTER_Y_REGS; |
90e7678c DC |
1189 | } |
1190 | else if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC) | |
1191 | && REG_P (XEXP (x, 0)) | |
1192 | && (strict ? REG_OK_FOR_BASE_STRICT_P (XEXP (x, 0)) | |
1193 | : REG_OK_FOR_BASE_NOSTRICT_P (XEXP (x, 0)))) | |
1194 | { | |
48ccfb40 | 1195 | r = POINTER_REGS; |
90e7678c DC |
1196 | } |
1197 | if (TARGET_ALL_DEBUG) | |
1198 | { | |
89a0b917 | 1199 | fprintf (stderr, " ret = %c\n", r + '0'); |
90e7678c | 1200 | } |
48ccfb40 | 1201 | return r == NO_REGS ? 0 : (int)r; |
90e7678c DC |
1202 | } |
1203 | ||
1204 | /* Attempts to replace X with a valid | |
1205 | memory address for an operand of mode MODE */ | |
1206 | ||
1207 | rtx | |
506d7b68 | 1208 | avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode) |
90e7678c DC |
1209 | { |
1210 | x = oldx; | |
1211 | if (TARGET_ALL_DEBUG) | |
1212 | { | |
1213 | fprintf (stderr, "legitimize_address mode: %s", GET_MODE_NAME(mode)); | |
1214 | debug_rtx (oldx); | |
1215 | } | |
1216 | ||
1217 | if (GET_CODE (oldx) == PLUS | |
1218 | && REG_P (XEXP (oldx,0))) | |
1219 | { | |
1220 | if (REG_P (XEXP (oldx,1))) | |
1221 | x = force_reg (GET_MODE (oldx), oldx); | |
1222 | else if (GET_CODE (XEXP (oldx, 1)) == CONST_INT) | |
1223 | { | |
1224 | int offs = INTVAL (XEXP (oldx,1)); | |
1225 | if (frame_pointer_rtx != XEXP (oldx,0)) | |
3454eb73 | 1226 | if (offs > MAX_LD_OFFSET (mode)) |
90e7678c DC |
1227 | { |
1228 | if (TARGET_ALL_DEBUG) | |
1229 | fprintf (stderr, "force_reg (big offset)\n"); | |
1230 | x = force_reg (GET_MODE (oldx), oldx); | |
1231 | } | |
1232 | } | |
1233 | } | |
1234 | return x; | |
1235 | } | |
1236 | ||
1237 | ||
2c338472 | 1238 | /* Return a pointer register name as a string. */ |
90e7678c | 1239 | |
5fecfd8d | 1240 | static const char * |
269e3795 | 1241 | ptrreg_to_str (int regno) |
90e7678c DC |
1242 | { |
1243 | switch (regno) | |
1244 | { | |
1245 | case REG_X: return "X"; | |
1246 | case REG_Y: return "Y"; | |
1247 | case REG_Z: return "Z"; | |
1248 | default: | |
3639b3fa | 1249 | output_operand_lossage ("address operand requires constraint for X, Y, or Z register"); |
90e7678c DC |
1250 | } |
1251 | return NULL; | |
1252 | } | |
1253 | ||
1254 | /* Return the condition name as a string. | |
1255 | Used in conditional jump constructing */ | |
1256 | ||
5fecfd8d | 1257 | static const char * |
269e3795 | 1258 | cond_string (enum rtx_code code) |
90e7678c DC |
1259 | { |
1260 | switch (code) | |
1261 | { | |
1262 | case NE: | |
1263 | return "ne"; | |
1264 | case EQ: | |
1265 | return "eq"; | |
1266 | case GE: | |
1267 | if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) | |
1268 | return "pl"; | |
1269 | else | |
1270 | return "ge"; | |
90e7678c DC |
1271 | case LT: |
1272 | if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) | |
1273 | return "mi"; | |
1274 | else | |
1275 | return "lt"; | |
1276 | case GEU: | |
1277 | return "sh"; | |
90e7678c DC |
1278 | case LTU: |
1279 | return "lo"; | |
1280 | default: | |
25b9575b | 1281 | gcc_unreachable (); |
90e7678c DC |
1282 | } |
1283 | } | |
1284 | ||
2c338472 | 1285 | /* Output ADDR to FILE as address. */ |
90e7678c DC |
1286 | |
1287 | void | |
269e3795 | 1288 | print_operand_address (FILE *file, rtx addr) |
90e7678c DC |
1289 | { |
1290 | switch (GET_CODE (addr)) | |
1291 | { | |
1292 | case REG: | |
1293 | fprintf (file, ptrreg_to_str (REGNO (addr))); | |
1294 | break; | |
1295 | ||
1296 | case PRE_DEC: | |
1297 | fprintf (file, "-%s", ptrreg_to_str (REGNO (XEXP (addr, 0)))); | |
1298 | break; | |
1299 | ||
1300 | case POST_INC: | |
1301 | fprintf (file, "%s+", ptrreg_to_str (REGNO (XEXP (addr, 0)))); | |
1302 | break; | |
1303 | ||
1304 | default: | |
1305 | if (CONSTANT_ADDRESS_P (addr) | |
846428f1 | 1306 | && text_segment_operand (addr, VOIDmode)) |
90e7678c | 1307 | { |
f64c36ba EB |
1308 | rtx x = addr; |
1309 | if (GET_CODE (x) == CONST) | |
1310 | x = XEXP (x, 0); | |
846428f1 AH |
1311 | if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x,1)) == CONST_INT) |
1312 | { | |
1313 | /* Assembler gs() will implant word address. Make offset | |
1314 | a byte offset inside gs() for assembler. This is | |
1315 | needed because the more logical (constant+gs(sym)) is not | |
1316 | accepted by gas. For 128K and lower devices this is ok. For | |
1317 | large devices it will create a Trampoline to offset from symbol | |
1318 | which may not be what the user really wanted. */ | |
1319 | fprintf (file, "gs("); | |
1320 | output_addr_const (file, XEXP (x,0)); | |
1321 | fprintf (file,"+" HOST_WIDE_INT_PRINT_DEC ")", 2 * INTVAL (XEXP (x,1))); | |
1322 | if (AVR_3_BYTE_PC) | |
d8a07487 | 1323 | if (warning (0, "pointer offset from symbol maybe incorrect")) |
846428f1 AH |
1324 | { |
1325 | output_addr_const (stderr, addr); | |
1326 | fprintf(stderr,"\n"); | |
1327 | } | |
1328 | } | |
1329 | else | |
1330 | { | |
1331 | fprintf (file, "gs("); | |
1332 | output_addr_const (file, addr); | |
1333 | fprintf (file, ")"); | |
1334 | } | |
90e7678c DC |
1335 | } |
1336 | else | |
1337 | output_addr_const (file, addr); | |
1338 | } | |
1339 | } | |
1340 | ||
1341 | ||
2c338472 | 1342 | /* Output X as assembler operand to file FILE. */ |
90e7678c DC |
1343 | |
1344 | void | |
269e3795 | 1345 | print_operand (FILE *file, rtx x, int code) |
90e7678c DC |
1346 | { |
1347 | int abcd = 0; | |
1348 | ||
1349 | if (code >= 'A' && code <= 'D') | |
1350 | abcd = code - 'A'; | |
1351 | ||
cf14485b MM |
1352 | if (code == '~') |
1353 | { | |
5dc77808 | 1354 | if (!AVR_HAVE_JMP_CALL) |
cf14485b MM |
1355 | fputc ('r', file); |
1356 | } | |
693092fb BH |
1357 | else if (code == '!') |
1358 | { | |
1359 | if (AVR_HAVE_EIJMP_EICALL) | |
1360 | fputc ('e', file); | |
1361 | } | |
cf14485b | 1362 | else if (REG_P (x)) |
90e7678c DC |
1363 | { |
1364 | if (x == zero_reg_rtx) | |
cf14485b | 1365 | fprintf (file, "__zero_reg__"); |
90e7678c DC |
1366 | else |
1367 | fprintf (file, reg_names[true_regnum (x) + abcd]); | |
1368 | } | |
1369 | else if (GET_CODE (x) == CONST_INT) | |
7e53359d | 1370 | fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) + abcd); |
90e7678c DC |
1371 | else if (GET_CODE (x) == MEM) |
1372 | { | |
1373 | rtx addr = XEXP (x,0); | |
846428f1 | 1374 | if (code == 'm') |
90e7678c | 1375 | { |
846428f1 AH |
1376 | if (!CONSTANT_P (addr)) |
1377 | fatal_insn ("bad address, not a constant):", addr); | |
1378 | /* Assembler template with m-code is data - not progmem section */ | |
1379 | if (text_segment_operand (addr, VOIDmode)) | |
1380 | if (warning ( 0, "accessing data memory with program memory address")) | |
1381 | { | |
1382 | output_addr_const (stderr, addr); | |
1383 | fprintf(stderr,"\n"); | |
1384 | } | |
1385 | output_addr_const (file, addr); | |
90e7678c | 1386 | } |
cf14485b MM |
1387 | else if (code == 'o') |
1388 | { | |
1389 | if (GET_CODE (addr) != PLUS) | |
c725bd79 | 1390 | fatal_insn ("bad address, not (reg+disp):", addr); |
cf14485b MM |
1391 | |
1392 | print_operand (file, XEXP (addr, 1), 0); | |
1393 | } | |
a6990185 MM |
1394 | else if (code == 'p' || code == 'r') |
1395 | { | |
1396 | if (GET_CODE (addr) != POST_INC && GET_CODE (addr) != PRE_DEC) | |
1397 | fatal_insn ("bad address, not post_inc or pre_dec:", addr); | |
1398 | ||
1399 | if (code == 'p') | |
1400 | print_operand_address (file, XEXP (addr, 0)); /* X, Y, Z */ | |
1401 | else | |
1402 | print_operand (file, XEXP (addr, 0), 0); /* r26, r28, r30 */ | |
1403 | } | |
90e7678c DC |
1404 | else if (GET_CODE (addr) == PLUS) |
1405 | { | |
1406 | print_operand_address (file, XEXP (addr,0)); | |
1407 | if (REGNO (XEXP (addr, 0)) == REG_X) | |
c725bd79 | 1408 | fatal_insn ("internal compiler error. Bad address:" |
90e7678c DC |
1409 | ,addr); |
1410 | fputc ('+', file); | |
1411 | print_operand (file, XEXP (addr,1), code); | |
1412 | } | |
1413 | else | |
1414 | print_operand_address (file, addr); | |
1415 | } | |
846428f1 AH |
1416 | else if (code == 'x') |
1417 | { | |
1418 | /* Constant progmem address - like used in jmp or call */ | |
1419 | if (0 == text_segment_operand (x, VOIDmode)) | |
1420 | if (warning ( 0, "accessing program memory with data memory address")) | |
1421 | { | |
1422 | output_addr_const (stderr, x); | |
1423 | fprintf(stderr,"\n"); | |
1424 | } | |
1425 | /* Use normal symbol for direct address no linker trampoline needed */ | |
1426 | output_addr_const (file, x); | |
1427 | } | |
90e7678c DC |
1428 | else if (GET_CODE (x) == CONST_DOUBLE) |
1429 | { | |
1430 | long val; | |
1431 | REAL_VALUE_TYPE rv; | |
1432 | if (GET_MODE (x) != SFmode) | |
c725bd79 | 1433 | fatal_insn ("internal compiler error. Unknown mode:", x); |
90e7678c DC |
1434 | REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
1435 | REAL_VALUE_TO_TARGET_SINGLE (rv, val); | |
761c70aa | 1436 | fprintf (file, "0x%lx", val); |
90e7678c DC |
1437 | } |
1438 | else if (code == 'j') | |
761c70aa | 1439 | fputs (cond_string (GET_CODE (x)), file); |
90e7678c | 1440 | else if (code == 'k') |
761c70aa | 1441 | fputs (cond_string (reverse_condition (GET_CODE (x))), file); |
90e7678c | 1442 | else |
cdd9eb8f | 1443 | print_operand_address (file, x); |
90e7678c DC |
1444 | } |
1445 | ||
90e7678c DC |
1446 | /* Update the condition code in the INSN. */ |
1447 | ||
1448 | void | |
269e3795 | 1449 | notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn) |
90e7678c | 1450 | { |
3454eb73 DC |
1451 | rtx set; |
1452 | ||
90e7678c DC |
1453 | switch (get_attr_cc (insn)) |
1454 | { | |
1455 | case CC_NONE: | |
1456 | /* Insn does not affect CC at all. */ | |
1457 | break; | |
1458 | ||
1459 | case CC_SET_N: | |
1460 | CC_STATUS_INIT; | |
1461 | break; | |
1462 | ||
1463 | case CC_SET_ZN: | |
3454eb73 DC |
1464 | set = single_set (insn); |
1465 | CC_STATUS_INIT; | |
1466 | if (set) | |
1467 | { | |
1468 | cc_status.flags |= CC_NO_OVERFLOW; | |
1469 | cc_status.value1 = SET_DEST (set); | |
1470 | } | |
90e7678c DC |
1471 | break; |
1472 | ||
1473 | case CC_SET_CZN: | |
1474 | /* Insn sets the Z,N,C flags of CC to recog_operand[0]. | |
1475 | The V flag may or may not be known but that's ok because | |
1476 | alter_cond will change tests to use EQ/NE. */ | |
3454eb73 DC |
1477 | set = single_set (insn); |
1478 | CC_STATUS_INIT; | |
1479 | if (set) | |
1480 | { | |
1481 | cc_status.value1 = SET_DEST (set); | |
1482 | cc_status.flags |= CC_OVERFLOW_UNUSABLE; | |
1483 | } | |
90e7678c DC |
1484 | break; |
1485 | ||
1486 | case CC_COMPARE: | |
3454eb73 DC |
1487 | set = single_set (insn); |
1488 | CC_STATUS_INIT; | |
1489 | if (set) | |
1490 | cc_status.value1 = SET_SRC (set); | |
90e7678c | 1491 | break; |
3454eb73 | 1492 | |
90e7678c DC |
1493 | case CC_CLOBBER: |
1494 | /* Insn doesn't leave CC in a usable state. */ | |
1495 | CC_STATUS_INIT; | |
3454eb73 DC |
1496 | |
1497 | /* Correct CC for the ashrqi3 with the shift count as CONST_INT != 6 */ | |
1498 | set = single_set (insn); | |
1499 | if (set) | |
1500 | { | |
1501 | rtx src = SET_SRC (set); | |
1502 | ||
1503 | if (GET_CODE (src) == ASHIFTRT | |
1504 | && GET_MODE (src) == QImode) | |
1505 | { | |
1506 | rtx x = XEXP (src, 1); | |
1507 | ||
1508 | if (GET_CODE (x) == CONST_INT | |
a3cf5992 | 1509 | && INTVAL (x) > 0 |
3454eb73 DC |
1510 | && INTVAL (x) != 6) |
1511 | { | |
1512 | cc_status.value1 = SET_DEST (set); | |
1513 | cc_status.flags |= CC_OVERFLOW_UNUSABLE; | |
1514 | } | |
1515 | } | |
1516 | } | |
90e7678c DC |
1517 | break; |
1518 | } | |
1519 | } | |
1520 | ||
1521 | /* Return maximum number of consecutive registers of | |
1522 | class CLASS needed to hold a value of mode MODE. */ | |
1523 | ||
1524 | int | |
0a2aaacc | 1525 | class_max_nregs (enum reg_class rclass ATTRIBUTE_UNUSED,enum machine_mode mode) |
90e7678c DC |
1526 | { |
1527 | return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD); | |
1528 | } | |
1529 | ||
1530 | /* Choose mode for jump insn: | |
1531 | 1 - relative jump in range -63 <= x <= 62 ; | |
1532 | 2 - relative jump in range -2046 <= x <= 2045 ; | |
1533 | 3 - absolute jump (only for ATmega[16]03). */ | |
1534 | ||
1535 | int | |
269e3795 | 1536 | avr_jump_mode (rtx x, rtx insn) |
90e7678c | 1537 | { |
5a82ecd9 | 1538 | int dest_addr = INSN_ADDRESSES (INSN_UID (GET_CODE (x) == LABEL_REF |
9d98a694 AO |
1539 | ? XEXP (x, 0) : x)); |
1540 | int cur_addr = INSN_ADDRESSES (INSN_UID (insn)); | |
90e7678c DC |
1541 | int jump_distance = cur_addr - dest_addr; |
1542 | ||
1543 | if (-63 <= jump_distance && jump_distance <= 62) | |
1544 | return 1; | |
1545 | else if (-2046 <= jump_distance && jump_distance <= 2045) | |
1546 | return 2; | |
5dc77808 | 1547 | else if (AVR_HAVE_JMP_CALL) |
90e7678c DC |
1548 | return 3; |
1549 | ||
1550 | return 2; | |
1551 | } | |
1552 | ||
56b871c1 DC |
1553 | /* return an AVR condition jump commands. |
1554 | X is a comparison RTX. | |
1555 | LEN is a number returned by avr_jump_mode function. | |
1556 | if REVERSE nonzero then condition code in X must be reversed. */ | |
90e7678c | 1557 | |
5fecfd8d | 1558 | const char * |
269e3795 | 1559 | ret_cond_branch (rtx x, int len, int reverse) |
90e7678c | 1560 | { |
56b871c1 DC |
1561 | RTX_CODE cond = reverse ? reverse_condition (GET_CODE (x)) : GET_CODE (x); |
1562 | ||
90e7678c DC |
1563 | switch (cond) |
1564 | { | |
1565 | case GT: | |
1566 | if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) | |
13e8651c | 1567 | return (len == 1 ? (AS1 (breq,.+2) CR_TAB |
90e7678c | 1568 | AS1 (brpl,%0)) : |
13e8651c TR |
1569 | len == 2 ? (AS1 (breq,.+4) CR_TAB |
1570 | AS1 (brmi,.+2) CR_TAB | |
90e7678c | 1571 | AS1 (rjmp,%0)) : |
13e8651c TR |
1572 | (AS1 (breq,.+6) CR_TAB |
1573 | AS1 (brmi,.+4) CR_TAB | |
90e7678c DC |
1574 | AS1 (jmp,%0))); |
1575 | ||
1576 | else | |
13e8651c | 1577 | return (len == 1 ? (AS1 (breq,.+2) CR_TAB |
90e7678c | 1578 | AS1 (brge,%0)) : |
13e8651c TR |
1579 | len == 2 ? (AS1 (breq,.+4) CR_TAB |
1580 | AS1 (brlt,.+2) CR_TAB | |
90e7678c | 1581 | AS1 (rjmp,%0)) : |
13e8651c TR |
1582 | (AS1 (breq,.+6) CR_TAB |
1583 | AS1 (brlt,.+4) CR_TAB | |
90e7678c DC |
1584 | AS1 (jmp,%0))); |
1585 | case GTU: | |
13e8651c | 1586 | return (len == 1 ? (AS1 (breq,.+2) CR_TAB |
90e7678c | 1587 | AS1 (brsh,%0)) : |
13e8651c TR |
1588 | len == 2 ? (AS1 (breq,.+4) CR_TAB |
1589 | AS1 (brlo,.+2) CR_TAB | |
90e7678c | 1590 | AS1 (rjmp,%0)) : |
13e8651c TR |
1591 | (AS1 (breq,.+6) CR_TAB |
1592 | AS1 (brlo,.+4) CR_TAB | |
90e7678c DC |
1593 | AS1 (jmp,%0))); |
1594 | case LE: | |
1595 | if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) | |
1596 | return (len == 1 ? (AS1 (breq,%0) CR_TAB | |
1597 | AS1 (brmi,%0)) : | |
13e8651c TR |
1598 | len == 2 ? (AS1 (breq,.+2) CR_TAB |
1599 | AS1 (brpl,.+2) CR_TAB | |
90e7678c | 1600 | AS1 (rjmp,%0)) : |
13e8651c TR |
1601 | (AS1 (breq,.+2) CR_TAB |
1602 | AS1 (brpl,.+4) CR_TAB | |
90e7678c DC |
1603 | AS1 (jmp,%0))); |
1604 | else | |
1605 | return (len == 1 ? (AS1 (breq,%0) CR_TAB | |
1606 | AS1 (brlt,%0)) : | |
13e8651c TR |
1607 | len == 2 ? (AS1 (breq,.+2) CR_TAB |
1608 | AS1 (brge,.+2) CR_TAB | |
90e7678c | 1609 | AS1 (rjmp,%0)) : |
13e8651c TR |
1610 | (AS1 (breq,.+2) CR_TAB |
1611 | AS1 (brge,.+4) CR_TAB | |
90e7678c DC |
1612 | AS1 (jmp,%0))); |
1613 | case LEU: | |
1614 | return (len == 1 ? (AS1 (breq,%0) CR_TAB | |
1615 | AS1 (brlo,%0)) : | |
13e8651c TR |
1616 | len == 2 ? (AS1 (breq,.+2) CR_TAB |
1617 | AS1 (brsh,.+2) CR_TAB | |
90e7678c | 1618 | AS1 (rjmp,%0)) : |
13e8651c TR |
1619 | (AS1 (breq,.+2) CR_TAB |
1620 | AS1 (brsh,.+4) CR_TAB | |
90e7678c DC |
1621 | AS1 (jmp,%0))); |
1622 | default: | |
56b871c1 DC |
1623 | if (reverse) |
1624 | { | |
1625 | switch (len) | |
1626 | { | |
1627 | case 1: | |
1628 | return AS1 (br%k1,%0); | |
1629 | case 2: | |
13e8651c | 1630 | return (AS1 (br%j1,.+2) CR_TAB |
56b871c1 DC |
1631 | AS1 (rjmp,%0)); |
1632 | default: | |
13e8651c | 1633 | return (AS1 (br%j1,.+4) CR_TAB |
56b871c1 DC |
1634 | AS1 (jmp,%0)); |
1635 | } | |
1636 | } | |
1637 | else | |
1638 | { | |
1639 | switch (len) | |
1640 | { | |
1641 | case 1: | |
1642 | return AS1 (br%j1,%0); | |
1643 | case 2: | |
13e8651c | 1644 | return (AS1 (br%k1,.+2) CR_TAB |
56b871c1 DC |
1645 | AS1 (rjmp,%0)); |
1646 | default: | |
13e8651c | 1647 | return (AS1 (br%k1,.+4) CR_TAB |
56b871c1 DC |
1648 | AS1 (jmp,%0)); |
1649 | } | |
1650 | } | |
90e7678c DC |
1651 | } |
1652 | return ""; | |
1653 | } | |
1654 | ||
1655 | /* Predicate function for immediate operand which fits to byte (8bit) */ | |
1656 | ||
1657 | int | |
269e3795 | 1658 | byte_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) |
90e7678c DC |
1659 | { |
1660 | return (GET_CODE (op) == CONST_INT | |
1661 | && INTVAL (op) <= 0xff && INTVAL (op) >= 0); | |
1662 | } | |
1663 | ||
007a178c | 1664 | /* Output insn cost for next insn. */ |
90e7678c DC |
1665 | |
1666 | void | |
269e3795 SB |
1667 | final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED, |
1668 | int num_operands ATTRIBUTE_UNUSED) | |
90e7678c | 1669 | { |
007a178c | 1670 | if (TARGET_ALL_DEBUG) |
90e7678c | 1671 | { |
007a178c | 1672 | fprintf (asm_out_file, "/* DEBUG: cost = %d. */\n", |
f40751dd | 1673 | rtx_cost (PATTERN (insn), INSN, !optimize_size)); |
90e7678c | 1674 | } |
90e7678c DC |
1675 | } |
1676 | ||
5fecfd8d | 1677 | /* Return 0 if undefined, 1 if always true or always false. */ |
90e7678c DC |
1678 | |
1679 | int | |
0a2aaacc | 1680 | avr_simplify_comparison_p (enum machine_mode mode, RTX_CODE op, rtx x) |
90e7678c DC |
1681 | { |
1682 | unsigned int max = (mode == QImode ? 0xff : | |
1683 | mode == HImode ? 0xffff : | |
11f9ed1a | 1684 | mode == SImode ? 0xffffffff : 0); |
0a2aaacc | 1685 | if (max && op && GET_CODE (x) == CONST_INT) |
90e7678c | 1686 | { |
0a2aaacc | 1687 | if (unsigned_condition (op) != op) |
90e7678c DC |
1688 | max >>= 1; |
1689 | ||
1690 | if (max != (INTVAL (x) & max) | |
1691 | && INTVAL (x) != 0xff) | |
1692 | return 1; | |
1693 | } | |
1694 | return 0; | |
1695 | } | |
1696 | ||
1697 | ||
1698 | /* Returns nonzero if REGNO is the number of a hard | |
1699 | register in which function arguments are sometimes passed. */ | |
1700 | ||
1701 | int | |
269e3795 | 1702 | function_arg_regno_p(int r) |
90e7678c DC |
1703 | { |
1704 | return (r >= 8 && r <= 25); | |
1705 | } | |
1706 | ||
1707 | /* Initializing the variable cum for the state at the beginning | |
1708 | of the argument list. */ | |
1709 | ||
1710 | void | |
269e3795 SB |
1711 | init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname, |
1712 | tree fndecl ATTRIBUTE_UNUSED) | |
90e7678c DC |
1713 | { |
1714 | cum->nregs = 18; | |
1715 | cum->regno = FIRST_CUM_REG; | |
f38958e8 NF |
1716 | if (!libname && stdarg_p (fntype)) |
1717 | cum->nregs = 0; | |
980a0ff4 GJL |
1718 | |
1719 | /* Assume the calle may be tail called */ | |
1720 | ||
1721 | cfun->machine->sibcall_fails = 0; | |
90e7678c DC |
1722 | } |
1723 | ||
c4984bad MM |
1724 | /* Returns the number of registers to allocate for a function argument. */ |
1725 | ||
1726 | static int | |
3f02a5f3 | 1727 | avr_num_arg_regs (enum machine_mode mode, const_tree type) |
c4984bad MM |
1728 | { |
1729 | int size; | |
1730 | ||
1731 | if (mode == BLKmode) | |
1732 | size = int_size_in_bytes (type); | |
1733 | else | |
1734 | size = GET_MODE_SIZE (mode); | |
1735 | ||
afee2a52 MM |
1736 | /* Align all function arguments to start in even-numbered registers. |
1737 | Odd-sized arguments leave holes above them. */ | |
c4984bad | 1738 | |
afee2a52 | 1739 | return (size + 1) & ~1; |
c4984bad MM |
1740 | } |
1741 | ||
90e7678c | 1742 | /* Controls whether a function argument is passed |
2c338472 | 1743 | in a register, and which register. */ |
90e7678c | 1744 | |
733bdfbd NF |
1745 | static rtx |
1746 | avr_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, | |
1747 | const_tree type, bool named ATTRIBUTE_UNUSED) | |
90e7678c | 1748 | { |
c4984bad | 1749 | int bytes = avr_num_arg_regs (mode, type); |
90e7678c DC |
1750 | |
1751 | if (cum->nregs && bytes <= cum->nregs) | |
f1c25d3b | 1752 | return gen_rtx_REG (mode, cum->regno - bytes); |
c4984bad | 1753 | |
90e7678c DC |
1754 | return NULL_RTX; |
1755 | } | |
1756 | ||
1757 | /* Update the summarizer variable CUM to advance past an argument | |
1758 | in the argument list. */ | |
1759 | ||
733bdfbd NF |
1760 | static void |
1761 | avr_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, | |
1762 | const_tree type, bool named ATTRIBUTE_UNUSED) | |
90e7678c | 1763 | { |
c4984bad | 1764 | int bytes = avr_num_arg_regs (mode, type); |
90e7678c | 1765 | |
90e7678c DC |
1766 | cum->nregs -= bytes; |
1767 | cum->regno -= bytes; | |
1768 | ||
980a0ff4 GJL |
1769 | /* A parameter is being passed in a call-saved register. As the original |
1770 | contents of these regs has to be restored before leaving the function, | |
1771 | a function must not pass arguments in call-saved regs in order to get | |
1772 | tail-called. */ | |
1773 | ||
3f5ea9dc GJL |
1774 | if (cum->regno >= 8 |
1775 | && cum->nregs >= 0 | |
980a0ff4 GJL |
1776 | && !call_used_regs[cum->regno]) |
1777 | { | |
1778 | /* FIXME: We ship info on failing tail-call in struct machine_function. | |
1779 | This uses internals of calls.c:expand_call() and the way args_so_far | |
1780 | is used. targetm.function_ok_for_sibcall() needs to be extended to | |
1781 | pass &args_so_far, too. At present, CUMULATIVE_ARGS is target | |
1782 | dependent so that such an extension is not wanted. */ | |
1783 | ||
1784 | cfun->machine->sibcall_fails = 1; | |
1785 | } | |
1786 | ||
f24a5190 GJL |
1787 | /* Test if all registers needed by the ABI are actually available. If the |
1788 | user has fixed a GPR needed to pass an argument, an (implicit) function | |
1789 | call would clobber that fixed register. See PR45099 for an example. */ | |
1790 | ||
3f5ea9dc GJL |
1791 | if (cum->regno >= 8 |
1792 | && cum->nregs >= 0) | |
f24a5190 GJL |
1793 | { |
1794 | int regno; | |
1795 | ||
1796 | for (regno = cum->regno; regno < cum->regno + bytes; regno++) | |
1797 | if (fixed_regs[regno]) | |
1798 | error ("Register %s is needed to pass a parameter but is fixed", | |
1799 | reg_names[regno]); | |
1800 | } | |
1801 | ||
90e7678c DC |
1802 | if (cum->nregs <= 0) |
1803 | { | |
1804 | cum->nregs = 0; | |
1805 | cum->regno = FIRST_CUM_REG; | |
1806 | } | |
90e7678c DC |
1807 | } |
1808 | ||
980a0ff4 GJL |
1809 | /* Implement `TARGET_FUNCTION_OK_FOR_SIBCALL' */ |
1810 | /* Decide whether we can make a sibling call to a function. DECL is the | |
1811 | declaration of the function being targeted by the call and EXP is the | |
1812 | CALL_EXPR representing the call. */ | |
1813 | ||
1814 | static bool | |
1815 | avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee) | |
1816 | { | |
1817 | tree fntype_callee; | |
1818 | ||
1819 | /* Tail-calling must fail if callee-saved regs are used to pass | |
1820 | function args. We must not tail-call when `epilogue_restores' | |
1821 | is used. Unfortunately, we cannot tell at this point if that | |
1822 | actually will happen or not, and we cannot step back from | |
1823 | tail-calling. Thus, we inhibit tail-calling with -mcall-prologues. */ | |
1824 | ||
1825 | if (cfun->machine->sibcall_fails | |
1826 | || TARGET_CALL_PROLOGUES) | |
1827 | { | |
1828 | return false; | |
1829 | } | |
1830 | ||
1831 | fntype_callee = TREE_TYPE (CALL_EXPR_FN (exp_callee)); | |
1832 | ||
1833 | if (decl_callee) | |
1834 | { | |
1835 | decl_callee = TREE_TYPE (decl_callee); | |
1836 | } | |
1837 | else | |
1838 | { | |
1839 | decl_callee = fntype_callee; | |
1840 | ||
1841 | while (FUNCTION_TYPE != TREE_CODE (decl_callee) | |
1842 | && METHOD_TYPE != TREE_CODE (decl_callee)) | |
1843 | { | |
1844 | decl_callee = TREE_TYPE (decl_callee); | |
1845 | } | |
1846 | } | |
1847 | ||
1848 | /* Ensure that caller and callee have compatible epilogues */ | |
1849 | ||
1850 | if (interrupt_function_p (current_function_decl) | |
1851 | || signal_function_p (current_function_decl) | |
1852 | || avr_naked_function_p (decl_callee) | |
1853 | || avr_naked_function_p (current_function_decl) | |
1854 | /* FIXME: For OS_task and OS_main, we are over-conservative. | |
1855 | This is due to missing documentation of these attributes | |
1856 | and what they actually should do and should not do. */ | |
1857 | || (avr_OS_task_function_p (decl_callee) | |
1858 | != avr_OS_task_function_p (current_function_decl)) | |
1859 | || (avr_OS_main_function_p (decl_callee) | |
1860 | != avr_OS_main_function_p (current_function_decl))) | |
1861 | { | |
1862 | return false; | |
1863 | } | |
1864 | ||
1865 | return true; | |
1866 | } | |
1867 | ||
90e7678c DC |
1868 | /*********************************************************************** |
1869 | Functions for outputting various mov's for a various modes | |
1870 | ************************************************************************/ | |
5fecfd8d | 1871 | const char * |
269e3795 | 1872 | output_movqi (rtx insn, rtx operands[], int *l) |
90e7678c | 1873 | { |
6bec29c9 DC |
1874 | int dummy; |
1875 | rtx dest = operands[0]; | |
1876 | rtx src = operands[1]; | |
1877 | int *real_l = l; | |
1878 | ||
1879 | if (!l) | |
1880 | l = &dummy; | |
1881 | ||
1882 | *l = 1; | |
1883 | ||
1884 | if (register_operand (dest, QImode)) | |
90e7678c | 1885 | { |
6bec29c9 | 1886 | if (register_operand (src, QImode)) /* mov r,r */ |
90e7678c | 1887 | { |
6bec29c9 DC |
1888 | if (test_hard_reg_class (STACK_REG, dest)) |
1889 | return AS2 (out,%0,%1); | |
1890 | else if (test_hard_reg_class (STACK_REG, src)) | |
1891 | return AS2 (in,%0,%1); | |
1892 | ||
1893 | return AS2 (mov,%0,%1); | |
1894 | } | |
1895 | else if (CONSTANT_P (src)) | |
1896 | { | |
1897 | if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */ | |
1898 | return AS2 (ldi,%0,lo8(%1)); | |
1899 | ||
1900 | if (GET_CODE (src) == CONST_INT) | |
90e7678c | 1901 | { |
6bec29c9 DC |
1902 | if (src == const0_rtx) /* mov r,L */ |
1903 | return AS1 (clr,%0); | |
1904 | else if (src == const1_rtx) | |
90e7678c | 1905 | { |
6bec29c9 DC |
1906 | *l = 2; |
1907 | return (AS1 (clr,%0) CR_TAB | |
1908 | AS1 (inc,%0)); | |
1909 | } | |
6bec29c9 | 1910 | else if (src == constm1_rtx) |
90e7678c | 1911 | { |
6bec29c9 | 1912 | /* Immediate constants -1 to any register */ |
6bec29c9 DC |
1913 | *l = 2; |
1914 | return (AS1 (clr,%0) CR_TAB | |
1915 | AS1 (dec,%0)); | |
90e7678c | 1916 | } |
afee2a52 MM |
1917 | else |
1918 | { | |
1919 | int bit_nr = exact_log2 (INTVAL (src)); | |
1920 | ||
1921 | if (bit_nr >= 0) | |
1922 | { | |
6001794d KH |
1923 | *l = 3; |
1924 | if (!real_l) | |
1925 | output_asm_insn ((AS1 (clr,%0) CR_TAB | |
1926 | "set"), operands); | |
afee2a52 MM |
1927 | if (!real_l) |
1928 | avr_output_bld (operands, bit_nr); | |
1929 | ||
1930 | return ""; | |
1931 | } | |
1932 | } | |
90e7678c | 1933 | } |
6bec29c9 DC |
1934 | |
1935 | /* Last resort, larger than loading from memory. */ | |
1936 | *l = 4; | |
1937 | return (AS2 (mov,__tmp_reg__,r31) CR_TAB | |
1938 | AS2 (ldi,r31,lo8(%1)) CR_TAB | |
1939 | AS2 (mov,%0,r31) CR_TAB | |
1940 | AS2 (mov,r31,__tmp_reg__)); | |
1941 | } | |
1942 | else if (GET_CODE (src) == MEM) | |
1943 | return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */ | |
1944 | } | |
1945 | else if (GET_CODE (dest) == MEM) | |
1946 | { | |
0a2aaacc | 1947 | const char *templ; |
5fecfd8d MM |
1948 | |
1949 | if (src == const0_rtx) | |
1950 | operands[1] = zero_reg_rtx; | |
1951 | ||
0a2aaacc | 1952 | templ = out_movqi_mr_r (insn, operands, real_l); |
6bec29c9 DC |
1953 | |
1954 | if (!real_l) | |
0a2aaacc | 1955 | output_asm_insn (templ, operands); |
5fecfd8d MM |
1956 | |
1957 | operands[1] = src; | |
6bec29c9 DC |
1958 | } |
1959 | return ""; | |
1960 | } | |
1961 | ||
1962 | ||
5fecfd8d | 1963 | const char * |
269e3795 | 1964 | output_movhi (rtx insn, rtx operands[], int *l) |
6bec29c9 DC |
1965 | { |
1966 | int dummy; | |
1967 | rtx dest = operands[0]; | |
1968 | rtx src = operands[1]; | |
1969 | int *real_l = l; | |
1970 | ||
1971 | if (!l) | |
1972 | l = &dummy; | |
1973 | ||
1974 | if (register_operand (dest, HImode)) | |
1975 | { | |
1976 | if (register_operand (src, HImode)) /* mov r,r */ | |
1977 | { | |
1978 | if (test_hard_reg_class (STACK_REG, dest)) | |
90e7678c | 1979 | { |
164709cf | 1980 | if (AVR_HAVE_8BIT_SP) |
794cb45e AS |
1981 | return *l = 1, AS2 (out,__SP_L__,%A1); |
1982 | /* Use simple load of stack pointer if no interrupts are | |
1983 | used. */ | |
1984 | else if (TARGET_NO_INTERRUPTS) | |
1985 | return *l = 2, (AS2 (out,__SP_H__,%B1) CR_TAB | |
1986 | AS2 (out,__SP_L__,%A1)); | |
6bec29c9 DC |
1987 | *l = 5; |
1988 | return (AS2 (in,__tmp_reg__,__SREG__) CR_TAB | |
1989 | "cli" CR_TAB | |
1990 | AS2 (out,__SP_H__,%B1) CR_TAB | |
1991 | AS2 (out,__SREG__,__tmp_reg__) CR_TAB | |
1992 | AS2 (out,__SP_L__,%A1)); | |
1993 | } | |
1994 | else if (test_hard_reg_class (STACK_REG, src)) | |
1995 | { | |
1996 | *l = 2; | |
1997 | return (AS2 (in,%A0,__SP_L__) CR_TAB | |
1998 | AS2 (in,%B0,__SP_H__)); | |
1999 | } | |
2000 | ||
4301ec4f | 2001 | if (AVR_HAVE_MOVW) |
6bec29c9 DC |
2002 | { |
2003 | *l = 1; | |
2004 | return (AS2 (movw,%0,%1)); | |
2005 | } | |
6bec29c9 DC |
2006 | else |
2007 | { | |
2008 | *l = 2; | |
2009 | return (AS2 (mov,%A0,%A1) CR_TAB | |
2010 | AS2 (mov,%B0,%B1)); | |
2011 | } | |
2012 | } | |
2013 | else if (CONSTANT_P (src)) | |
2014 | { | |
2015 | if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */ | |
2016 | { | |
2017 | *l = 2; | |
2018 | return (AS2 (ldi,%A0,lo8(%1)) CR_TAB | |
2019 | AS2 (ldi,%B0,hi8(%1))); | |
2020 | } | |
2021 | ||
2022 | if (GET_CODE (src) == CONST_INT) | |
2023 | { | |
2024 | if (src == const0_rtx) /* mov r,L */ | |
2025 | { | |
2026 | *l = 2; | |
2027 | return (AS1 (clr,%A0) CR_TAB | |
2028 | AS1 (clr,%B0)); | |
2029 | } | |
2030 | else if (src == const1_rtx) | |
2031 | { | |
08f81276 MM |
2032 | *l = 3; |
2033 | return (AS1 (clr,%A0) CR_TAB | |
2034 | AS1 (clr,%B0) CR_TAB | |
2035 | AS1 (inc,%A0)); | |
6bec29c9 | 2036 | } |
6bec29c9 DC |
2037 | else if (src == constm1_rtx) |
2038 | { | |
2039 | /* Immediate constants -1 to any register */ | |
6bec29c9 DC |
2040 | *l = 3; |
2041 | return (AS1 (clr,%0) CR_TAB | |
2042 | AS1 (dec,%A0) CR_TAB | |
2043 | AS2 (mov,%B0,%A0)); | |
2044 | } | |
afee2a52 MM |
2045 | else |
2046 | { | |
2047 | int bit_nr = exact_log2 (INTVAL (src)); | |
2048 | ||
2049 | if (bit_nr >= 0) | |
2050 | { | |
6001794d KH |
2051 | *l = 4; |
2052 | if (!real_l) | |
2053 | output_asm_insn ((AS1 (clr,%A0) CR_TAB | |
2054 | AS1 (clr,%B0) CR_TAB | |
2055 | "set"), operands); | |
afee2a52 MM |
2056 | if (!real_l) |
2057 | avr_output_bld (operands, bit_nr); | |
2058 | ||
2059 | return ""; | |
2060 | } | |
2061 | } | |
2062 | ||
6bec29c9 DC |
2063 | if ((INTVAL (src) & 0xff) == 0) |
2064 | { | |
2065 | *l = 5; | |
2066 | return (AS2 (mov,__tmp_reg__,r31) CR_TAB | |
2067 | AS1 (clr,%A0) CR_TAB | |
2068 | AS2 (ldi,r31,hi8(%1)) CR_TAB | |
2069 | AS2 (mov,%B0,r31) CR_TAB | |
2070 | AS2 (mov,r31,__tmp_reg__)); | |
2071 | } | |
2072 | else if ((INTVAL (src) & 0xff00) == 0) | |
2073 | { | |
2074 | *l = 5; | |
2075 | return (AS2 (mov,__tmp_reg__,r31) CR_TAB | |
2076 | AS2 (ldi,r31,lo8(%1)) CR_TAB | |
2077 | AS2 (mov,%A0,r31) CR_TAB | |
2078 | AS1 (clr,%B0) CR_TAB | |
2079 | AS2 (mov,r31,__tmp_reg__)); | |
90e7678c | 2080 | } |
90e7678c | 2081 | } |
6bec29c9 DC |
2082 | |
2083 | /* Last resort, equal to loading from memory. */ | |
2084 | *l = 6; | |
2085 | return (AS2 (mov,__tmp_reg__,r31) CR_TAB | |
2086 | AS2 (ldi,r31,lo8(%1)) CR_TAB | |
2087 | AS2 (mov,%A0,r31) CR_TAB | |
2088 | AS2 (ldi,r31,hi8(%1)) CR_TAB | |
2089 | AS2 (mov,%B0,r31) CR_TAB | |
2090 | AS2 (mov,r31,__tmp_reg__)); | |
2091 | } | |
2092 | else if (GET_CODE (src) == MEM) | |
2093 | return out_movhi_r_mr (insn, operands, real_l); /* mov r,m */ | |
2094 | } | |
2095 | else if (GET_CODE (dest) == MEM) | |
2096 | { | |
0a2aaacc | 2097 | const char *templ; |
5fecfd8d MM |
2098 | |
2099 | if (src == const0_rtx) | |
2100 | operands[1] = zero_reg_rtx; | |
2101 | ||
0a2aaacc | 2102 | templ = out_movhi_mr_r (insn, operands, real_l); |
6bec29c9 DC |
2103 | |
2104 | if (!real_l) | |
0a2aaacc | 2105 | output_asm_insn (templ, operands); |
5fecfd8d MM |
2106 | |
2107 | operands[1] = src; | |
6bec29c9 | 2108 | return ""; |
90e7678c | 2109 | } |
c725bd79 | 2110 | fatal_insn ("invalid insn:", insn); |
6bec29c9 DC |
2111 | return ""; |
2112 | } | |
2113 | ||
5fecfd8d | 2114 | const char * |
269e3795 | 2115 | out_movqi_r_mr (rtx insn, rtx op[], int *l) |
6bec29c9 | 2116 | { |
5fecfd8d MM |
2117 | rtx dest = op[0]; |
2118 | rtx src = op[1]; | |
2119 | rtx x = XEXP (src, 0); | |
6bec29c9 DC |
2120 | int dummy; |
2121 | ||
2122 | if (!l) | |
2123 | l = &dummy; | |
6bec29c9 DC |
2124 | |
2125 | if (CONSTANT_ADDRESS_P (x)) | |
2126 | { | |
4fc2b4ff AH |
2127 | if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR) |
2128 | { | |
2129 | *l = 1; | |
2130 | return AS2 (in,%0,__SREG__); | |
2131 | } | |
e6b69d0e | 2132 | if (optimize > 0 && io_address_operand (x, QImode)) |
6bec29c9 DC |
2133 | { |
2134 | *l = 1; | |
846428f1 | 2135 | return AS2 (in,%0,%m1-0x20); |
6bec29c9 DC |
2136 | } |
2137 | *l = 2; | |
846428f1 | 2138 | return AS2 (lds,%0,%m1); |
6bec29c9 DC |
2139 | } |
2140 | /* memory access by reg+disp */ | |
2141 | else if (GET_CODE (x) == PLUS | |
2142 | && REG_P (XEXP (x,0)) | |
2143 | && GET_CODE (XEXP (x,1)) == CONST_INT) | |
2144 | { | |
5fecfd8d | 2145 | if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63) |
6bec29c9 DC |
2146 | { |
2147 | int disp = INTVAL (XEXP (x,1)); | |
2148 | if (REGNO (XEXP (x,0)) != REG_Y) | |
c725bd79 | 2149 | fatal_insn ("incorrect insn:",insn); |
33d5e2dc | 2150 | |
5fecfd8d | 2151 | if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) |
33d5e2dc MM |
2152 | return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB |
2153 | AS2 (ldd,%0,Y+63) CR_TAB | |
2154 | AS2 (sbiw,r28,%o1-63)); | |
2155 | ||
2156 | return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB | |
2157 | AS2 (sbci,r29,hi8(-%o1)) CR_TAB | |
2158 | AS2 (ld,%0,Y) CR_TAB | |
2159 | AS2 (subi,r28,lo8(%o1)) CR_TAB | |
2160 | AS2 (sbci,r29,hi8(%o1))); | |
6bec29c9 DC |
2161 | } |
2162 | else if (REGNO (XEXP (x,0)) == REG_X) | |
2163 | { | |
6bec29c9 DC |
2164 | /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude |
2165 | it but I have this situation with extremal optimizing options. */ | |
5fecfd8d | 2166 | if (reg_overlap_mentioned_p (dest, XEXP (x,0)) |
6bec29c9 | 2167 | || reg_unused_after (insn, XEXP (x,0))) |
33d5e2dc MM |
2168 | return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB |
2169 | AS2 (ld,%0,X)); | |
2170 | ||
2171 | return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB | |
2172 | AS2 (ld,%0,X) CR_TAB | |
2173 | AS2 (sbiw,r26,%o1)); | |
6bec29c9 DC |
2174 | } |
2175 | *l = 1; | |
2176 | return AS2 (ldd,%0,%1); | |
2177 | } | |
2178 | *l = 1; | |
2179 | return AS2 (ld,%0,%1); | |
90e7678c DC |
2180 | } |
2181 | ||
5fecfd8d | 2182 | const char * |
269e3795 | 2183 | out_movhi_r_mr (rtx insn, rtx op[], int *l) |
90e7678c | 2184 | { |
5fecfd8d MM |
2185 | rtx dest = op[0]; |
2186 | rtx src = op[1]; | |
2187 | rtx base = XEXP (src, 0); | |
2188 | int reg_dest = true_regnum (dest); | |
2189 | int reg_base = true_regnum (base); | |
a6990185 MM |
2190 | /* "volatile" forces reading low byte first, even if less efficient, |
2191 | for correct operation with 16-bit I/O registers. */ | |
2192 | int mem_volatile_p = MEM_VOLATILE_P (src); | |
6bec29c9 | 2193 | int tmp; |
90e7678c DC |
2194 | |
2195 | if (!l) | |
6bec29c9 | 2196 | l = &tmp; |
90e7678c DC |
2197 | |
2198 | if (reg_base > 0) | |
2199 | { | |
2200 | if (reg_dest == reg_base) /* R = (R) */ | |
6bec29c9 DC |
2201 | { |
2202 | *l = 3; | |
2203 | return (AS2 (ld,__tmp_reg__,%1+) CR_TAB | |
2204 | AS2 (ld,%B0,%1) CR_TAB | |
2205 | AS2 (mov,%A0,__tmp_reg__)); | |
2206 | } | |
90e7678c DC |
2207 | else if (reg_base == REG_X) /* (R26) */ |
2208 | { | |
5fecfd8d | 2209 | if (reg_unused_after (insn, base)) |
6bec29c9 DC |
2210 | { |
2211 | *l = 2; | |
2212 | return (AS2 (ld,%A0,X+) CR_TAB | |
2213 | AS2 (ld,%B0,X)); | |
2214 | } | |
2215 | *l = 3; | |
2216 | return (AS2 (ld,%A0,X+) CR_TAB | |
2217 | AS2 (ld,%B0,X) CR_TAB | |
2218 | AS2 (sbiw,r26,1)); | |
90e7678c DC |
2219 | } |
2220 | else /* (R) */ | |
6bec29c9 DC |
2221 | { |
2222 | *l = 2; | |
2223 | return (AS2 (ld,%A0,%1) CR_TAB | |
2224 | AS2 (ldd,%B0,%1+1)); | |
2225 | } | |
90e7678c | 2226 | } |
5fecfd8d | 2227 | else if (GET_CODE (base) == PLUS) /* (R + i) */ |
90e7678c | 2228 | { |
5fecfd8d MM |
2229 | int disp = INTVAL (XEXP (base, 1)); |
2230 | int reg_base = true_regnum (XEXP (base, 0)); | |
90e7678c | 2231 | |
5fecfd8d | 2232 | if (disp > MAX_LD_OFFSET (GET_MODE (src))) |
90e7678c | 2233 | { |
5fecfd8d | 2234 | if (REGNO (XEXP (base, 0)) != REG_Y) |
c725bd79 | 2235 | fatal_insn ("incorrect insn:",insn); |
6bec29c9 | 2236 | |
5fecfd8d | 2237 | if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) |
33d5e2dc MM |
2238 | return *l = 4, (AS2 (adiw,r28,%o1-62) CR_TAB |
2239 | AS2 (ldd,%A0,Y+62) CR_TAB | |
2240 | AS2 (ldd,%B0,Y+63) CR_TAB | |
2241 | AS2 (sbiw,r28,%o1-62)); | |
2242 | ||
2243 | return *l = 6, (AS2 (subi,r28,lo8(-%o1)) CR_TAB | |
2244 | AS2 (sbci,r29,hi8(-%o1)) CR_TAB | |
2245 | AS2 (ld,%A0,Y) CR_TAB | |
2246 | AS2 (ldd,%B0,Y+1) CR_TAB | |
2247 | AS2 (subi,r28,lo8(%o1)) CR_TAB | |
2248 | AS2 (sbci,r29,hi8(%o1))); | |
90e7678c DC |
2249 | } |
2250 | if (reg_base == REG_X) | |
2251 | { | |
2252 | /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude | |
6bec29c9 DC |
2253 | it but I have this situation with extremal |
2254 | optimization options. */ | |
2255 | ||
33d5e2dc | 2256 | *l = 4; |
6bec29c9 | 2257 | if (reg_base == reg_dest) |
33d5e2dc MM |
2258 | return (AS2 (adiw,r26,%o1) CR_TAB |
2259 | AS2 (ld,__tmp_reg__,X+) CR_TAB | |
2260 | AS2 (ld,%B0,X) CR_TAB | |
2261 | AS2 (mov,%A0,__tmp_reg__)); | |
6bec29c9 | 2262 | |
33d5e2dc MM |
2263 | return (AS2 (adiw,r26,%o1) CR_TAB |
2264 | AS2 (ld,%A0,X+) CR_TAB | |
2265 | AS2 (ld,%B0,X) CR_TAB | |
2266 | AS2 (sbiw,r26,%o1+1)); | |
6bec29c9 DC |
2267 | } |
2268 | ||
2269 | if (reg_base == reg_dest) | |
2270 | { | |
2271 | *l = 3; | |
2272 | return (AS2 (ldd,__tmp_reg__,%A1) CR_TAB | |
2273 | AS2 (ldd,%B0,%B1) CR_TAB | |
2274 | AS2 (mov,%A0,__tmp_reg__)); | |
90e7678c DC |
2275 | } |
2276 | ||
6bec29c9 DC |
2277 | *l = 2; |
2278 | return (AS2 (ldd,%A0,%A1) CR_TAB | |
2279 | AS2 (ldd,%B0,%B1)); | |
90e7678c | 2280 | } |
5fecfd8d | 2281 | else if (GET_CODE (base) == PRE_DEC) /* (--R) */ |
90e7678c | 2282 | { |
5fecfd8d | 2283 | if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) |
c725bd79 | 2284 | fatal_insn ("incorrect insn:", insn); |
6bec29c9 | 2285 | |
a6990185 MM |
2286 | if (mem_volatile_p) |
2287 | { | |
2288 | if (REGNO (XEXP (base, 0)) == REG_X) | |
2289 | { | |
2290 | *l = 4; | |
2291 | return (AS2 (sbiw,r26,2) CR_TAB | |
2292 | AS2 (ld,%A0,X+) CR_TAB | |
2293 | AS2 (ld,%B0,X) CR_TAB | |
2294 | AS2 (sbiw,r26,1)); | |
2295 | } | |
2296 | else | |
2297 | { | |
2298 | *l = 3; | |
2299 | return (AS2 (sbiw,%r1,2) CR_TAB | |
2300 | AS2 (ld,%A0,%p1) CR_TAB | |
2301 | AS2 (ldd,%B0,%p1+1)); | |
2302 | } | |
2303 | } | |
2304 | ||
6bec29c9 DC |
2305 | *l = 2; |
2306 | return (AS2 (ld,%B0,%1) CR_TAB | |
2307 | AS2 (ld,%A0,%1)); | |
90e7678c | 2308 | } |
5fecfd8d | 2309 | else if (GET_CODE (base) == POST_INC) /* (R++) */ |
90e7678c | 2310 | { |
5fecfd8d | 2311 | if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) |
c725bd79 | 2312 | fatal_insn ("incorrect insn:", insn); |
6bec29c9 DC |
2313 | |
2314 | *l = 2; | |
2315 | return (AS2 (ld,%A0,%1) CR_TAB | |
2316 | AS2 (ld,%B0,%1)); | |
90e7678c | 2317 | } |
5fecfd8d | 2318 | else if (CONSTANT_ADDRESS_P (base)) |
6bec29c9 | 2319 | { |
e6b69d0e | 2320 | if (optimize > 0 && io_address_operand (base, HImode)) |
5fecfd8d MM |
2321 | { |
2322 | *l = 2; | |
846428f1 AH |
2323 | return (AS2 (in,%A0,%m1-0x20) CR_TAB |
2324 | AS2 (in,%B0,%m1+1-0x20)); | |
5fecfd8d | 2325 | } |
6bec29c9 | 2326 | *l = 4; |
846428f1 AH |
2327 | return (AS2 (lds,%A0,%m1) CR_TAB |
2328 | AS2 (lds,%B0,%m1+1)); | |
6bec29c9 DC |
2329 | } |
2330 | ||
c725bd79 | 2331 | fatal_insn ("unknown move insn:",insn); |
90e7678c DC |
2332 | return ""; |
2333 | } | |
2334 | ||
5fecfd8d | 2335 | const char * |
269e3795 | 2336 | out_movsi_r_mr (rtx insn, rtx op[], int *l) |
90e7678c | 2337 | { |
5fecfd8d MM |
2338 | rtx dest = op[0]; |
2339 | rtx src = op[1]; | |
2340 | rtx base = XEXP (src, 0); | |
2341 | int reg_dest = true_regnum (dest); | |
2342 | int reg_base = true_regnum (base); | |
90e7678c | 2343 | int tmp; |
6bec29c9 | 2344 | |
90e7678c | 2345 | if (!l) |
6bec29c9 DC |
2346 | l = &tmp; |
2347 | ||
90e7678c DC |
2348 | if (reg_base > 0) |
2349 | { | |
2350 | if (reg_base == REG_X) /* (R26) */ | |
2351 | { | |
2352 | if (reg_dest == REG_X) | |
dfaf5abf MM |
2353 | /* "ld r26,-X" is undefined */ |
2354 | return *l=7, (AS2 (adiw,r26,3) CR_TAB | |
2355 | AS2 (ld,r29,X) CR_TAB | |
2356 | AS2 (ld,r28,-X) CR_TAB | |
2357 | AS2 (ld,__tmp_reg__,-X) CR_TAB | |
2358 | AS2 (sbiw,r26,1) CR_TAB | |
2359 | AS2 (ld,r26,X) CR_TAB | |
2360 | AS2 (mov,r27,__tmp_reg__)); | |
90e7678c DC |
2361 | else if (reg_dest == REG_X - 2) |
2362 | return *l=5, (AS2 (ld,%A0,X+) CR_TAB | |
2363 | AS2 (ld,%B0,X+) CR_TAB | |
2364 | AS2 (ld,__tmp_reg__,X+) CR_TAB | |
2365 | AS2 (ld,%D0,X) CR_TAB | |
2366 | AS2 (mov,%C0,__tmp_reg__)); | |
5fecfd8d | 2367 | else if (reg_unused_after (insn, base)) |
90e7678c DC |
2368 | return *l=4, (AS2 (ld,%A0,X+) CR_TAB |
2369 | AS2 (ld,%B0,X+) CR_TAB | |
2370 | AS2 (ld,%C0,X+) CR_TAB | |
2371 | AS2 (ld,%D0,X)); | |
2372 | else | |
2373 | return *l=5, (AS2 (ld,%A0,X+) CR_TAB | |
2374 | AS2 (ld,%B0,X+) CR_TAB | |
2375 | AS2 (ld,%C0,X+) CR_TAB | |
2376 | AS2 (ld,%D0,X) CR_TAB | |
2377 | AS2 (sbiw,r26,3)); | |
2378 | } | |
2379 | else | |
2380 | { | |
2381 | if (reg_dest == reg_base) | |
2382 | return *l=5, (AS2 (ldd,%D0,%1+3) CR_TAB | |
2383 | AS2 (ldd,%C0,%1+2) CR_TAB | |
2384 | AS2 (ldd,__tmp_reg__,%1+1) CR_TAB | |
2385 | AS2 (ld,%A0,%1) CR_TAB | |
2386 | AS2 (mov,%B0,__tmp_reg__)); | |
2387 | else if (reg_base == reg_dest + 2) | |
2388 | return *l=5, (AS2 (ld ,%A0,%1) CR_TAB | |
2389 | AS2 (ldd,%B0,%1+1) CR_TAB | |
2390 | AS2 (ldd,__tmp_reg__,%1+2) CR_TAB | |
2391 | AS2 (ldd,%D0,%1+3) CR_TAB | |
2392 | AS2 (mov,%C0,__tmp_reg__)); | |
2393 | else | |
2394 | return *l=4, (AS2 (ld ,%A0,%1) CR_TAB | |
2395 | AS2 (ldd,%B0,%1+1) CR_TAB | |
2396 | AS2 (ldd,%C0,%1+2) CR_TAB | |
2397 | AS2 (ldd,%D0,%1+3)); | |
2398 | } | |
2399 | } | |
5fecfd8d | 2400 | else if (GET_CODE (base) == PLUS) /* (R + i) */ |
90e7678c | 2401 | { |
5fecfd8d | 2402 | int disp = INTVAL (XEXP (base, 1)); |
90e7678c | 2403 | |
5fecfd8d | 2404 | if (disp > MAX_LD_OFFSET (GET_MODE (src))) |
90e7678c | 2405 | { |
5fecfd8d | 2406 | if (REGNO (XEXP (base, 0)) != REG_Y) |
c725bd79 | 2407 | fatal_insn ("incorrect insn:",insn); |
33d5e2dc | 2408 | |
5fecfd8d | 2409 | if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) |
33d5e2dc MM |
2410 | return *l = 6, (AS2 (adiw,r28,%o1-60) CR_TAB |
2411 | AS2 (ldd,%A0,Y+60) CR_TAB | |
2412 | AS2 (ldd,%B0,Y+61) CR_TAB | |
2413 | AS2 (ldd,%C0,Y+62) CR_TAB | |
2414 | AS2 (ldd,%D0,Y+63) CR_TAB | |
2415 | AS2 (sbiw,r28,%o1-60)); | |
2416 | ||
2417 | return *l = 8, (AS2 (subi,r28,lo8(-%o1)) CR_TAB | |
2418 | AS2 (sbci,r29,hi8(-%o1)) CR_TAB | |
2419 | AS2 (ld,%A0,Y) CR_TAB | |
2420 | AS2 (ldd,%B0,Y+1) CR_TAB | |
2421 | AS2 (ldd,%C0,Y+2) CR_TAB | |
2422 | AS2 (ldd,%D0,Y+3) CR_TAB | |
2423 | AS2 (subi,r28,lo8(%o1)) CR_TAB | |
2424 | AS2 (sbci,r29,hi8(%o1))); | |
90e7678c DC |
2425 | } |
2426 | ||
5fecfd8d | 2427 | reg_base = true_regnum (XEXP (base, 0)); |
dfaf5abf MM |
2428 | if (reg_base == REG_X) |
2429 | { | |
2430 | /* R = (X + d) */ | |
2431 | if (reg_dest == REG_X) | |
2432 | { | |
2433 | *l = 7; | |
2434 | /* "ld r26,-X" is undefined */ | |
2435 | return (AS2 (adiw,r26,%o1+3) CR_TAB | |
2436 | AS2 (ld,r29,X) CR_TAB | |
2437 | AS2 (ld,r28,-X) CR_TAB | |
2438 | AS2 (ld,__tmp_reg__,-X) CR_TAB | |
2439 | AS2 (sbiw,r26,1) CR_TAB | |
2440 | AS2 (ld,r26,X) CR_TAB | |
2441 | AS2 (mov,r27,__tmp_reg__)); | |
2442 | } | |
2443 | *l = 6; | |
2444 | if (reg_dest == REG_X - 2) | |
2445 | return (AS2 (adiw,r26,%o1) CR_TAB | |
2446 | AS2 (ld,r24,X+) CR_TAB | |
2447 | AS2 (ld,r25,X+) CR_TAB | |
2448 | AS2 (ld,__tmp_reg__,X+) CR_TAB | |
2449 | AS2 (ld,r27,X) CR_TAB | |
2450 | AS2 (mov,r26,__tmp_reg__)); | |
2451 | ||
2452 | return (AS2 (adiw,r26,%o1) CR_TAB | |
2453 | AS2 (ld,%A0,X+) CR_TAB | |
2454 | AS2 (ld,%B0,X+) CR_TAB | |
2455 | AS2 (ld,%C0,X+) CR_TAB | |
2456 | AS2 (ld,%D0,X) CR_TAB | |
2457 | AS2 (sbiw,r26,%o1+3)); | |
2458 | } | |
90e7678c DC |
2459 | if (reg_dest == reg_base) |
2460 | return *l=5, (AS2 (ldd,%D0,%D1) CR_TAB | |
2461 | AS2 (ldd,%C0,%C1) CR_TAB | |
2462 | AS2 (ldd,__tmp_reg__,%B1) CR_TAB | |
2463 | AS2 (ldd,%A0,%A1) CR_TAB | |
2464 | AS2 (mov,%B0,__tmp_reg__)); | |
2465 | else if (reg_dest == reg_base - 2) | |
2466 | return *l=5, (AS2 (ldd,%A0,%A1) CR_TAB | |
2467 | AS2 (ldd,%B0,%B1) CR_TAB | |
2468 | AS2 (ldd,__tmp_reg__,%C1) CR_TAB | |
2469 | AS2 (ldd,%D0,%D1) CR_TAB | |
2470 | AS2 (mov,%C0,__tmp_reg__)); | |
2471 | return *l=4, (AS2 (ldd,%A0,%A1) CR_TAB | |
2472 | AS2 (ldd,%B0,%B1) CR_TAB | |
2473 | AS2 (ldd,%C0,%C1) CR_TAB | |
2474 | AS2 (ldd,%D0,%D1)); | |
2475 | } | |
5fecfd8d | 2476 | else if (GET_CODE (base) == PRE_DEC) /* (--R) */ |
90e7678c DC |
2477 | return *l=4, (AS2 (ld,%D0,%1) CR_TAB |
2478 | AS2 (ld,%C0,%1) CR_TAB | |
2479 | AS2 (ld,%B0,%1) CR_TAB | |
2480 | AS2 (ld,%A0,%1)); | |
5fecfd8d | 2481 | else if (GET_CODE (base) == POST_INC) /* (R++) */ |
90e7678c DC |
2482 | return *l=4, (AS2 (ld,%A0,%1) CR_TAB |
2483 | AS2 (ld,%B0,%1) CR_TAB | |
2484 | AS2 (ld,%C0,%1) CR_TAB | |
2485 | AS2 (ld,%D0,%1)); | |
5fecfd8d | 2486 | else if (CONSTANT_ADDRESS_P (base)) |
846428f1 AH |
2487 | return *l=8, (AS2 (lds,%A0,%m1) CR_TAB |
2488 | AS2 (lds,%B0,%m1+1) CR_TAB | |
2489 | AS2 (lds,%C0,%m1+2) CR_TAB | |
2490 | AS2 (lds,%D0,%m1+3)); | |
90e7678c | 2491 | |
c725bd79 | 2492 | fatal_insn ("unknown move insn:",insn); |
90e7678c DC |
2493 | return ""; |
2494 | } | |
2495 | ||
5fecfd8d | 2496 | const char * |
269e3795 | 2497 | out_movsi_mr_r (rtx insn, rtx op[], int *l) |
90e7678c | 2498 | { |
5fecfd8d MM |
2499 | rtx dest = op[0]; |
2500 | rtx src = op[1]; | |
2501 | rtx base = XEXP (dest, 0); | |
2502 | int reg_base = true_regnum (base); | |
2503 | int reg_src = true_regnum (src); | |
90e7678c | 2504 | int tmp; |
6bec29c9 | 2505 | |
90e7678c DC |
2506 | if (!l) |
2507 | l = &tmp; | |
6bec29c9 | 2508 | |
5fecfd8d | 2509 | if (CONSTANT_ADDRESS_P (base)) |
846428f1 AH |
2510 | return *l=8,(AS2 (sts,%m0,%A1) CR_TAB |
2511 | AS2 (sts,%m0+1,%B1) CR_TAB | |
2512 | AS2 (sts,%m0+2,%C1) CR_TAB | |
2513 | AS2 (sts,%m0+3,%D1)); | |
90e7678c DC |
2514 | if (reg_base > 0) /* (r) */ |
2515 | { | |
2516 | if (reg_base == REG_X) /* (R26) */ | |
2517 | { | |
5fecfd8d | 2518 | if (reg_src == REG_X) |
90e7678c | 2519 | { |
dfaf5abf | 2520 | /* "st X+,r26" is undefined */ |
5fecfd8d | 2521 | if (reg_unused_after (insn, base)) |
dfaf5abf MM |
2522 | return *l=6, (AS2 (mov,__tmp_reg__,r27) CR_TAB |
2523 | AS2 (st,X,r26) CR_TAB | |
2524 | AS2 (adiw,r26,1) CR_TAB | |
2525 | AS2 (st,X+,__tmp_reg__) CR_TAB | |
2526 | AS2 (st,X+,r28) CR_TAB | |
2527 | AS2 (st,X,r29)); | |
90e7678c | 2528 | else |
dfaf5abf MM |
2529 | return *l=7, (AS2 (mov,__tmp_reg__,r27) CR_TAB |
2530 | AS2 (st,X,r26) CR_TAB | |
2531 | AS2 (adiw,r26,1) CR_TAB | |
2532 | AS2 (st,X+,__tmp_reg__) CR_TAB | |
2533 | AS2 (st,X+,r28) CR_TAB | |
2534 | AS2 (st,X,r29) CR_TAB | |
2535 | AS2 (sbiw,r26,3)); | |
90e7678c | 2536 | } |
5fecfd8d | 2537 | else if (reg_base == reg_src + 2) |
90e7678c | 2538 | { |
5fecfd8d | 2539 | if (reg_unused_after (insn, base)) |
90e7678c DC |
2540 | return *l=7, (AS2 (mov,__zero_reg__,%C1) CR_TAB |
2541 | AS2 (mov,__tmp_reg__,%D1) CR_TAB | |
2542 | AS2 (st,%0+,%A1) CR_TAB | |
2543 | AS2 (st,%0+,%B1) CR_TAB | |
2544 | AS2 (st,%0+,__zero_reg__) CR_TAB | |
2545 | AS2 (st,%0,__tmp_reg__) CR_TAB | |
2546 | AS1 (clr,__zero_reg__)); | |
2547 | else | |
2548 | return *l=8, (AS2 (mov,__zero_reg__,%C1) CR_TAB | |
2549 | AS2 (mov,__tmp_reg__,%D1) CR_TAB | |
2550 | AS2 (st,%0+,%A1) CR_TAB | |
2551 | AS2 (st,%0+,%B1) CR_TAB | |
2552 | AS2 (st,%0+,__zero_reg__) CR_TAB | |
2553 | AS2 (st,%0,__tmp_reg__) CR_TAB | |
2554 | AS1 (clr,__zero_reg__) CR_TAB | |
2555 | AS2 (sbiw,r26,3)); | |
2556 | } | |
2557 | return *l=5, (AS2 (st,%0+,%A1) CR_TAB | |
2558 | AS2 (st,%0+,%B1) CR_TAB | |
2559 | AS2 (st,%0+,%C1) CR_TAB | |
2560 | AS2 (st,%0,%D1) CR_TAB | |
2561 | AS2 (sbiw,r26,3)); | |
2562 | } | |
2563 | else | |
2564 | return *l=4, (AS2 (st,%0,%A1) CR_TAB | |
2565 | AS2 (std,%0+1,%B1) CR_TAB | |
2566 | AS2 (std,%0+2,%C1) CR_TAB | |
2567 | AS2 (std,%0+3,%D1)); | |
2568 | } | |
5fecfd8d | 2569 | else if (GET_CODE (base) == PLUS) /* (R + i) */ |
90e7678c | 2570 | { |
5fecfd8d | 2571 | int disp = INTVAL (XEXP (base, 1)); |
dfaf5abf | 2572 | reg_base = REGNO (XEXP (base, 0)); |
5fecfd8d | 2573 | if (disp > MAX_LD_OFFSET (GET_MODE (dest))) |
90e7678c | 2574 | { |
dfaf5abf | 2575 | if (reg_base != REG_Y) |
c725bd79 | 2576 | fatal_insn ("incorrect insn:",insn); |
33d5e2dc | 2577 | |
5fecfd8d | 2578 | if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) |
33d5e2dc MM |
2579 | return *l = 6, (AS2 (adiw,r28,%o0-60) CR_TAB |
2580 | AS2 (std,Y+60,%A1) CR_TAB | |
2581 | AS2 (std,Y+61,%B1) CR_TAB | |
2582 | AS2 (std,Y+62,%C1) CR_TAB | |
2583 | AS2 (std,Y+63,%D1) CR_TAB | |
2584 | AS2 (sbiw,r28,%o0-60)); | |
2585 | ||
2586 | return *l = 8, (AS2 (subi,r28,lo8(-%o0)) CR_TAB | |
2587 | AS2 (sbci,r29,hi8(-%o0)) CR_TAB | |
2588 | AS2 (st,Y,%A1) CR_TAB | |
2589 | AS2 (std,Y+1,%B1) CR_TAB | |
2590 | AS2 (std,Y+2,%C1) CR_TAB | |
2591 | AS2 (std,Y+3,%D1) CR_TAB | |
2592 | AS2 (subi,r28,lo8(%o0)) CR_TAB | |
2593 | AS2 (sbci,r29,hi8(%o0))); | |
90e7678c | 2594 | } |
dfaf5abf MM |
2595 | if (reg_base == REG_X) |
2596 | { | |
2597 | /* (X + d) = R */ | |
2598 | if (reg_src == REG_X) | |
2599 | { | |
2600 | *l = 9; | |
2601 | return (AS2 (mov,__tmp_reg__,r26) CR_TAB | |
2602 | AS2 (mov,__zero_reg__,r27) CR_TAB | |
2603 | AS2 (adiw,r26,%o0) CR_TAB | |
2604 | AS2 (st,X+,__tmp_reg__) CR_TAB | |
2605 | AS2 (st,X+,__zero_reg__) CR_TAB | |
2606 | AS2 (st,X+,r28) CR_TAB | |
2607 | AS2 (st,X,r29) CR_TAB | |
2608 | AS1 (clr,__zero_reg__) CR_TAB | |
2609 | AS2 (sbiw,r26,%o0+3)); | |
2610 | } | |
2611 | else if (reg_src == REG_X - 2) | |
2612 | { | |
2613 | *l = 9; | |
2614 | return (AS2 (mov,__tmp_reg__,r26) CR_TAB | |
2615 | AS2 (mov,__zero_reg__,r27) CR_TAB | |
2616 | AS2 (adiw,r26,%o0) CR_TAB | |
2617 | AS2 (st,X+,r24) CR_TAB | |
2618 | AS2 (st,X+,r25) CR_TAB | |
2619 | AS2 (st,X+,__tmp_reg__) CR_TAB | |
2620 | AS2 (st,X,__zero_reg__) CR_TAB | |
2621 | AS1 (clr,__zero_reg__) CR_TAB | |
2622 | AS2 (sbiw,r26,%o0+3)); | |
2623 | } | |
2624 | *l = 6; | |
2625 | return (AS2 (adiw,r26,%o0) CR_TAB | |
2626 | AS2 (st,X+,%A1) CR_TAB | |
2627 | AS2 (st,X+,%B1) CR_TAB | |
2628 | AS2 (st,X+,%C1) CR_TAB | |
2629 | AS2 (st,X,%D1) CR_TAB | |
2630 | AS2 (sbiw,r26,%o0+3)); | |
2631 | } | |
90e7678c DC |
2632 | return *l=4, (AS2 (std,%A0,%A1) CR_TAB |
2633 | AS2 (std,%B0,%B1) CR_TAB | |
2634 | AS2 (std,%C0,%C1) CR_TAB | |
2635 | AS2 (std,%D0,%D1)); | |
2636 | } | |
5fecfd8d | 2637 | else if (GET_CODE (base) == PRE_DEC) /* (--R) */ |
90e7678c DC |
2638 | return *l=4, (AS2 (st,%0,%D1) CR_TAB |
2639 | AS2 (st,%0,%C1) CR_TAB | |
2640 | AS2 (st,%0,%B1) CR_TAB | |
2641 | AS2 (st,%0,%A1)); | |
5fecfd8d | 2642 | else if (GET_CODE (base) == POST_INC) /* (R++) */ |
90e7678c DC |
2643 | return *l=4, (AS2 (st,%0,%A1) CR_TAB |
2644 | AS2 (st,%0,%B1) CR_TAB | |
2645 | AS2 (st,%0,%C1) CR_TAB | |
2646 | AS2 (st,%0,%D1)); | |
c725bd79 | 2647 | fatal_insn ("unknown move insn:",insn); |
90e7678c DC |
2648 | return ""; |
2649 | } | |
2650 | ||
5fecfd8d | 2651 | const char * |
269e3795 | 2652 | output_movsisf(rtx insn, rtx operands[], int *l) |
90e7678c | 2653 | { |
6bec29c9 DC |
2654 | int dummy; |
2655 | rtx dest = operands[0]; | |
2656 | rtx src = operands[1]; | |
2657 | int *real_l = l; | |
2658 | ||
2659 | if (!l) | |
2660 | l = &dummy; | |
2661 | ||
2662 | if (register_operand (dest, VOIDmode)) | |
90e7678c | 2663 | { |
6bec29c9 | 2664 | if (register_operand (src, VOIDmode)) /* mov r,r */ |
c4984bad | 2665 | { |
6bec29c9 DC |
2666 | if (true_regnum (dest) > true_regnum (src)) |
2667 | { | |
4301ec4f | 2668 | if (AVR_HAVE_MOVW) |
6bec29c9 DC |
2669 | { |
2670 | *l = 2; | |
2671 | return (AS2 (movw,%C0,%C1) CR_TAB | |
2672 | AS2 (movw,%A0,%A1)); | |
2673 | } | |
2674 | *l = 4; | |
2675 | return (AS2 (mov,%D0,%D1) CR_TAB | |
2676 | AS2 (mov,%C0,%C1) CR_TAB | |
2677 | AS2 (mov,%B0,%B1) CR_TAB | |
2678 | AS2 (mov,%A0,%A1)); | |
2679 | } | |
c4984bad | 2680 | else |
6bec29c9 | 2681 | { |
4301ec4f | 2682 | if (AVR_HAVE_MOVW) |
6bec29c9 DC |
2683 | { |
2684 | *l = 2; | |
2685 | return (AS2 (movw,%A0,%A1) CR_TAB | |
2686 | AS2 (movw,%C0,%C1)); | |
2687 | } | |
2688 | *l = 4; | |
2689 | return (AS2 (mov,%A0,%A1) CR_TAB | |
2690 | AS2 (mov,%B0,%B1) CR_TAB | |
2691 | AS2 (mov,%C0,%C1) CR_TAB | |
2692 | AS2 (mov,%D0,%D1)); | |
2693 | } | |
c4984bad | 2694 | } |
6bec29c9 | 2695 | else if (CONSTANT_P (src)) |
c4984bad | 2696 | { |
6bec29c9 DC |
2697 | if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */ |
2698 | { | |
2699 | *l = 4; | |
2700 | return (AS2 (ldi,%A0,lo8(%1)) CR_TAB | |
2701 | AS2 (ldi,%B0,hi8(%1)) CR_TAB | |
2702 | AS2 (ldi,%C0,hlo8(%1)) CR_TAB | |
2703 | AS2 (ldi,%D0,hhi8(%1))); | |
2704 | } | |
2705 | ||
2706 | if (GET_CODE (src) == CONST_INT) | |
2707 | { | |
83182544 | 2708 | const char *const clr_op0 = |
4301ec4f | 2709 | AVR_HAVE_MOVW ? (AS1 (clr,%A0) CR_TAB |
afee2a52 MM |
2710 | AS1 (clr,%B0) CR_TAB |
2711 | AS2 (movw,%C0,%A0)) | |
2712 | : (AS1 (clr,%A0) CR_TAB | |
2713 | AS1 (clr,%B0) CR_TAB | |
2714 | AS1 (clr,%C0) CR_TAB | |
2715 | AS1 (clr,%D0)); | |
2716 | ||
6bec29c9 DC |
2717 | if (src == const0_rtx) /* mov r,L */ |
2718 | { | |
4301ec4f | 2719 | *l = AVR_HAVE_MOVW ? 3 : 4; |
afee2a52 | 2720 | return clr_op0; |
6bec29c9 DC |
2721 | } |
2722 | else if (src == const1_rtx) | |
2723 | { | |
afee2a52 MM |
2724 | if (!real_l) |
2725 | output_asm_insn (clr_op0, operands); | |
4301ec4f | 2726 | *l = AVR_HAVE_MOVW ? 4 : 5; |
afee2a52 | 2727 | return AS1 (inc,%A0); |
6bec29c9 DC |
2728 | } |
2729 | else if (src == constm1_rtx) | |
2730 | { | |
2731 | /* Immediate constants -1 to any register */ | |
4301ec4f | 2732 | if (AVR_HAVE_MOVW) |
6bec29c9 DC |
2733 | { |
2734 | *l = 4; | |
2735 | return (AS1 (clr,%A0) CR_TAB | |
2736 | AS1 (dec,%A0) CR_TAB | |
2737 | AS2 (mov,%B0,%A0) CR_TAB | |
2738 | AS2 (movw,%C0,%A0)); | |
2739 | } | |
2740 | *l = 5; | |
2741 | return (AS1 (clr,%A0) CR_TAB | |
2742 | AS1 (dec,%A0) CR_TAB | |
2743 | AS2 (mov,%B0,%A0) CR_TAB | |
2744 | AS2 (mov,%C0,%A0) CR_TAB | |
2745 | AS2 (mov,%D0,%A0)); | |
2746 | } | |
afee2a52 MM |
2747 | else |
2748 | { | |
2749 | int bit_nr = exact_log2 (INTVAL (src)); | |
2750 | ||
2751 | if (bit_nr >= 0) | |
2752 | { | |
4301ec4f | 2753 | *l = AVR_HAVE_MOVW ? 5 : 6; |
6001794d | 2754 | if (!real_l) |
afee2a52 | 2755 | { |
6001794d KH |
2756 | output_asm_insn (clr_op0, operands); |
2757 | output_asm_insn ("set", operands); | |
afee2a52 MM |
2758 | } |
2759 | if (!real_l) | |
2760 | avr_output_bld (operands, bit_nr); | |
2761 | ||
2762 | return ""; | |
2763 | } | |
2764 | } | |
6bec29c9 DC |
2765 | } |
2766 | ||
2767 | /* Last resort, better than loading from memory. */ | |
2768 | *l = 10; | |
2769 | return (AS2 (mov,__tmp_reg__,r31) CR_TAB | |
2770 | AS2 (ldi,r31,lo8(%1)) CR_TAB | |
2771 | AS2 (mov,%A0,r31) CR_TAB | |
2772 | AS2 (ldi,r31,hi8(%1)) CR_TAB | |
2773 | AS2 (mov,%B0,r31) CR_TAB | |
2774 | AS2 (ldi,r31,hlo8(%1)) CR_TAB | |
2775 | AS2 (mov,%C0,r31) CR_TAB | |
2776 | AS2 (ldi,r31,hhi8(%1)) CR_TAB | |
2777 | AS2 (mov,%D0,r31) CR_TAB | |
2778 | AS2 (mov,r31,__tmp_reg__)); | |
2779 | } | |
2780 | else if (GET_CODE (src) == MEM) | |
2781 | return out_movsi_r_mr (insn, operands, real_l); /* mov r,m */ | |
2782 | } | |
2783 | else if (GET_CODE (dest) == MEM) | |
2784 | { | |
0a2aaacc | 2785 | const char *templ; |
5fecfd8d MM |
2786 | |
2787 | if (src == const0_rtx) | |
6bec29c9 | 2788 | operands[1] = zero_reg_rtx; |
5fecfd8d | 2789 | |
0a2aaacc | 2790 | templ = out_movsi_mr_r (insn, operands, real_l); |
6bec29c9 DC |
2791 | |
2792 | if (!real_l) | |
0a2aaacc | 2793 | output_asm_insn (templ, operands); |
5fecfd8d MM |
2794 | |
2795 | operands[1] = src; | |
6bec29c9 | 2796 | return ""; |
90e7678c | 2797 | } |
c725bd79 | 2798 | fatal_insn ("invalid insn:", insn); |
90e7678c DC |
2799 | return ""; |
2800 | } | |
2801 | ||
5fecfd8d | 2802 | const char * |
269e3795 | 2803 | out_movqi_mr_r (rtx insn, rtx op[], int *l) |
90e7678c | 2804 | { |
5fecfd8d MM |
2805 | rtx dest = op[0]; |
2806 | rtx src = op[1]; | |
2807 | rtx x = XEXP (dest, 0); | |
6bec29c9 | 2808 | int dummy; |
90e7678c | 2809 | |
6bec29c9 DC |
2810 | if (!l) |
2811 | l = &dummy; | |
6bec29c9 DC |
2812 | |
2813 | if (CONSTANT_ADDRESS_P (x)) | |
90e7678c | 2814 | { |
4fc2b4ff AH |
2815 | if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR) |
2816 | { | |
2817 | *l = 1; | |
2818 | return AS2 (out,__SREG__,%1); | |
2819 | } | |
e6b69d0e | 2820 | if (optimize > 0 && io_address_operand (x, QImode)) |
6bec29c9 DC |
2821 | { |
2822 | *l = 1; | |
846428f1 | 2823 | return AS2 (out,%m0-0x20,%1); |
6bec29c9 DC |
2824 | } |
2825 | *l = 2; | |
846428f1 | 2826 | return AS2 (sts,%m0,%1); |
6bec29c9 DC |
2827 | } |
2828 | /* memory access by reg+disp */ | |
2829 | else if (GET_CODE (x) == PLUS | |
2830 | && REG_P (XEXP (x,0)) | |
2831 | && GET_CODE (XEXP (x,1)) == CONST_INT) | |
2832 | { | |
5fecfd8d | 2833 | if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63) |
90e7678c | 2834 | { |
6bec29c9 DC |
2835 | int disp = INTVAL (XEXP (x,1)); |
2836 | if (REGNO (XEXP (x,0)) != REG_Y) | |
c725bd79 | 2837 | fatal_insn ("incorrect insn:",insn); |
33d5e2dc | 2838 | |
5fecfd8d | 2839 | if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) |
33d5e2dc MM |
2840 | return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB |
2841 | AS2 (std,Y+63,%1) CR_TAB | |
2842 | AS2 (sbiw,r28,%o0-63)); | |
2843 | ||
2844 | return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB | |
2845 | AS2 (sbci,r29,hi8(-%o0)) CR_TAB | |
2846 | AS2 (st,Y,%1) CR_TAB | |
2847 | AS2 (subi,r28,lo8(%o0)) CR_TAB | |
2848 | AS2 (sbci,r29,hi8(%o0))); | |
6bec29c9 DC |
2849 | } |
2850 | else if (REGNO (XEXP (x,0)) == REG_X) | |
2851 | { | |
5fecfd8d | 2852 | if (reg_overlap_mentioned_p (src, XEXP (x, 0))) |
6bec29c9 DC |
2853 | { |
2854 | if (reg_unused_after (insn, XEXP (x,0))) | |
33d5e2dc MM |
2855 | return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB |
2856 | AS2 (adiw,r26,%o0) CR_TAB | |
2857 | AS2 (st,X,__tmp_reg__)); | |
2858 | ||
2859 | return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB | |
2860 | AS2 (adiw,r26,%o0) CR_TAB | |
2861 | AS2 (st,X,__tmp_reg__) CR_TAB | |
2862 | AS2 (sbiw,r26,%o0)); | |
90e7678c | 2863 | } |
6bec29c9 | 2864 | else |
90e7678c | 2865 | { |
6bec29c9 | 2866 | if (reg_unused_after (insn, XEXP (x,0))) |
33d5e2dc MM |
2867 | return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB |
2868 | AS2 (st,X,%1)); | |
2869 | ||
2870 | return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB | |
2871 | AS2 (st,X,%1) CR_TAB | |
2872 | AS2 (sbiw,r26,%o0)); | |
90e7678c DC |
2873 | } |
2874 | } | |
6bec29c9 DC |
2875 | *l = 1; |
2876 | return AS2 (std,%0,%1); | |
90e7678c | 2877 | } |
6bec29c9 DC |
2878 | *l = 1; |
2879 | return AS2 (st,%0,%1); | |
90e7678c DC |
2880 | } |
2881 | ||
5fecfd8d | 2882 | const char * |
269e3795 | 2883 | out_movhi_mr_r (rtx insn, rtx op[], int *l) |
90e7678c | 2884 | { |
5fecfd8d MM |
2885 | rtx dest = op[0]; |
2886 | rtx src = op[1]; | |
2887 | rtx base = XEXP (dest, 0); | |
2888 | int reg_base = true_regnum (base); | |
2889 | int reg_src = true_regnum (src); | |
a6990185 MM |
2890 | /* "volatile" forces writing high byte first, even if less efficient, |
2891 | for correct operation with 16-bit I/O registers. */ | |
2892 | int mem_volatile_p = MEM_VOLATILE_P (dest); | |
90e7678c | 2893 | int tmp; |
a6990185 | 2894 | |
90e7678c DC |
2895 | if (!l) |
2896 | l = &tmp; | |
5fecfd8d MM |
2897 | if (CONSTANT_ADDRESS_P (base)) |
2898 | { | |
e6b69d0e | 2899 | if (optimize > 0 && io_address_operand (base, HImode)) |
5fecfd8d MM |
2900 | { |
2901 | *l = 2; | |
846428f1 AH |
2902 | return (AS2 (out,%m0+1-0x20,%B1) CR_TAB |
2903 | AS2 (out,%m0-0x20,%A1)); | |
5fecfd8d | 2904 | } |
846428f1 AH |
2905 | return *l = 4, (AS2 (sts,%m0+1,%B1) CR_TAB |
2906 | AS2 (sts,%m0,%A1)); | |
5fecfd8d | 2907 | } |
90e7678c DC |
2908 | if (reg_base > 0) |
2909 | { | |
2910 | if (reg_base == REG_X) | |
2911 | { | |
5fecfd8d | 2912 | if (reg_src == REG_X) |
90e7678c | 2913 | { |
a6990185 MM |
2914 | /* "st X+,r26" and "st -X,r26" are undefined. */ |
2915 | if (!mem_volatile_p && reg_unused_after (insn, src)) | |
dfaf5abf MM |
2916 | return *l=4, (AS2 (mov,__tmp_reg__,r27) CR_TAB |
2917 | AS2 (st,X,r26) CR_TAB | |
2918 | AS2 (adiw,r26,1) CR_TAB | |
2919 | AS2 (st,X,__tmp_reg__)); | |
90e7678c | 2920 | else |
dfaf5abf | 2921 | return *l=5, (AS2 (mov,__tmp_reg__,r27) CR_TAB |
dfaf5abf MM |
2922 | AS2 (adiw,r26,1) CR_TAB |
2923 | AS2 (st,X,__tmp_reg__) CR_TAB | |
a6990185 MM |
2924 | AS2 (sbiw,r26,1) CR_TAB |
2925 | AS2 (st,X,r26)); | |
90e7678c DC |
2926 | } |
2927 | else | |
2928 | { | |
a6990185 | 2929 | if (!mem_volatile_p && reg_unused_after (insn, base)) |
90e7678c DC |
2930 | return *l=2, (AS2 (st,X+,%A1) CR_TAB |
2931 | AS2 (st,X,%B1)); | |
2932 | else | |
a6990185 MM |
2933 | return *l=3, (AS2 (adiw,r26,1) CR_TAB |
2934 | AS2 (st,X,%B1) CR_TAB | |
2935 | AS2 (st,-X,%A1)); | |
90e7678c DC |
2936 | } |
2937 | } | |
2938 | else | |
a6990185 MM |
2939 | return *l=2, (AS2 (std,%0+1,%B1) CR_TAB |
2940 | AS2 (st,%0,%A1)); | |
90e7678c | 2941 | } |
5fecfd8d | 2942 | else if (GET_CODE (base) == PLUS) |
90e7678c | 2943 | { |
5fecfd8d | 2944 | int disp = INTVAL (XEXP (base, 1)); |
dfaf5abf | 2945 | reg_base = REGNO (XEXP (base, 0)); |
5fecfd8d | 2946 | if (disp > MAX_LD_OFFSET (GET_MODE (dest))) |
90e7678c | 2947 | { |
dfaf5abf | 2948 | if (reg_base != REG_Y) |
c725bd79 | 2949 | fatal_insn ("incorrect insn:",insn); |
33d5e2dc | 2950 | |
5fecfd8d | 2951 | if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) |
33d5e2dc | 2952 | return *l = 4, (AS2 (adiw,r28,%o0-62) CR_TAB |
33d5e2dc | 2953 | AS2 (std,Y+63,%B1) CR_TAB |
a6990185 | 2954 | AS2 (std,Y+62,%A1) CR_TAB |
33d5e2dc MM |
2955 | AS2 (sbiw,r28,%o0-62)); |
2956 | ||
2957 | return *l = 6, (AS2 (subi,r28,lo8(-%o0)) CR_TAB | |
2958 | AS2 (sbci,r29,hi8(-%o0)) CR_TAB | |
33d5e2dc | 2959 | AS2 (std,Y+1,%B1) CR_TAB |
a6990185 | 2960 | AS2 (st,Y,%A1) CR_TAB |
33d5e2dc MM |
2961 | AS2 (subi,r28,lo8(%o0)) CR_TAB |
2962 | AS2 (sbci,r29,hi8(%o0))); | |
90e7678c | 2963 | } |
dfaf5abf MM |
2964 | if (reg_base == REG_X) |
2965 | { | |
2966 | /* (X + d) = R */ | |
2967 | if (reg_src == REG_X) | |
a6990185 | 2968 | { |
dfaf5abf MM |
2969 | *l = 7; |
2970 | return (AS2 (mov,__tmp_reg__,r26) CR_TAB | |
2971 | AS2 (mov,__zero_reg__,r27) CR_TAB | |
a6990185 | 2972 | AS2 (adiw,r26,%o0+1) CR_TAB |
dfaf5abf | 2973 | AS2 (st,X,__zero_reg__) CR_TAB |
a6990185 | 2974 | AS2 (st,-X,__tmp_reg__) CR_TAB |
dfaf5abf | 2975 | AS1 (clr,__zero_reg__) CR_TAB |
a6990185 | 2976 | AS2 (sbiw,r26,%o0)); |
dfaf5abf MM |
2977 | } |
2978 | *l = 4; | |
a6990185 MM |
2979 | return (AS2 (adiw,r26,%o0+1) CR_TAB |
2980 | AS2 (st,X,%B1) CR_TAB | |
2981 | AS2 (st,-X,%A1) CR_TAB | |
2982 | AS2 (sbiw,r26,%o0)); | |
dfaf5abf | 2983 | } |
a6990185 MM |
2984 | return *l=2, (AS2 (std,%B0,%B1) CR_TAB |
2985 | AS2 (std,%A0,%A1)); | |
6bec29c9 | 2986 | } |
5fecfd8d | 2987 | else if (GET_CODE (base) == PRE_DEC) /* (--R) */ |
90e7678c DC |
2988 | return *l=2, (AS2 (st,%0,%B1) CR_TAB |
2989 | AS2 (st,%0,%A1)); | |
5fecfd8d | 2990 | else if (GET_CODE (base) == POST_INC) /* (R++) */ |
a6990185 MM |
2991 | { |
2992 | if (mem_volatile_p) | |
2993 | { | |
2994 | if (REGNO (XEXP (base, 0)) == REG_X) | |
2995 | { | |
2996 | *l = 4; | |
2997 | return (AS2 (adiw,r26,1) CR_TAB | |
2998 | AS2 (st,X,%B1) CR_TAB | |
2999 | AS2 (st,-X,%A1) CR_TAB | |
3000 | AS2 (adiw,r26,2)); | |
3001 | } | |
3002 | else | |
3003 | { | |
3004 | *l = 3; | |
3005 | return (AS2 (std,%p0+1,%B1) CR_TAB | |
3006 | AS2 (st,%p0,%A1) CR_TAB | |
3007 | AS2 (adiw,%r0,2)); | |
3008 | } | |
3009 | } | |
3010 | ||
3011 | *l = 2; | |
3012 | return (AS2 (st,%0,%A1) CR_TAB | |
3013 | AS2 (st,%0,%B1)); | |
3014 | } | |
c725bd79 | 3015 | fatal_insn ("unknown move insn:",insn); |
90e7678c DC |
3016 | return ""; |
3017 | } | |
3018 | ||
2c338472 | 3019 | /* Return 1 if frame pointer for current function required. */ |
90e7678c | 3020 | |
c21ca196 AS |
3021 | bool |
3022 | avr_frame_pointer_required_p (void) | |
90e7678c | 3023 | { |
e3b5732b | 3024 | return (cfun->calls_alloca |
38173d38 | 3025 | || crtl->args.info.nregs == 0 |
90e7678c DC |
3026 | || get_frame_size () > 0); |
3027 | } | |
3028 | ||
89741abb | 3029 | /* Returns the condition of compare insn INSN, or UNKNOWN. */ |
90e7678c | 3030 | |
89741abb | 3031 | static RTX_CODE |
269e3795 | 3032 | compare_condition (rtx insn) |
90e7678c DC |
3033 | { |
3034 | rtx next = next_real_insn (insn); | |
3035 | RTX_CODE cond = UNKNOWN; | |
89741abb | 3036 | if (next && GET_CODE (next) == JUMP_INSN) |
90e7678c DC |
3037 | { |
3038 | rtx pat = PATTERN (next); | |
3039 | rtx src = SET_SRC (pat); | |
89741abb | 3040 | rtx t = XEXP (src, 0); |
90e7678c DC |
3041 | cond = GET_CODE (t); |
3042 | } | |
89741abb MM |
3043 | return cond; |
3044 | } | |
3045 | ||
3046 | /* Returns nonzero if INSN is a tst insn that only tests the sign. */ | |
3047 | ||
3048 | static int | |
269e3795 | 3049 | compare_sign_p (rtx insn) |
89741abb MM |
3050 | { |
3051 | RTX_CODE cond = compare_condition (insn); | |
3052 | return (cond == GE || cond == LT); | |
3053 | } | |
3054 | ||
3055 | /* Returns nonzero if the next insn is a JUMP_INSN with a condition | |
3056 | that needs to be swapped (GT, GTU, LE, LEU). */ | |
3057 | ||
3058 | int | |
269e3795 | 3059 | compare_diff_p (rtx insn) |
89741abb MM |
3060 | { |
3061 | RTX_CODE cond = compare_condition (insn); | |
90e7678c DC |
3062 | return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0; |
3063 | } | |
3064 | ||
89741abb | 3065 | /* Returns nonzero if INSN is a compare insn with the EQ or NE condition. */ |
90e7678c DC |
3066 | |
3067 | int | |
269e3795 | 3068 | compare_eq_p (rtx insn) |
90e7678c | 3069 | { |
89741abb | 3070 | RTX_CODE cond = compare_condition (insn); |
90e7678c DC |
3071 | return (cond == EQ || cond == NE); |
3072 | } | |
3073 | ||
3074 | ||
2c338472 | 3075 | /* Output test instruction for HImode. */ |
90e7678c | 3076 | |
5fecfd8d | 3077 | const char * |
f90b7a5a | 3078 | out_tsthi (rtx insn, rtx op, int *l) |
90e7678c | 3079 | { |
89741abb | 3080 | if (compare_sign_p (insn)) |
90e7678c DC |
3081 | { |
3082 | if (l) *l = 1; | |
3083 | return AS1 (tst,%B0); | |
3084 | } | |
f90b7a5a | 3085 | if (reg_unused_after (insn, op) |
89741abb | 3086 | && compare_eq_p (insn)) |
90e7678c | 3087 | { |
2c338472 | 3088 | /* Faster than sbiw if we can clobber the operand. */ |
90e7678c | 3089 | if (l) *l = 1; |
5a82ecd9 | 3090 | return "or %A0,%B0"; |
90e7678c | 3091 | } |
f90b7a5a | 3092 | if (test_hard_reg_class (ADDW_REGS, op)) |
90e7678c DC |
3093 | { |
3094 | if (l) *l = 1; | |
5fecfd8d | 3095 | return AS2 (sbiw,%0,0); |
90e7678c DC |
3096 | } |
3097 | if (l) *l = 2; | |
3098 | return (AS2 (cp,%A0,__zero_reg__) CR_TAB | |
3099 | AS2 (cpc,%B0,__zero_reg__)); | |
3100 | } | |
3101 | ||
3102 | ||
2c338472 | 3103 | /* Output test instruction for SImode. */ |
90e7678c | 3104 | |
5fecfd8d | 3105 | const char * |
f90b7a5a | 3106 | out_tstsi (rtx insn, rtx op, int *l) |
90e7678c | 3107 | { |
89741abb | 3108 | if (compare_sign_p (insn)) |
90e7678c DC |
3109 | { |
3110 | if (l) *l = 1; | |
3111 | return AS1 (tst,%D0); | |
3112 | } | |
f90b7a5a | 3113 | if (test_hard_reg_class (ADDW_REGS, op)) |
90e7678c DC |
3114 | { |
3115 | if (l) *l = 3; | |
3116 | return (AS2 (sbiw,%A0,0) CR_TAB | |
3117 | AS2 (cpc,%C0,__zero_reg__) CR_TAB | |
3118 | AS2 (cpc,%D0,__zero_reg__)); | |
3119 | } | |
3120 | if (l) *l = 4; | |
3121 | return (AS2 (cp,%A0,__zero_reg__) CR_TAB | |
3122 | AS2 (cpc,%B0,__zero_reg__) CR_TAB | |
3123 | AS2 (cpc,%C0,__zero_reg__) CR_TAB | |
3124 | AS2 (cpc,%D0,__zero_reg__)); | |
3125 | } | |
3126 | ||
3127 | ||
afee2a52 MM |
3128 | /* Generate asm equivalent for various shifts. |
3129 | Shift count is a CONST_INT, MEM or REG. | |
3130 | This only handles cases that are not already | |
3131 | carefully hand-optimized in ?sh??i3_out. */ | |
90e7678c DC |
3132 | |
3133 | void | |
0a2aaacc | 3134 | out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], |
269e3795 | 3135 | int *len, int t_len) |
90e7678c DC |
3136 | { |
3137 | rtx op[10]; | |
afee2a52 | 3138 | char str[500]; |
0db7ad3a | 3139 | int second_label = 1; |
afee2a52 MM |
3140 | int saved_in_tmp = 0; |
3141 | int use_zero_reg = 0; | |
3142 | ||
90e7678c DC |
3143 | op[0] = operands[0]; |
3144 | op[1] = operands[1]; | |
3145 | op[2] = operands[2]; | |
3146 | op[3] = operands[3]; | |
3147 | str[0] = 0; | |
afee2a52 MM |
3148 | |
3149 | if (len) | |
3150 | *len = 1; | |
3151 | ||
3152 | if (GET_CODE (operands[2]) == CONST_INT) | |
90e7678c | 3153 | { |
afee2a52 MM |
3154 | int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); |
3155 | int count = INTVAL (operands[2]); | |
3156 | int max_len = 10; /* If larger than this, always use a loop. */ | |
3157 | ||
a3cf5992 MM |
3158 | if (count <= 0) |
3159 | { | |
3160 | if (len) | |
3161 | *len = 0; | |
3162 | return; | |
3163 | } | |
3164 | ||
afee2a52 MM |
3165 | if (count < 8 && !scratch) |
3166 | use_zero_reg = 1; | |
3167 | ||
3168 | if (optimize_size) | |
3169 | max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5)); | |
3170 | ||
3171 | if (t_len * count <= max_len) | |
3172 | { | |
3173 | /* Output shifts inline with no loop - faster. */ | |
3174 | if (len) | |
3175 | *len = t_len * count; | |
3176 | else | |
3177 | { | |
3178 | while (count-- > 0) | |
0a2aaacc | 3179 | output_asm_insn (templ, op); |
afee2a52 MM |
3180 | } |
3181 | ||
3182 | return; | |
3183 | } | |
3184 | ||
3185 | if (scratch) | |
3186 | { | |
3187 | if (!len) | |
3188 | strcat (str, AS2 (ldi,%3,%2)); | |
3189 | } | |
3190 | else if (use_zero_reg) | |
3191 | { | |
3192 | /* Hack to save one word: use __zero_reg__ as loop counter. | |
3193 | Set one bit, then shift in a loop until it is 0 again. */ | |
3194 | ||
3195 | op[3] = zero_reg_rtx; | |
3196 | if (len) | |
3197 | *len = 2; | |
3198 | else | |
3199 | strcat (str, ("set" CR_TAB | |
3200 | AS2 (bld,%3,%2-1))); | |
3201 | } | |
90e7678c | 3202 | else |
afee2a52 MM |
3203 | { |
3204 | /* No scratch register available, use one from LD_REGS (saved in | |
3205 | __tmp_reg__) that doesn't overlap with registers to shift. */ | |
3206 | ||
f1c25d3b | 3207 | op[3] = gen_rtx_REG (QImode, |
afee2a52 MM |
3208 | ((true_regnum (operands[0]) - 1) & 15) + 16); |
3209 | op[4] = tmp_reg_rtx; | |
3210 | saved_in_tmp = 1; | |
3211 | ||
3212 | if (len) | |
3213 | *len = 3; /* Includes "mov %3,%4" after the loop. */ | |
3214 | else | |
3215 | strcat (str, (AS2 (mov,%4,%3) CR_TAB | |
3216 | AS2 (ldi,%3,%2))); | |
3217 | } | |
3218 | ||
0db7ad3a | 3219 | second_label = 0; |
90e7678c DC |
3220 | } |
3221 | else if (GET_CODE (operands[2]) == MEM) | |
3222 | { | |
90e7678c | 3223 | rtx op_mov[10]; |
0db7ad3a | 3224 | |
90e7678c DC |
3225 | op[3] = op_mov[0] = tmp_reg_rtx; |
3226 | op_mov[1] = op[2]; | |
afee2a52 MM |
3227 | |
3228 | if (len) | |
3229 | out_movqi_r_mr (insn, op_mov, len); | |
90e7678c | 3230 | else |
afee2a52 | 3231 | output_asm_insn (out_movqi_r_mr (insn, op_mov, NULL), op_mov); |
90e7678c | 3232 | } |
c4984bad | 3233 | else if (register_operand (operands[2], QImode)) |
90e7678c | 3234 | { |
90e7678c | 3235 | if (reg_unused_after (insn, operands[2])) |
0db7ad3a | 3236 | op[3] = op[2]; |
90e7678c DC |
3237 | else |
3238 | { | |
3239 | op[3] = tmp_reg_rtx; | |
afee2a52 MM |
3240 | if (!len) |
3241 | strcat (str, (AS2 (mov,%3,%2) CR_TAB)); | |
90e7678c | 3242 | } |
afee2a52 MM |
3243 | } |
3244 | else | |
c725bd79 | 3245 | fatal_insn ("bad shift insn:", insn); |
afee2a52 MM |
3246 | |
3247 | if (second_label) | |
3248 | { | |
0db7ad3a DC |
3249 | if (len) |
3250 | ++*len; | |
3251 | else | |
3252 | strcat (str, AS1 (rjmp,2f)); | |
90e7678c | 3253 | } |
afee2a52 MM |
3254 | |
3255 | if (len) | |
3256 | *len += t_len + 2; /* template + dec + brXX */ | |
3257 | else | |
90e7678c | 3258 | { |
80b8585d | 3259 | strcat (str, "\n1:\t"); |
0a2aaacc | 3260 | strcat (str, templ); |
0db7ad3a | 3261 | strcat (str, second_label ? "\n2:\t" : "\n\t"); |
afee2a52 MM |
3262 | strcat (str, use_zero_reg ? AS1 (lsr,%3) : AS1 (dec,%3)); |
3263 | strcat (str, CR_TAB); | |
3264 | strcat (str, second_label ? AS1 (brpl,1b) : AS1 (brne,1b)); | |
3265 | if (saved_in_tmp) | |
3266 | strcat (str, (CR_TAB AS2 (mov,%3,%4))); | |
90e7678c DC |
3267 | output_asm_insn (str, op); |
3268 | } | |
3269 | } | |
3270 | ||
3271 | ||
3272 | /* 8bit shift left ((char)x << i) */ | |
3273 | ||
5fecfd8d | 3274 | const char * |
269e3795 | 3275 | ashlqi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
3276 | { |
3277 | if (GET_CODE (operands[2]) == CONST_INT) | |
3278 | { | |
3279 | int k; | |
3454eb73 | 3280 | |
90e7678c DC |
3281 | if (!len) |
3282 | len = &k; | |
3454eb73 | 3283 | |
90e7678c DC |
3284 | switch (INTVAL (operands[2])) |
3285 | { | |
3454eb73 | 3286 | default: |
a3cf5992 MM |
3287 | if (INTVAL (operands[2]) < 8) |
3288 | break; | |
3289 | ||
3454eb73 DC |
3290 | *len = 1; |
3291 | return AS1 (clr,%0); | |
3292 | ||
90e7678c | 3293 | case 1: |
3454eb73 | 3294 | *len = 1; |
90e7678c | 3295 | return AS1 (lsl,%0); |
3454eb73 | 3296 | |
90e7678c | 3297 | case 2: |
3454eb73 | 3298 | *len = 2; |
90e7678c DC |
3299 | return (AS1 (lsl,%0) CR_TAB |
3300 | AS1 (lsl,%0)); | |
3454eb73 | 3301 | |
90e7678c | 3302 | case 3: |
3454eb73 | 3303 | *len = 3; |
90e7678c DC |
3304 | return (AS1 (lsl,%0) CR_TAB |
3305 | AS1 (lsl,%0) CR_TAB | |
3306 | AS1 (lsl,%0)); | |
3454eb73 | 3307 | |
90e7678c | 3308 | case 4: |
c4984bad | 3309 | if (test_hard_reg_class (LD_REGS, operands[0])) |
90e7678c | 3310 | { |
3454eb73 | 3311 | *len = 2; |
90e7678c DC |
3312 | return (AS1 (swap,%0) CR_TAB |
3313 | AS2 (andi,%0,0xf0)); | |
3314 | } | |
3454eb73 | 3315 | *len = 4; |
90e7678c DC |
3316 | return (AS1 (lsl,%0) CR_TAB |
3317 | AS1 (lsl,%0) CR_TAB | |
3318 | AS1 (lsl,%0) CR_TAB | |
3319 | AS1 (lsl,%0)); | |
3454eb73 | 3320 | |
90e7678c | 3321 | case 5: |
c4984bad | 3322 | if (test_hard_reg_class (LD_REGS, operands[0])) |
90e7678c | 3323 | { |
3454eb73 | 3324 | *len = 3; |
90e7678c DC |
3325 | return (AS1 (swap,%0) CR_TAB |
3326 | AS1 (lsl,%0) CR_TAB | |
3327 | AS2 (andi,%0,0xe0)); | |
3328 | } | |
3454eb73 | 3329 | *len = 5; |
90e7678c DC |
3330 | return (AS1 (lsl,%0) CR_TAB |
3331 | AS1 (lsl,%0) CR_TAB | |
3332 | AS1 (lsl,%0) CR_TAB | |
3333 | AS1 (lsl,%0) CR_TAB | |
3334 | AS1 (lsl,%0)); | |
3454eb73 | 3335 | |
90e7678c | 3336 | case 6: |
c4984bad | 3337 | if (test_hard_reg_class (LD_REGS, operands[0])) |
90e7678c | 3338 | { |
3454eb73 | 3339 | *len = 4; |
90e7678c DC |
3340 | return (AS1 (swap,%0) CR_TAB |
3341 | AS1 (lsl,%0) CR_TAB | |
3342 | AS1 (lsl,%0) CR_TAB | |
3343 | AS2 (andi,%0,0xc0)); | |
3344 | } | |
3454eb73 | 3345 | *len = 6; |
90e7678c DC |
3346 | return (AS1 (lsl,%0) CR_TAB |
3347 | AS1 (lsl,%0) CR_TAB | |
3348 | AS1 (lsl,%0) CR_TAB | |
3349 | AS1 (lsl,%0) CR_TAB | |
3350 | AS1 (lsl,%0) CR_TAB | |
3351 | AS1 (lsl,%0)); | |
3454eb73 | 3352 | |
90e7678c | 3353 | case 7: |
3454eb73 | 3354 | *len = 3; |
90e7678c DC |
3355 | return (AS1 (ror,%0) CR_TAB |
3356 | AS1 (clr,%0) CR_TAB | |
3357 | AS1 (ror,%0)); | |
3358 | } | |
3359 | } | |
3454eb73 | 3360 | else if (CONSTANT_P (operands[2])) |
c725bd79 | 3361 | fatal_insn ("internal compiler error. Incorrect shift:", insn); |
3454eb73 | 3362 | |
0db7ad3a | 3363 | out_shift_with_cnt (AS1 (lsl,%0), |
afee2a52 | 3364 | insn, operands, len, 1); |
90e7678c DC |
3365 | return ""; |
3366 | } | |
3367 | ||
3368 | ||
3369 | /* 16bit shift left ((short)x << i) */ | |
3370 | ||
5fecfd8d | 3371 | const char * |
269e3795 | 3372 | ashlhi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
3373 | { |
3374 | if (GET_CODE (operands[2]) == CONST_INT) | |
3375 | { | |
afee2a52 MM |
3376 | int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); |
3377 | int ldi_ok = test_hard_reg_class (LD_REGS, operands[0]); | |
90e7678c | 3378 | int k; |
afee2a52 | 3379 | int *t = len; |
3454eb73 | 3380 | |
90e7678c DC |
3381 | if (!len) |
3382 | len = &k; | |
3454eb73 | 3383 | |
90e7678c DC |
3384 | switch (INTVAL (operands[2])) |
3385 | { | |
a3cf5992 MM |
3386 | default: |
3387 | if (INTVAL (operands[2]) < 16) | |
3388 | break; | |
3389 | ||
3390 | *len = 2; | |
3391 | return (AS1 (clr,%B0) CR_TAB | |
3392 | AS1 (clr,%A0)); | |
3393 | ||
afee2a52 MM |
3394 | case 4: |
3395 | if (optimize_size && scratch) | |
3396 | break; /* 5 */ | |
3397 | if (ldi_ok) | |
3398 | { | |
3399 | *len = 6; | |
3400 | return (AS1 (swap,%A0) CR_TAB | |
3401 | AS1 (swap,%B0) CR_TAB | |
3402 | AS2 (andi,%B0,0xf0) CR_TAB | |
3403 | AS2 (eor,%B0,%A0) CR_TAB | |
3404 | AS2 (andi,%A0,0xf0) CR_TAB | |
3405 | AS2 (eor,%B0,%A0)); | |
3406 | } | |
3407 | if (scratch) | |
3408 | { | |
3409 | *len = 7; | |
3410 | return (AS1 (swap,%A0) CR_TAB | |
3411 | AS1 (swap,%B0) CR_TAB | |
3412 | AS2 (ldi,%3,0xf0) CR_TAB | |
5a82ecd9 | 3413 | "and %B0,%3" CR_TAB |
afee2a52 | 3414 | AS2 (eor,%B0,%A0) CR_TAB |
5a82ecd9 | 3415 | "and %A0,%3" CR_TAB |
afee2a52 MM |
3416 | AS2 (eor,%B0,%A0)); |
3417 | } | |
3418 | break; /* optimize_size ? 6 : 8 */ | |
3454eb73 | 3419 | |
afee2a52 MM |
3420 | case 5: |
3421 | if (optimize_size) | |
3422 | break; /* scratch ? 5 : 6 */ | |
3423 | if (ldi_ok) | |
3424 | { | |
3425 | *len = 8; | |
3426 | return (AS1 (lsl,%A0) CR_TAB | |
3427 | AS1 (rol,%B0) CR_TAB | |
3428 | AS1 (swap,%A0) CR_TAB | |
3429 | AS1 (swap,%B0) CR_TAB | |
3430 | AS2 (andi,%B0,0xf0) CR_TAB | |
3431 | AS2 (eor,%B0,%A0) CR_TAB | |
3432 | AS2 (andi,%A0,0xf0) CR_TAB | |
3433 | AS2 (eor,%B0,%A0)); | |
3434 | } | |
3435 | if (scratch) | |
3436 | { | |
3437 | *len = 9; | |
3438 | return (AS1 (lsl,%A0) CR_TAB | |
3439 | AS1 (rol,%B0) CR_TAB | |
3440 | AS1 (swap,%A0) CR_TAB | |
3441 | AS1 (swap,%B0) CR_TAB | |
3442 | AS2 (ldi,%3,0xf0) CR_TAB | |
5a82ecd9 | 3443 | "and %B0,%3" CR_TAB |
afee2a52 | 3444 | AS2 (eor,%B0,%A0) CR_TAB |
5a82ecd9 | 3445 | "and %A0,%3" CR_TAB |
afee2a52 MM |
3446 | AS2 (eor,%B0,%A0)); |
3447 | } | |
3448 | break; /* 10 */ | |
3449 | ||
3450 | case 6: | |
3451 | if (optimize_size) | |
3452 | break; /* scratch ? 5 : 6 */ | |
3453 | *len = 9; | |
3454 | return (AS1 (clr,__tmp_reg__) CR_TAB | |
3455 | AS1 (lsr,%B0) CR_TAB | |
3456 | AS1 (ror,%A0) CR_TAB | |
3457 | AS1 (ror,__tmp_reg__) CR_TAB | |
3458 | AS1 (lsr,%B0) CR_TAB | |
3459 | AS1 (ror,%A0) CR_TAB | |
3460 | AS1 (ror,__tmp_reg__) CR_TAB | |
3461 | AS2 (mov,%B0,%A0) CR_TAB | |
3462 | AS2 (mov,%A0,__tmp_reg__)); | |
3454eb73 | 3463 | |
adcd8f77 MM |
3464 | case 7: |
3465 | *len = 5; | |
3466 | return (AS1 (lsr,%B0) CR_TAB | |
3467 | AS2 (mov,%B0,%A0) CR_TAB | |
3468 | AS1 (clr,%A0) CR_TAB | |
3469 | AS1 (ror,%B0) CR_TAB | |
3470 | AS1 (ror,%A0)); | |
3471 | ||
90e7678c | 3472 | case 8: |
120b24f7 AS |
3473 | return *len = 2, (AS2 (mov,%B0,%A1) CR_TAB |
3474 | AS1 (clr,%A0)); | |
adcd8f77 MM |
3475 | |
3476 | case 9: | |
3477 | *len = 3; | |
3478 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3479 | AS1 (clr,%A0) CR_TAB | |
3480 | AS1 (lsl,%B0)); | |
3481 | ||
3482 | case 10: | |
3483 | *len = 4; | |
3484 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3485 | AS1 (clr,%A0) CR_TAB | |
3486 | AS1 (lsl,%B0) CR_TAB | |
3487 | AS1 (lsl,%B0)); | |
3488 | ||
3489 | case 11: | |
3490 | *len = 5; | |
3491 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3492 | AS1 (clr,%A0) CR_TAB | |
3493 | AS1 (lsl,%B0) CR_TAB | |
3494 | AS1 (lsl,%B0) CR_TAB | |
3495 | AS1 (lsl,%B0)); | |
3496 | ||
3497 | case 12: | |
afee2a52 | 3498 | if (ldi_ok) |
adcd8f77 MM |
3499 | { |
3500 | *len = 4; | |
3501 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3502 | AS1 (clr,%A0) CR_TAB | |
3503 | AS1 (swap,%B0) CR_TAB | |
3504 | AS2 (andi,%B0,0xf0)); | |
3505 | } | |
afee2a52 MM |
3506 | if (scratch) |
3507 | { | |
3508 | *len = 5; | |
3509 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3510 | AS1 (clr,%A0) CR_TAB | |
3511 | AS1 (swap,%B0) CR_TAB | |
3512 | AS2 (ldi,%3,0xf0) CR_TAB | |
5a82ecd9 | 3513 | "and %B0,%3"); |
afee2a52 MM |
3514 | } |
3515 | *len = 6; | |
adcd8f77 MM |
3516 | return (AS2 (mov,%B0,%A0) CR_TAB |
3517 | AS1 (clr,%A0) CR_TAB | |
afee2a52 MM |
3518 | AS1 (lsl,%B0) CR_TAB |
3519 | AS1 (lsl,%B0) CR_TAB | |
3520 | AS1 (lsl,%B0) CR_TAB | |
3521 | AS1 (lsl,%B0)); | |
adcd8f77 MM |
3522 | |
3523 | case 13: | |
afee2a52 | 3524 | if (ldi_ok) |
adcd8f77 MM |
3525 | { |
3526 | *len = 5; | |
3527 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3528 | AS1 (clr,%A0) CR_TAB | |
3529 | AS1 (swap,%B0) CR_TAB | |
3530 | AS1 (lsl,%B0) CR_TAB | |
3531 | AS2 (andi,%B0,0xe0)); | |
3532 | } | |
dd6d1f8c | 3533 | if (AVR_HAVE_MUL && scratch) |
adcd8f77 MM |
3534 | { |
3535 | *len = 5; | |
3536 | return (AS2 (ldi,%3,0x20) CR_TAB | |
3537 | AS2 (mul,%A0,%3) CR_TAB | |
3538 | AS2 (mov,%B0,r0) CR_TAB | |
3539 | AS1 (clr,%A0) CR_TAB | |
3540 | AS1 (clr,__zero_reg__)); | |
3541 | } | |
afee2a52 MM |
3542 | if (optimize_size && scratch) |
3543 | break; /* 5 */ | |
3544 | if (scratch) | |
3545 | { | |
3546 | *len = 6; | |
3547 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3548 | AS1 (clr,%A0) CR_TAB | |
3549 | AS1 (swap,%B0) CR_TAB | |
3550 | AS1 (lsl,%B0) CR_TAB | |
3551 | AS2 (ldi,%3,0xe0) CR_TAB | |
5a82ecd9 | 3552 | "and %B0,%3"); |
afee2a52 | 3553 | } |
dd6d1f8c | 3554 | if (AVR_HAVE_MUL) |
afee2a52 MM |
3555 | { |
3556 | *len = 6; | |
3557 | return ("set" CR_TAB | |
3558 | AS2 (bld,r1,5) CR_TAB | |
3559 | AS2 (mul,%A0,r1) CR_TAB | |
3560 | AS2 (mov,%B0,r0) CR_TAB | |
3561 | AS1 (clr,%A0) CR_TAB | |
3562 | AS1 (clr,__zero_reg__)); | |
3563 | } | |
3564 | *len = 7; | |
3565 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3566 | AS1 (clr,%A0) CR_TAB | |
3567 | AS1 (lsl,%B0) CR_TAB | |
3568 | AS1 (lsl,%B0) CR_TAB | |
3569 | AS1 (lsl,%B0) CR_TAB | |
3570 | AS1 (lsl,%B0) CR_TAB | |
3571 | AS1 (lsl,%B0)); | |
adcd8f77 MM |
3572 | |
3573 | case 14: | |
dd6d1f8c | 3574 | if (AVR_HAVE_MUL && ldi_ok) |
afee2a52 MM |
3575 | { |
3576 | *len = 5; | |
3577 | return (AS2 (ldi,%B0,0x40) CR_TAB | |
3578 | AS2 (mul,%A0,%B0) CR_TAB | |
3579 | AS2 (mov,%B0,r0) CR_TAB | |
3580 | AS1 (clr,%A0) CR_TAB | |
3581 | AS1 (clr,__zero_reg__)); | |
3582 | } | |
dd6d1f8c | 3583 | if (AVR_HAVE_MUL && scratch) |
adcd8f77 MM |
3584 | { |
3585 | *len = 5; | |
3586 | return (AS2 (ldi,%3,0x40) CR_TAB | |
3587 | AS2 (mul,%A0,%3) CR_TAB | |
3588 | AS2 (mov,%B0,r0) CR_TAB | |
3589 | AS1 (clr,%A0) CR_TAB | |
3590 | AS1 (clr,__zero_reg__)); | |
3591 | } | |
afee2a52 MM |
3592 | if (optimize_size && ldi_ok) |
3593 | { | |
3594 | *len = 5; | |
3595 | return (AS2 (mov,%B0,%A0) CR_TAB | |
3596 | AS2 (ldi,%A0,6) "\n1:\t" | |
3597 | AS1 (lsl,%B0) CR_TAB | |
3598 | AS1 (dec,%A0) CR_TAB | |
3599 | AS1 (brne,1b)); | |
3600 | } | |
3601 | if (optimize_size && scratch) | |
3602 | break; /* 5 */ | |
3603 | *len = 6; | |
3604 | return (AS1 (clr,%B0) CR_TAB | |
3605 | AS1 (lsr,%A0) CR_TAB | |
3606 | AS1 (ror,%B0) CR_TAB | |
3607 | AS1 (lsr,%A0) CR_TAB | |
3608 | AS1 (ror,%B0) CR_TAB | |
3609 | AS1 (clr,%A0)); | |
adcd8f77 MM |
3610 | |
3611 | case 15: | |
3612 | *len = 4; | |
3613 | return (AS1 (clr,%B0) CR_TAB | |
3614 | AS1 (lsr,%A0) CR_TAB | |
3615 | AS1 (ror,%B0) CR_TAB | |
3616 | AS1 (clr,%A0)); | |
90e7678c | 3617 | } |
adcd8f77 | 3618 | len = t; |
90e7678c | 3619 | } |
afee2a52 MM |
3620 | out_shift_with_cnt ((AS1 (lsl,%A0) CR_TAB |
3621 | AS1 (rol,%B0)), | |
3622 | insn, operands, len, 2); | |
90e7678c DC |
3623 | return ""; |
3624 | } | |
3625 | ||
3626 | ||
3627 | /* 32bit shift left ((long)x << i) */ | |
3628 | ||
5fecfd8d | 3629 | const char * |
269e3795 | 3630 | ashlsi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
3631 | { |
3632 | if (GET_CODE (operands[2]) == CONST_INT) | |
3633 | { | |
3634 | int k; | |
afee2a52 | 3635 | int *t = len; |
3454eb73 | 3636 | |
90e7678c DC |
3637 | if (!len) |
3638 | len = &k; | |
3454eb73 | 3639 | |
90e7678c DC |
3640 | switch (INTVAL (operands[2])) |
3641 | { | |
a3cf5992 MM |
3642 | default: |
3643 | if (INTVAL (operands[2]) < 32) | |
3644 | break; | |
3645 | ||
4301ec4f | 3646 | if (AVR_HAVE_MOVW) |
a3cf5992 MM |
3647 | return *len = 3, (AS1 (clr,%D0) CR_TAB |
3648 | AS1 (clr,%C0) CR_TAB | |
3649 | AS2 (movw,%A0,%C0)); | |
3650 | *len = 4; | |
3651 | return (AS1 (clr,%D0) CR_TAB | |
3652 | AS1 (clr,%C0) CR_TAB | |
3653 | AS1 (clr,%B0) CR_TAB | |
3654 | AS1 (clr,%A0)); | |
3655 | ||
90e7678c DC |
3656 | case 8: |
3657 | { | |
3658 | int reg0 = true_regnum (operands[0]); | |
3659 | int reg1 = true_regnum (operands[1]); | |
3454eb73 | 3660 | *len = 4; |
90e7678c DC |
3661 | if (reg0 >= reg1) |
3662 | return (AS2 (mov,%D0,%C1) CR_TAB | |
3663 | AS2 (mov,%C0,%B1) CR_TAB | |
3664 | AS2 (mov,%B0,%A1) CR_TAB | |
3665 | AS1 (clr,%A0)); | |
90e7678c DC |
3666 | else |
3667 | return (AS1 (clr,%A0) CR_TAB | |
3668 | AS2 (mov,%B0,%A1) CR_TAB | |
3669 | AS2 (mov,%C0,%B1) CR_TAB | |
3670 | AS2 (mov,%D0,%C1)); | |
3671 | } | |
3454eb73 | 3672 | |
90e7678c DC |
3673 | case 16: |
3674 | { | |
3675 | int reg0 = true_regnum (operands[0]); | |
3676 | int reg1 = true_regnum (operands[1]); | |
90e7678c | 3677 | if (reg0 + 2 == reg1) |
120b24f7 AS |
3678 | return *len = 2, (AS1 (clr,%B0) CR_TAB |
3679 | AS1 (clr,%A0)); | |
3680 | if (AVR_HAVE_MOVW) | |
3681 | return *len = 3, (AS2 (movw,%C0,%A1) CR_TAB | |
3682 | AS1 (clr,%B0) CR_TAB | |
3683 | AS1 (clr,%A0)); | |
90e7678c | 3684 | else |
120b24f7 AS |
3685 | return *len = 4, (AS2 (mov,%C0,%A1) CR_TAB |
3686 | AS2 (mov,%D0,%B1) CR_TAB | |
3687 | AS1 (clr,%B0) CR_TAB | |
3688 | AS1 (clr,%A0)); | |
90e7678c | 3689 | } |
3454eb73 | 3690 | |
90e7678c | 3691 | case 24: |
3454eb73 | 3692 | *len = 4; |
120b24f7 AS |
3693 | return (AS2 (mov,%D0,%A1) CR_TAB |
3694 | AS1 (clr,%C0) CR_TAB | |
3695 | AS1 (clr,%B0) CR_TAB | |
3696 | AS1 (clr,%A0)); | |
adcd8f77 MM |
3697 | |
3698 | case 31: | |
3699 | *len = 6; | |
3700 | return (AS1 (clr,%D0) CR_TAB | |
3701 | AS1 (lsr,%A0) CR_TAB | |
3702 | AS1 (ror,%D0) CR_TAB | |
3703 | AS1 (clr,%C0) CR_TAB | |
3704 | AS1 (clr,%B0) CR_TAB | |
3705 | AS1 (clr,%A0)); | |
90e7678c | 3706 | } |
adcd8f77 | 3707 | len = t; |
90e7678c | 3708 | } |
afee2a52 MM |
3709 | out_shift_with_cnt ((AS1 (lsl,%A0) CR_TAB |
3710 | AS1 (rol,%B0) CR_TAB | |
3711 | AS1 (rol,%C0) CR_TAB | |
3712 | AS1 (rol,%D0)), | |
3713 | insn, operands, len, 4); | |
90e7678c DC |
3714 | return ""; |
3715 | } | |
3716 | ||
3717 | /* 8bit arithmetic shift right ((signed char)x >> i) */ | |
3718 | ||
5fecfd8d | 3719 | const char * |
269e3795 | 3720 | ashrqi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
3721 | { |
3722 | if (GET_CODE (operands[2]) == CONST_INT) | |
3723 | { | |
90e7678c | 3724 | int k; |
3454eb73 | 3725 | |
90e7678c DC |
3726 | if (!len) |
3727 | len = &k; | |
3454eb73 | 3728 | |
90e7678c DC |
3729 | switch (INTVAL (operands[2])) |
3730 | { | |
90e7678c | 3731 | case 1: |
3454eb73 | 3732 | *len = 1; |
90e7678c | 3733 | return AS1 (asr,%0); |
3454eb73 | 3734 | |
90e7678c | 3735 | case 2: |
3454eb73 | 3736 | *len = 2; |
90e7678c DC |
3737 | return (AS1 (asr,%0) CR_TAB |
3738 | AS1 (asr,%0)); | |
3454eb73 | 3739 | |
90e7678c | 3740 | case 3: |
3454eb73 | 3741 | *len = 3; |
90e7678c DC |
3742 | return (AS1 (asr,%0) CR_TAB |
3743 | AS1 (asr,%0) CR_TAB | |
3744 | AS1 (asr,%0)); | |
3454eb73 | 3745 | |
90e7678c | 3746 | case 4: |
3454eb73 DC |
3747 | *len = 4; |
3748 | return (AS1 (asr,%0) CR_TAB | |
3749 | AS1 (asr,%0) CR_TAB | |
3750 | AS1 (asr,%0) CR_TAB | |
3751 | AS1 (asr,%0)); | |
3752 | ||
3753 | case 5: | |
3754 | *len = 5; | |
90e7678c | 3755 | return (AS1 (asr,%0) CR_TAB |
3454eb73 | 3756 | AS1 (asr,%0) CR_TAB |
90e7678c DC |
3757 | AS1 (asr,%0) CR_TAB |
3758 | AS1 (asr,%0) CR_TAB | |
3759 | AS1 (asr,%0)); | |
3454eb73 DC |
3760 | |
3761 | case 6: | |
3762 | *len = 4; | |
3763 | return (AS2 (bst,%0,6) CR_TAB | |
3764 | AS1 (lsl,%0) CR_TAB | |
3765 | AS2 (sbc,%0,%0) CR_TAB | |
3766 | AS2 (bld,%0,0)); | |
3767 | ||
3768 | default: | |
a3cf5992 MM |
3769 | if (INTVAL (operands[2]) < 8) |
3770 | break; | |
3771 | ||
3772 | /* fall through */ | |
3773 | ||
3454eb73 DC |
3774 | case 7: |
3775 | *len = 2; | |
3776 | return (AS1 (lsl,%0) CR_TAB | |
3777 | AS2 (sbc,%0,%0)); | |
90e7678c DC |
3778 | } |
3779 | } | |
3454eb73 | 3780 | else if (CONSTANT_P (operands[2])) |
c725bd79 | 3781 | fatal_insn ("internal compiler error. Incorrect shift:", insn); |
3454eb73 | 3782 | |
0db7ad3a | 3783 | out_shift_with_cnt (AS1 (asr,%0), |
afee2a52 | 3784 | insn, operands, len, 1); |
90e7678c DC |
3785 | return ""; |
3786 | } | |
3787 | ||
3788 | ||
3789 | /* 16bit arithmetic shift right ((signed short)x >> i) */ | |
3790 | ||
5fecfd8d | 3791 | const char * |
269e3795 | 3792 | ashrhi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
3793 | { |
3794 | if (GET_CODE (operands[2]) == CONST_INT) | |
3795 | { | |
afee2a52 MM |
3796 | int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); |
3797 | int ldi_ok = test_hard_reg_class (LD_REGS, operands[0]); | |
90e7678c | 3798 | int k; |
3454eb73 DC |
3799 | int *t = len; |
3800 | ||
90e7678c DC |
3801 | if (!len) |
3802 | len = &k; | |
3454eb73 | 3803 | |
90e7678c DC |
3804 | switch (INTVAL (operands[2])) |
3805 | { | |
afee2a52 MM |
3806 | case 4: |
3807 | case 5: | |
3808 | /* XXX try to optimize this too? */ | |
3809 | break; | |
3454eb73 | 3810 | |
afee2a52 MM |
3811 | case 6: |
3812 | if (optimize_size) | |
3813 | break; /* scratch ? 5 : 6 */ | |
3814 | *len = 8; | |
3815 | return (AS2 (mov,__tmp_reg__,%A0) CR_TAB | |
3816 | AS2 (mov,%A0,%B0) CR_TAB | |
3817 | AS1 (lsl,__tmp_reg__) CR_TAB | |
3818 | AS1 (rol,%A0) CR_TAB | |
3819 | AS2 (sbc,%B0,%B0) CR_TAB | |
3820 | AS1 (lsl,__tmp_reg__) CR_TAB | |
3821 | AS1 (rol,%A0) CR_TAB | |
3822 | AS1 (rol,%B0)); | |
3454eb73 | 3823 | |
adcd8f77 MM |
3824 | case 7: |
3825 | *len = 4; | |
3826 | return (AS1 (lsl,%A0) CR_TAB | |
3827 | AS2 (mov,%A0,%B0) CR_TAB | |
3828 | AS1 (rol,%A0) CR_TAB | |
3829 | AS2 (sbc,%B0,%B0)); | |
3830 | ||
90e7678c | 3831 | case 8: |
afee2a52 MM |
3832 | { |
3833 | int reg0 = true_regnum (operands[0]); | |
3834 | int reg1 = true_regnum (operands[1]); | |
3835 | ||
3836 | if (reg0 == reg1) | |
3837 | return *len = 3, (AS2 (mov,%A0,%B0) CR_TAB | |
3838 | AS1 (lsl,%B0) CR_TAB | |
3839 | AS2 (sbc,%B0,%B0)); | |
120b24f7 AS |
3840 | else |
3841 | return *len = 4, (AS2 (mov,%A0,%B1) CR_TAB | |
3842 | AS1 (clr,%B0) CR_TAB | |
3843 | AS2 (sbrc,%A0,7) CR_TAB | |
3844 | AS1 (dec,%B0)); | |
afee2a52 | 3845 | } |
3454eb73 | 3846 | |
adcd8f77 MM |
3847 | case 9: |
3848 | *len = 4; | |
3849 | return (AS2 (mov,%A0,%B0) CR_TAB | |
3850 | AS1 (lsl,%B0) CR_TAB | |
3851 | AS2 (sbc,%B0,%B0) CR_TAB | |
3852 | AS1 (asr,%A0)); | |
3853 | ||
3854 | case 10: | |
3855 | *len = 5; | |
3856 | return (AS2 (mov,%A0,%B0) CR_TAB | |
3857 | AS1 (lsl,%B0) CR_TAB | |
3858 | AS2 (sbc,%B0,%B0) CR_TAB | |
3859 | AS1 (asr,%A0) CR_TAB | |
3860 | AS1 (asr,%A0)); | |
3861 | ||
3862 | case 11: | |
dd6d1f8c | 3863 | if (AVR_HAVE_MUL && ldi_ok) |
adcd8f77 MM |
3864 | { |
3865 | *len = 5; | |
afee2a52 MM |
3866 | return (AS2 (ldi,%A0,0x20) CR_TAB |
3867 | AS2 (muls,%B0,%A0) CR_TAB | |
3868 | AS2 (mov,%A0,r1) CR_TAB | |
3869 | AS2 (sbc,%B0,%B0) CR_TAB | |
adcd8f77 MM |
3870 | AS1 (clr,__zero_reg__)); |
3871 | } | |
afee2a52 MM |
3872 | if (optimize_size && scratch) |
3873 | break; /* 5 */ | |
3874 | *len = 6; | |
3875 | return (AS2 (mov,%A0,%B0) CR_TAB | |
3876 | AS1 (lsl,%B0) CR_TAB | |
3877 | AS2 (sbc,%B0,%B0) CR_TAB | |
3878 | AS1 (asr,%A0) CR_TAB | |
3879 | AS1 (asr,%A0) CR_TAB | |
3880 | AS1 (asr,%A0)); | |
adcd8f77 MM |
3881 | |
3882 | case 12: | |
dd6d1f8c | 3883 | if (AVR_HAVE_MUL && ldi_ok) |
adcd8f77 MM |
3884 | { |
3885 | *len = 5; | |
afee2a52 MM |
3886 | return (AS2 (ldi,%A0,0x10) CR_TAB |
3887 | AS2 (muls,%B0,%A0) CR_TAB | |
3888 | AS2 (mov,%A0,r1) CR_TAB | |
3889 | AS2 (sbc,%B0,%B0) CR_TAB | |
adcd8f77 MM |
3890 | AS1 (clr,__zero_reg__)); |
3891 | } | |
afee2a52 MM |
3892 | if (optimize_size && scratch) |
3893 | break; /* 5 */ | |
3894 | *len = 7; | |
3895 | return (AS2 (mov,%A0,%B0) CR_TAB | |
3896 | AS1 (lsl,%B0) CR_TAB | |
3897 | AS2 (sbc,%B0,%B0) CR_TAB | |
3898 | AS1 (asr,%A0) CR_TAB | |
3899 | AS1 (asr,%A0) CR_TAB | |
3900 | AS1 (asr,%A0) CR_TAB | |
3901 | AS1 (asr,%A0)); | |
adcd8f77 MM |
3902 | |
3903 | case 13: | |
dd6d1f8c | 3904 | if (AVR_HAVE_MUL && ldi_ok) |
adcd8f77 MM |
3905 | { |
3906 | *len = 5; | |
afee2a52 MM |
3907 | return (AS2 (ldi,%A0,0x08) CR_TAB |
3908 | AS2 (muls,%B0,%A0) CR_TAB | |
3909 | AS2 (mov,%A0,r1) CR_TAB | |
3910 | AS2 (sbc,%B0,%B0) CR_TAB | |
adcd8f77 MM |
3911 | AS1 (clr,__zero_reg__)); |
3912 | } | |
afee2a52 MM |
3913 | if (optimize_size) |
3914 | break; /* scratch ? 5 : 7 */ | |
3915 | *len = 8; | |
3916 | return (AS2 (mov,%A0,%B0) CR_TAB | |
3917 | AS1 (lsl,%B0) CR_TAB | |
3918 | AS2 (sbc,%B0,%B0) CR_TAB | |
3919 | AS1 (asr,%A0) CR_TAB | |
3920 | AS1 (asr,%A0) CR_TAB | |
3921 | AS1 (asr,%A0) CR_TAB | |
3922 | AS1 (asr,%A0) CR_TAB | |
3923 | AS1 (asr,%A0)); | |
adcd8f77 MM |
3924 | |
3925 | case 14: | |
3926 | *len = 5; | |
3927 | return (AS1 (lsl,%B0) CR_TAB | |
3928 | AS2 (sbc,%A0,%A0) CR_TAB | |
3929 | AS1 (lsl,%B0) CR_TAB | |
3930 | AS2 (mov,%B0,%A0) CR_TAB | |
3931 | AS1 (rol,%A0)); | |
3932 | ||
a3cf5992 MM |
3933 | default: |
3934 | if (INTVAL (operands[2]) < 16) | |
3935 | break; | |
3936 | ||
3937 | /* fall through */ | |
3938 | ||
28e801e2 DC |
3939 | case 15: |
3940 | return *len = 3, (AS1 (lsl,%B0) CR_TAB | |
3941 | AS2 (sbc,%A0,%A0) CR_TAB | |
3942 | AS2 (mov,%B0,%A0)); | |
90e7678c | 3943 | } |
adcd8f77 | 3944 | len = t; |
90e7678c | 3945 | } |
afee2a52 MM |
3946 | out_shift_with_cnt ((AS1 (asr,%B0) CR_TAB |
3947 | AS1 (ror,%A0)), | |
3948 | insn, operands, len, 2); | |
90e7678c DC |
3949 | return ""; |
3950 | } | |
3951 | ||
3952 | ||
3953 | /* 32bit arithmetic shift right ((signed long)x >> i) */ | |
3954 | ||
5fecfd8d | 3955 | const char * |
269e3795 | 3956 | ashrsi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
3957 | { |
3958 | if (GET_CODE (operands[2]) == CONST_INT) | |
3959 | { | |
3960 | int k; | |
3961 | int *t = len; | |
3454eb73 | 3962 | |
90e7678c DC |
3963 | if (!len) |
3964 | len = &k; | |
3454eb73 | 3965 | |
90e7678c DC |
3966 | switch (INTVAL (operands[2])) |
3967 | { | |
90e7678c DC |
3968 | case 8: |
3969 | { | |
3970 | int reg0 = true_regnum (operands[0]); | |
3971 | int reg1 = true_regnum (operands[1]); | |
3972 | *len=6; | |
3973 | if (reg0 <= reg1) | |
3974 | return (AS2 (mov,%A0,%B1) CR_TAB | |
3975 | AS2 (mov,%B0,%C1) CR_TAB | |
3976 | AS2 (mov,%C0,%D1) CR_TAB | |
3977 | AS1 (clr,%D0) CR_TAB | |
3978 | AS2 (sbrc,%C0,7) CR_TAB | |
3979 | AS1 (dec,%D0)); | |
90e7678c DC |
3980 | else |
3981 | return (AS1 (clr,%D0) CR_TAB | |
28e801e2 | 3982 | AS2 (sbrc,%D1,7) CR_TAB |
90e7678c DC |
3983 | AS1 (dec,%D0) CR_TAB |
3984 | AS2 (mov,%C0,%D1) CR_TAB | |
3985 | AS2 (mov,%B0,%C1) CR_TAB | |
3986 | AS2 (mov,%A0,%B1)); | |
3987 | } | |
3454eb73 | 3988 | |
90e7678c DC |
3989 | case 16: |
3990 | { | |
3991 | int reg0 = true_regnum (operands[0]); | |
3992 | int reg1 = true_regnum (operands[1]); | |
120b24f7 AS |
3993 | |
3994 | if (reg0 == reg1 + 2) | |
90e7678c DC |
3995 | return *len = 4, (AS1 (clr,%D0) CR_TAB |
3996 | AS2 (sbrc,%B0,7) CR_TAB | |
3997 | AS1 (com,%D0) CR_TAB | |
3998 | AS2 (mov,%C0,%D0)); | |
120b24f7 AS |
3999 | if (AVR_HAVE_MOVW) |
4000 | return *len = 5, (AS2 (movw,%A0,%C1) CR_TAB | |
4001 | AS1 (clr,%D0) CR_TAB | |
4002 | AS2 (sbrc,%B0,7) CR_TAB | |
4003 | AS1 (com,%D0) CR_TAB | |
4004 | AS2 (mov,%C0,%D0)); | |
4005 | else | |
4006 | return *len = 6, (AS2 (mov,%B0,%D1) CR_TAB | |
4007 | AS2 (mov,%A0,%C1) CR_TAB | |
4008 | AS1 (clr,%D0) CR_TAB | |
4009 | AS2 (sbrc,%B0,7) CR_TAB | |
4010 | AS1 (com,%D0) CR_TAB | |
4011 | AS2 (mov,%C0,%D0)); | |
90e7678c | 4012 | } |
3454eb73 | 4013 | |
90e7678c | 4014 | case 24: |
120b24f7 AS |
4015 | return *len = 6, (AS2 (mov,%A0,%D1) CR_TAB |
4016 | AS1 (clr,%D0) CR_TAB | |
4017 | AS2 (sbrc,%A0,7) CR_TAB | |
4018 | AS1 (com,%D0) CR_TAB | |
4019 | AS2 (mov,%B0,%D0) CR_TAB | |
4020 | AS2 (mov,%C0,%D0)); | |
adcd8f77 | 4021 | |
a3cf5992 MM |
4022 | default: |
4023 | if (INTVAL (operands[2]) < 32) | |
4024 | break; | |
4025 | ||
4026 | /* fall through */ | |
4027 | ||
adcd8f77 | 4028 | case 31: |
4301ec4f | 4029 | if (AVR_HAVE_MOVW) |
adcd8f77 MM |
4030 | return *len = 4, (AS1 (lsl,%D0) CR_TAB |
4031 | AS2 (sbc,%A0,%A0) CR_TAB | |
4032 | AS2 (mov,%B0,%A0) CR_TAB | |
4033 | AS2 (movw,%C0,%A0)); | |
4034 | else | |
4035 | return *len = 5, (AS1 (lsl,%D0) CR_TAB | |
4036 | AS2 (sbc,%A0,%A0) CR_TAB | |
4037 | AS2 (mov,%B0,%A0) CR_TAB | |
4038 | AS2 (mov,%C0,%A0) CR_TAB | |
4039 | AS2 (mov,%D0,%A0)); | |
90e7678c | 4040 | } |
adcd8f77 | 4041 | len = t; |
90e7678c | 4042 | } |
afee2a52 MM |
4043 | out_shift_with_cnt ((AS1 (asr,%D0) CR_TAB |
4044 | AS1 (ror,%C0) CR_TAB | |
4045 | AS1 (ror,%B0) CR_TAB | |
4046 | AS1 (ror,%A0)), | |
4047 | insn, operands, len, 4); | |
90e7678c DC |
4048 | return ""; |
4049 | } | |
4050 | ||
4051 | /* 8bit logic shift right ((unsigned char)x >> i) */ | |
4052 | ||
5fecfd8d | 4053 | const char * |
269e3795 | 4054 | lshrqi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
4055 | { |
4056 | if (GET_CODE (operands[2]) == CONST_INT) | |
4057 | { | |
4058 | int k; | |
3454eb73 | 4059 | |
90e7678c DC |
4060 | if (!len) |
4061 | len = &k; | |
3454eb73 | 4062 | |
90e7678c DC |
4063 | switch (INTVAL (operands[2])) |
4064 | { | |
3454eb73 | 4065 | default: |
a3cf5992 MM |
4066 | if (INTVAL (operands[2]) < 8) |
4067 | break; | |
4068 | ||
3454eb73 DC |
4069 | *len = 1; |
4070 | return AS1 (clr,%0); | |
4071 | ||
90e7678c | 4072 | case 1: |
3454eb73 | 4073 | *len = 1; |
90e7678c | 4074 | return AS1 (lsr,%0); |
3454eb73 | 4075 | |
90e7678c | 4076 | case 2: |
3454eb73 | 4077 | *len = 2; |
90e7678c DC |
4078 | return (AS1 (lsr,%0) CR_TAB |
4079 | AS1 (lsr,%0)); | |
4080 | case 3: | |
3454eb73 | 4081 | *len = 3; |
90e7678c DC |
4082 | return (AS1 (lsr,%0) CR_TAB |
4083 | AS1 (lsr,%0) CR_TAB | |
4084 | AS1 (lsr,%0)); | |
3454eb73 | 4085 | |
90e7678c | 4086 | case 4: |
c4984bad | 4087 | if (test_hard_reg_class (LD_REGS, operands[0])) |
90e7678c DC |
4088 | { |
4089 | *len=2; | |
4090 | return (AS1 (swap,%0) CR_TAB | |
4091 | AS2 (andi,%0,0x0f)); | |
4092 | } | |
3454eb73 | 4093 | *len = 4; |
90e7678c DC |
4094 | return (AS1 (lsr,%0) CR_TAB |
4095 | AS1 (lsr,%0) CR_TAB | |
4096 | AS1 (lsr,%0) CR_TAB | |
4097 | AS1 (lsr,%0)); | |
3454eb73 | 4098 | |
90e7678c | 4099 | case 5: |
c4984bad | 4100 | if (test_hard_reg_class (LD_REGS, operands[0])) |
90e7678c | 4101 | { |
3454eb73 | 4102 | *len = 3; |
90e7678c DC |
4103 | return (AS1 (swap,%0) CR_TAB |
4104 | AS1 (lsr,%0) CR_TAB | |
4105 | AS2 (andi,%0,0x7)); | |
4106 | } | |
3454eb73 | 4107 | *len = 5; |
90e7678c DC |
4108 | return (AS1 (lsr,%0) CR_TAB |
4109 | AS1 (lsr,%0) CR_TAB | |
4110 | AS1 (lsr,%0) CR_TAB | |
4111 | AS1 (lsr,%0) CR_TAB | |
4112 | AS1 (lsr,%0)); | |
3454eb73 | 4113 | |
90e7678c | 4114 | case 6: |
c4984bad | 4115 | if (test_hard_reg_class (LD_REGS, operands[0])) |
90e7678c | 4116 | { |
3454eb73 | 4117 | *len = 4; |
90e7678c DC |
4118 | return (AS1 (swap,%0) CR_TAB |
4119 | AS1 (lsr,%0) CR_TAB | |
4120 | AS1 (lsr,%0) CR_TAB | |
4121 | AS2 (andi,%0,0x3)); | |
4122 | } | |
3454eb73 | 4123 | *len = 6; |
90e7678c DC |
4124 | return (AS1 (lsr,%0) CR_TAB |
4125 | AS1 (lsr,%0) CR_TAB | |
4126 | AS1 (lsr,%0) CR_TAB | |
4127 | AS1 (lsr,%0) CR_TAB | |
4128 | AS1 (lsr,%0) CR_TAB | |
4129 | AS1 (lsr,%0)); | |
3454eb73 | 4130 | |
90e7678c | 4131 | case 7: |
3454eb73 | 4132 | *len = 3; |
90e7678c DC |
4133 | return (AS1 (rol,%0) CR_TAB |
4134 | AS1 (clr,%0) CR_TAB | |
4135 | AS1 (rol,%0)); | |
4136 | } | |
4137 | } | |
3454eb73 | 4138 | else if (CONSTANT_P (operands[2])) |
c725bd79 | 4139 | fatal_insn ("internal compiler error. Incorrect shift:", insn); |
3454eb73 | 4140 | |
0db7ad3a | 4141 | out_shift_with_cnt (AS1 (lsr,%0), |
afee2a52 | 4142 | insn, operands, len, 1); |
90e7678c DC |
4143 | return ""; |
4144 | } | |
4145 | ||
4146 | /* 16bit logic shift right ((unsigned short)x >> i) */ | |
4147 | ||
5fecfd8d | 4148 | const char * |
269e3795 | 4149 | lshrhi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
4150 | { |
4151 | if (GET_CODE (operands[2]) == CONST_INT) | |
4152 | { | |
afee2a52 MM |
4153 | int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); |
4154 | int ldi_ok = test_hard_reg_class (LD_REGS, operands[0]); | |
90e7678c | 4155 | int k; |
3454eb73 | 4156 | int *t = len; |
afee2a52 | 4157 | |
90e7678c DC |
4158 | if (!len) |
4159 | len = &k; | |
3454eb73 | 4160 | |
90e7678c DC |
4161 | switch (INTVAL (operands[2])) |
4162 | { | |
a3cf5992 MM |
4163 | default: |
4164 | if (INTVAL (operands[2]) < 16) | |
4165 | break; | |
4166 | ||
4167 | *len = 2; | |
4168 | return (AS1 (clr,%B0) CR_TAB | |
4169 | AS1 (clr,%A0)); | |
4170 | ||
afee2a52 MM |
4171 | case 4: |
4172 | if (optimize_size && scratch) | |
4173 | break; /* 5 */ | |
4174 | if (ldi_ok) | |
4175 | { | |
4176 | *len = 6; | |
4177 | return (AS1 (swap,%B0) CR_TAB | |
4178 | AS1 (swap,%A0) CR_TAB | |
4179 | AS2 (andi,%A0,0x0f) CR_TAB | |
4180 | AS2 (eor,%A0,%B0) CR_TAB | |
4181 | AS2 (andi,%B0,0x0f) CR_TAB | |
4182 | AS2 (eor,%A0,%B0)); | |
4183 | } | |
4184 | if (scratch) | |
4185 | { | |
4186 | *len = 7; | |
4187 | return (AS1 (swap,%B0) CR_TAB | |
4188 | AS1 (swap,%A0) CR_TAB | |
4189 | AS2 (ldi,%3,0x0f) CR_TAB | |
5a82ecd9 | 4190 | "and %A0,%3" CR_TAB |
afee2a52 | 4191 | AS2 (eor,%A0,%B0) CR_TAB |
5a82ecd9 | 4192 | "and %B0,%3" CR_TAB |
afee2a52 MM |
4193 | AS2 (eor,%A0,%B0)); |
4194 | } | |
4195 | break; /* optimize_size ? 6 : 8 */ | |
4196 | ||
4197 | case 5: | |
4198 | if (optimize_size) | |
4199 | break; /* scratch ? 5 : 6 */ | |
4200 | if (ldi_ok) | |
4201 | { | |
4202 | *len = 8; | |
4203 | return (AS1 (lsr,%B0) CR_TAB | |
4204 | AS1 (ror,%A0) CR_TAB | |
4205 | AS1 (swap,%B0) CR_TAB | |
4206 | AS1 (swap,%A0) CR_TAB | |
4207 | AS2 (andi,%A0,0x0f) CR_TAB | |
4208 | AS2 (eor,%A0,%B0) CR_TAB | |
4209 | AS2 (andi,%B0,0x0f) CR_TAB | |
4210 | AS2 (eor,%A0,%B0)); | |
4211 | } | |
4212 | if (scratch) | |
4213 | { | |
4214 | *len = 9; | |
4215 | return (AS1 (lsr,%B0) CR_TAB | |
4216 | AS1 (ror,%A0) CR_TAB | |
4217 | AS1 (swap,%B0) CR_TAB | |
4218 | AS1 (swap,%A0) CR_TAB | |
4219 | AS2 (ldi,%3,0x0f) CR_TAB | |
5a82ecd9 | 4220 | "and %A0,%3" CR_TAB |
afee2a52 | 4221 | AS2 (eor,%A0,%B0) CR_TAB |
5a82ecd9 | 4222 | "and %B0,%3" CR_TAB |
afee2a52 MM |
4223 | AS2 (eor,%A0,%B0)); |
4224 | } | |
4225 | break; /* 10 */ | |
4226 | ||
4227 | case 6: | |
4228 | if (optimize_size) | |
4229 | break; /* scratch ? 5 : 6 */ | |
4230 | *len = 9; | |
4231 | return (AS1 (clr,__tmp_reg__) CR_TAB | |
4232 | AS1 (lsl,%A0) CR_TAB | |
4233 | AS1 (rol,%B0) CR_TAB | |
4234 | AS1 (rol,__tmp_reg__) CR_TAB | |
4235 | AS1 (lsl,%A0) CR_TAB | |
4236 | AS1 (rol,%B0) CR_TAB | |
4237 | AS1 (rol,__tmp_reg__) CR_TAB | |
4238 | AS2 (mov,%A0,%B0) CR_TAB | |
4239 | AS2 (mov,%B0,__tmp_reg__)); | |
adcd8f77 MM |
4240 | |
4241 | case 7: | |
4242 | *len = 5; | |
4243 | return (AS1 (lsl,%A0) CR_TAB | |
4244 | AS2 (mov,%A0,%B0) CR_TAB | |
4245 | AS1 (rol,%A0) CR_TAB | |
4246 | AS2 (sbc,%B0,%B0) CR_TAB | |
4247 | AS1 (neg,%B0)); | |
4248 | ||
90e7678c | 4249 | case 8: |
120b24f7 AS |
4250 | return *len = 2, (AS2 (mov,%A0,%B1) CR_TAB |
4251 | AS1 (clr,%B0)); | |
adcd8f77 MM |
4252 | |
4253 | case 9: | |
4254 | *len = 3; | |
4255 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4256 | AS1 (clr,%B0) CR_TAB | |
4257 | AS1 (lsr,%A0)); | |
4258 | ||
4259 | case 10: | |
4260 | *len = 4; | |
4261 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4262 | AS1 (clr,%B0) CR_TAB | |
4263 | AS1 (lsr,%A0) CR_TAB | |
4264 | AS1 (lsr,%A0)); | |
4265 | ||
4266 | case 11: | |
4267 | *len = 5; | |
4268 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4269 | AS1 (clr,%B0) CR_TAB | |
4270 | AS1 (lsr,%A0) CR_TAB | |
4271 | AS1 (lsr,%A0) CR_TAB | |
4272 | AS1 (lsr,%A0)); | |
4273 | ||
4274 | case 12: | |
afee2a52 | 4275 | if (ldi_ok) |
adcd8f77 MM |
4276 | { |
4277 | *len = 4; | |
4278 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4279 | AS1 (clr,%B0) CR_TAB | |
4280 | AS1 (swap,%A0) CR_TAB | |
4281 | AS2 (andi,%A0,0x0f)); | |
4282 | } | |
afee2a52 MM |
4283 | if (scratch) |
4284 | { | |
4285 | *len = 5; | |
4286 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4287 | AS1 (clr,%B0) CR_TAB | |
4288 | AS1 (swap,%A0) CR_TAB | |
4289 | AS2 (ldi,%3,0x0f) CR_TAB | |
5a82ecd9 | 4290 | "and %A0,%3"); |
afee2a52 MM |
4291 | } |
4292 | *len = 6; | |
adcd8f77 MM |
4293 | return (AS2 (mov,%A0,%B0) CR_TAB |
4294 | AS1 (clr,%B0) CR_TAB | |
afee2a52 MM |
4295 | AS1 (lsr,%A0) CR_TAB |
4296 | AS1 (lsr,%A0) CR_TAB | |
4297 | AS1 (lsr,%A0) CR_TAB | |
4298 | AS1 (lsr,%A0)); | |
adcd8f77 MM |
4299 | |
4300 | case 13: | |
afee2a52 | 4301 | if (ldi_ok) |
adcd8f77 MM |
4302 | { |
4303 | *len = 5; | |
4304 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4305 | AS1 (clr,%B0) CR_TAB | |
4306 | AS1 (swap,%A0) CR_TAB | |
4307 | AS1 (lsr,%A0) CR_TAB | |
4308 | AS2 (andi,%A0,0x07)); | |
4309 | } | |
dd6d1f8c | 4310 | if (AVR_HAVE_MUL && scratch) |
adcd8f77 MM |
4311 | { |
4312 | *len = 5; | |
4313 | return (AS2 (ldi,%3,0x08) CR_TAB | |
4314 | AS2 (mul,%B0,%3) CR_TAB | |
4315 | AS2 (mov,%A0,r1) CR_TAB | |
4316 | AS1 (clr,%B0) CR_TAB | |
4317 | AS1 (clr,__zero_reg__)); | |
4318 | } | |
afee2a52 MM |
4319 | if (optimize_size && scratch) |
4320 | break; /* 5 */ | |
4321 | if (scratch) | |
4322 | { | |
4323 | *len = 6; | |
4324 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4325 | AS1 (clr,%B0) CR_TAB | |
4326 | AS1 (swap,%A0) CR_TAB | |
4327 | AS1 (lsr,%A0) CR_TAB | |
4328 | AS2 (ldi,%3,0x07) CR_TAB | |
5a82ecd9 | 4329 | "and %A0,%3"); |
afee2a52 | 4330 | } |
dd6d1f8c | 4331 | if (AVR_HAVE_MUL) |
afee2a52 MM |
4332 | { |
4333 | *len = 6; | |
4334 | return ("set" CR_TAB | |
4335 | AS2 (bld,r1,3) CR_TAB | |
4336 | AS2 (mul,%B0,r1) CR_TAB | |
4337 | AS2 (mov,%A0,r1) CR_TAB | |
4338 | AS1 (clr,%B0) CR_TAB | |
4339 | AS1 (clr,__zero_reg__)); | |
4340 | } | |
4341 | *len = 7; | |
4342 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4343 | AS1 (clr,%B0) CR_TAB | |
4344 | AS1 (lsr,%A0) CR_TAB | |
4345 | AS1 (lsr,%A0) CR_TAB | |
4346 | AS1 (lsr,%A0) CR_TAB | |
4347 | AS1 (lsr,%A0) CR_TAB | |
4348 | AS1 (lsr,%A0)); | |
adcd8f77 MM |
4349 | |
4350 | case 14: | |
dd6d1f8c | 4351 | if (AVR_HAVE_MUL && ldi_ok) |
afee2a52 MM |
4352 | { |
4353 | *len = 5; | |
4354 | return (AS2 (ldi,%A0,0x04) CR_TAB | |
4355 | AS2 (mul,%B0,%A0) CR_TAB | |
4356 | AS2 (mov,%A0,r1) CR_TAB | |
4357 | AS1 (clr,%B0) CR_TAB | |
4358 | AS1 (clr,__zero_reg__)); | |
4359 | } | |
dd6d1f8c | 4360 | if (AVR_HAVE_MUL && scratch) |
adcd8f77 MM |
4361 | { |
4362 | *len = 5; | |
4363 | return (AS2 (ldi,%3,0x04) CR_TAB | |
4364 | AS2 (mul,%B0,%3) CR_TAB | |
4365 | AS2 (mov,%A0,r1) CR_TAB | |
4366 | AS1 (clr,%B0) CR_TAB | |
4367 | AS1 (clr,__zero_reg__)); | |
4368 | } | |
afee2a52 MM |
4369 | if (optimize_size && ldi_ok) |
4370 | { | |
4371 | *len = 5; | |
4372 | return (AS2 (mov,%A0,%B0) CR_TAB | |
4373 | AS2 (ldi,%B0,6) "\n1:\t" | |
4374 | AS1 (lsr,%A0) CR_TAB | |
4375 | AS1 (dec,%B0) CR_TAB | |
4376 | AS1 (brne,1b)); | |
4377 | } | |
4378 | if (optimize_size && scratch) | |
4379 | break; /* 5 */ | |
4380 | *len = 6; | |
4381 | return (AS1 (clr,%A0) CR_TAB | |
4382 | AS1 (lsl,%B0) CR_TAB | |
4383 | AS1 (rol,%A0) CR_TAB | |
4384 | AS1 (lsl,%B0) CR_TAB | |
4385 | AS1 (rol,%A0) CR_TAB | |
4386 | AS1 (clr,%B0)); | |
adcd8f77 | 4387 | |
28e801e2 | 4388 | case 15: |
3454eb73 | 4389 | *len = 4; |
afee2a52 MM |
4390 | return (AS1 (clr,%A0) CR_TAB |
4391 | AS1 (lsl,%B0) CR_TAB | |
4392 | AS1 (rol,%A0) CR_TAB | |
3454eb73 | 4393 | AS1 (clr,%B0)); |
90e7678c | 4394 | } |
adcd8f77 | 4395 | len = t; |
90e7678c | 4396 | } |
afee2a52 MM |
4397 | out_shift_with_cnt ((AS1 (lsr,%B0) CR_TAB |
4398 | AS1 (ror,%A0)), | |
4399 | insn, operands, len, 2); | |
90e7678c DC |
4400 | return ""; |
4401 | } | |
4402 | ||
4403 | /* 32bit logic shift right ((unsigned int)x >> i) */ | |
4404 | ||
5fecfd8d | 4405 | const char * |
269e3795 | 4406 | lshrsi3_out (rtx insn, rtx operands[], int *len) |
90e7678c DC |
4407 | { |
4408 | if (GET_CODE (operands[2]) == CONST_INT) | |
4409 | { | |
4410 | int k; | |
3454eb73 DC |
4411 | int *t = len; |
4412 | ||
90e7678c DC |
4413 | if (!len) |
4414 | len = &k; | |
3454eb73 | 4415 | |
90e7678c DC |
4416 | switch (INTVAL (operands[2])) |
4417 | { | |
a3cf5992 MM |
4418 | default: |
4419 | if (INTVAL (operands[2]) < 32) | |
4420 | break; | |
4421 | ||
4301ec4f | 4422 | if (AVR_HAVE_MOVW) |
a3cf5992 MM |
4423 | return *len = 3, (AS1 (clr,%D0) CR_TAB |
4424 | AS1 (clr,%C0) CR_TAB | |
4425 | AS2 (movw,%A0,%C0)); | |
4426 | *len = 4; | |
4427 | return (AS1 (clr,%D0) CR_TAB | |
4428 | AS1 (clr,%C0) CR_TAB | |
4429 | AS1 (clr,%B0) CR_TAB | |
4430 | AS1 (clr,%A0)); | |
4431 | ||
90e7678c DC |
4432 | case 8: |
4433 | { | |
4434 | int reg0 = true_regnum (operands[0]); | |
4435 | int reg1 = true_regnum (operands[1]); | |
3454eb73 | 4436 | *len = 4; |
90e7678c DC |
4437 | if (reg0 <= reg1) |
4438 | return (AS2 (mov,%A0,%B1) CR_TAB | |
4439 | AS2 (mov,%B0,%C1) CR_TAB | |
4440 | AS2 (mov,%C0,%D1) CR_TAB | |
4441 | AS1 (clr,%D0)); | |
90e7678c DC |
4442 | else |
4443 | return (AS1 (clr,%D0) CR_TAB | |
4444 | AS2 (mov,%C0,%D1) CR_TAB | |
4445 | AS2 (mov,%B0,%C1) CR_TAB | |
4446 | AS2 (mov,%A0,%B1)); | |
4447 | } | |
3454eb73 | 4448 | |
90e7678c DC |
4449 | case 16: |
4450 | { | |
4451 | int reg0 = true_regnum (operands[0]); | |
4452 | int reg1 = true_regnum (operands[1]); | |
120b24f7 AS |
4453 | |
4454 | if (reg0 == reg1 + 2) | |
90e7678c DC |
4455 | return *len = 2, (AS1 (clr,%C0) CR_TAB |
4456 | AS1 (clr,%D0)); | |
120b24f7 AS |
4457 | if (AVR_HAVE_MOVW) |
4458 | return *len = 3, (AS2 (movw,%A0,%C1) CR_TAB | |
4459 | AS1 (clr,%C0) CR_TAB | |
4460 | AS1 (clr,%D0)); | |
90e7678c | 4461 | else |
120b24f7 AS |
4462 | return *len = 4, (AS2 (mov,%B0,%D1) CR_TAB |
4463 | AS2 (mov,%A0,%C1) CR_TAB | |
4464 | AS1 (clr,%C0) CR_TAB | |
4465 | AS1 (clr,%D0)); | |
90e7678c | 4466 | } |
3454eb73 | 4467 | |
90e7678c | 4468 | case 24: |
120b24f7 AS |
4469 | return *len = 4, (AS2 (mov,%A0,%D1) CR_TAB |
4470 | AS1 (clr,%B0) CR_TAB | |
4471 | AS1 (clr,%C0) CR_TAB | |
4472 | AS1 (clr,%D0)); | |
adcd8f77 MM |
4473 | |
4474 | case 31: | |
4475 | *len = 6; | |
4476 | return (AS1 (clr,%A0) CR_TAB | |
4477 | AS2 (sbrc,%D0,7) CR_TAB | |
4478 | AS1 (inc,%A0) CR_TAB | |
4479 | AS1 (clr,%B0) CR_TAB | |
4480 | AS1 (clr,%C0) CR_TAB | |
4481 | AS1 (clr,%D0)); | |
90e7678c | 4482 | } |
adcd8f77 | 4483 | len = t; |
90e7678c | 4484 | } |
afee2a52 MM |
4485 | out_shift_with_cnt ((AS1 (lsr,%D0) CR_TAB |
4486 | AS1 (ror,%C0) CR_TAB | |
4487 | AS1 (ror,%B0) CR_TAB | |
4488 | AS1 (ror,%A0)), | |
4489 | insn, operands, len, 4); | |
90e7678c DC |
4490 | return ""; |
4491 | } | |
4492 | ||
58f0ea2f AH |
4493 | /* Create RTL split patterns for byte sized rotate expressions. This |
4494 | produces a series of move instructions and considers overlap situations. | |
4495 | Overlapping non-HImode operands need a scratch register. */ | |
4496 | ||
4497 | bool | |
4498 | avr_rotate_bytes (rtx operands[]) | |
4499 | { | |
4500 | int i, j; | |
4501 | enum machine_mode mode = GET_MODE (operands[0]); | |
4502 | bool overlapped = reg_overlap_mentioned_p (operands[0], operands[1]); | |
4503 | bool same_reg = rtx_equal_p (operands[0], operands[1]); | |
4504 | int num = INTVAL (operands[2]); | |
4505 | rtx scratch = operands[3]; | |
4506 | /* Work out if byte or word move is needed. Odd byte rotates need QImode. | |
4507 | Word move if no scratch is needed, otherwise use size of scratch. */ | |
4508 | enum machine_mode move_mode = QImode; | |
3f02a5f3 JR |
4509 | int move_size, offset, size; |
4510 | ||
58f0ea2f AH |
4511 | if (num & 0xf) |
4512 | move_mode = QImode; | |
4513 | else if ((mode == SImode && !same_reg) || !overlapped) | |
4514 | move_mode = HImode; | |
4515 | else | |
4516 | move_mode = GET_MODE (scratch); | |
4517 | ||
4518 | /* Force DI rotate to use QI moves since other DI moves are currently split | |
4519 | into QI moves so forward propagation works better. */ | |
4520 | if (mode == DImode) | |
4521 | move_mode = QImode; | |
4522 | /* Make scratch smaller if needed. */ | |
4523 | if (GET_MODE (scratch) == HImode && move_mode == QImode) | |
4524 | scratch = simplify_gen_subreg (move_mode, scratch, HImode, 0); | |
4525 | ||
3f02a5f3 | 4526 | move_size = GET_MODE_SIZE (move_mode); |
58f0ea2f | 4527 | /* Number of bytes/words to rotate. */ |
3f02a5f3 | 4528 | offset = (num >> 3) / move_size; |
58f0ea2f | 4529 | /* Number of moves needed. */ |
3f02a5f3 | 4530 | size = GET_MODE_SIZE (mode) / move_size; |
58f0ea2f AH |
4531 | /* Himode byte swap is special case to avoid a scratch register. */ |
4532 | if (mode == HImode && same_reg) | |
4533 | { | |
4534 | /* HImode byte swap, using xor. This is as quick as using scratch. */ | |
4535 | rtx src, dst; | |
4536 | src = simplify_gen_subreg (move_mode, operands[1], mode, 0); | |
4537 | dst = simplify_gen_subreg (move_mode, operands[0], mode, 1); | |
4538 | if (!rtx_equal_p (dst, src)) | |
4539 | { | |
4540 | emit_move_insn (dst, gen_rtx_XOR (QImode, dst, src)); | |
4541 | emit_move_insn (src, gen_rtx_XOR (QImode, src, dst)); | |
4542 | emit_move_insn (dst, gen_rtx_XOR (QImode, dst, src)); | |
4543 | } | |
4544 | } | |
4545 | else | |
4546 | { | |
3f02a5f3 | 4547 | #define MAX_SIZE 8 /* GET_MODE_SIZE (DImode) / GET_MODE_SIZE (QImode) */ |
58f0ea2f AH |
4548 | /* Create linked list of moves to determine move order. */ |
4549 | struct { | |
4550 | rtx src, dst; | |
4551 | int links; | |
3f02a5f3 JR |
4552 | } move[MAX_SIZE + 8]; |
4553 | int blocked, moves; | |
58f0ea2f | 4554 | |
3f02a5f3 | 4555 | gcc_assert (size <= MAX_SIZE); |
58f0ea2f AH |
4556 | /* Generate list of subreg moves. */ |
4557 | for (i = 0; i < size; i++) | |
4558 | { | |
4559 | int from = i; | |
4560 | int to = (from + offset) % size; | |
4561 | move[i].src = simplify_gen_subreg (move_mode, operands[1], | |
4562 | mode, from * move_size); | |
4563 | move[i].dst = simplify_gen_subreg (move_mode, operands[0], | |
4564 | mode, to * move_size); | |
4565 | move[i].links = -1; | |
4566 | } | |
4567 | /* Mark dependence where a dst of one move is the src of another move. | |
4568 | The first move is a conflict as it must wait until second is | |
4569 | performed. We ignore moves to self - we catch this later. */ | |
4570 | if (overlapped) | |
4571 | for (i = 0; i < size; i++) | |
4572 | if (reg_overlap_mentioned_p (move[i].dst, operands[1])) | |
4573 | for (j = 0; j < size; j++) | |
4574 | if (j != i && rtx_equal_p (move[j].src, move[i].dst)) | |
4575 | { | |
4576 | /* The dst of move i is the src of move j. */ | |
4577 | move[i].links = j; | |
4578 | break; | |
4579 | } | |
4580 | ||
3f02a5f3 JR |
4581 | blocked = -1; |
4582 | moves = 0; | |
58f0ea2f AH |
4583 | /* Go through move list and perform non-conflicting moves. As each |
4584 | non-overlapping move is made, it may remove other conflicts | |
4585 | so the process is repeated until no conflicts remain. */ | |
4586 | do | |
4587 | { | |
4588 | blocked = -1; | |
4589 | moves = 0; | |
4590 | /* Emit move where dst is not also a src or we have used that | |
4591 | src already. */ | |
4592 | for (i = 0; i < size; i++) | |
4593 | if (move[i].src != NULL_RTX) | |
3f02a5f3 JR |
4594 | { |
4595 | if (move[i].links == -1 | |
4596 | || move[move[i].links].src == NULL_RTX) | |
4597 | { | |
4598 | moves++; | |
4599 | /* Ignore NOP moves to self. */ | |
4600 | if (!rtx_equal_p (move[i].dst, move[i].src)) | |
4601 | emit_move_insn (move[i].dst, move[i].src); | |
58f0ea2f | 4602 | |
3f02a5f3 JR |
4603 | /* Remove conflict from list. */ |
4604 | move[i].src = NULL_RTX; | |
4605 | } | |
4606 | else | |
4607 | blocked = i; | |
4608 | } | |
58f0ea2f AH |
4609 | |
4610 | /* Check for deadlock. This is when no moves occurred and we have | |
4611 | at least one blocked move. */ | |
4612 | if (moves == 0 && blocked != -1) | |
4613 | { | |
4614 | /* Need to use scratch register to break deadlock. | |
4615 | Add move to put dst of blocked move into scratch. | |
4616 | When this move occurs, it will break chain deadlock. | |
4617 | The scratch register is substituted for real move. */ | |
4618 | ||
4619 | move[size].src = move[blocked].dst; | |
4620 | move[size].dst = scratch; | |
4621 | /* Scratch move is never blocked. */ | |
4622 | move[size].links = -1; | |
4623 | /* Make sure we have valid link. */ | |
4624 | gcc_assert (move[blocked].links != -1); | |
4625 | /* Replace src of blocking move with scratch reg. */ | |
4626 | move[move[blocked].links].src = scratch; | |
4627 | /* Make dependent on scratch move occuring. */ | |
4628 | move[blocked].links = size; | |
4629 | size=size+1; | |
4630 | } | |
4631 | } | |
4632 | while (blocked != -1); | |
4633 | } | |
4634 | return true; | |
4635 | } | |
4636 | ||
90e7678c DC |
4637 | /* Modifies the length assigned to instruction INSN |
4638 | LEN is the initially computed length of the insn. */ | |
4639 | ||
4640 | int | |
269e3795 | 4641 | adjust_insn_length (rtx insn, int len) |
90e7678c DC |
4642 | { |
4643 | rtx patt = PATTERN (insn); | |
4644 | rtx set; | |
155b14a8 | 4645 | |
90e7678c DC |
4646 | if (GET_CODE (patt) == SET) |
4647 | { | |
4648 | rtx op[10]; | |
4649 | op[1] = SET_SRC (patt); | |
4650 | op[0] = SET_DEST (patt); | |
6bec29c9 DC |
4651 | if (general_operand (op[1], VOIDmode) |
4652 | && general_operand (op[0], VOIDmode)) | |
4653 | { | |
4654 | switch (GET_MODE (op[0])) | |
4655 | { | |
4656 | case QImode: | |
4657 | output_movqi (insn, op, &len); | |
4658 | break; | |
4659 | case HImode: | |
4660 | output_movhi (insn, op, &len); | |
4661 | break; | |
4662 | case SImode: | |
4663 | case SFmode: | |
4664 | output_movsisf (insn, op, &len); | |
4665 | break; | |
4666 | default: | |
4667 | break; | |
4668 | } | |
4669 | } | |
90e7678c DC |
4670 | else if (op[0] == cc0_rtx && REG_P (op[1])) |
4671 | { | |
4672 | switch (GET_MODE (op[1])) | |
4673 | { | |
f90b7a5a PB |
4674 | case HImode: out_tsthi (insn, op[1], &len); break; |
4675 | case SImode: out_tstsi (insn, op[1], &len); break; | |
90e7678c DC |
4676 | default: break; |
4677 | } | |
4678 | } | |
4679 | else if (GET_CODE (op[1]) == AND) | |
4680 | { | |
4681 | if (GET_CODE (XEXP (op[1],1)) == CONST_INT) | |
4682 | { | |
4683 | HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1)); | |
4684 | if (GET_MODE (op[1]) == SImode) | |
4685 | len = (((mask & 0xff) != 0xff) | |
4686 | + ((mask & 0xff00) != 0xff00) | |
11f9ed1a KG |
4687 | + ((mask & 0xff0000L) != 0xff0000L) |
4688 | + ((mask & 0xff000000L) != 0xff000000L)); | |
90e7678c DC |
4689 | else if (GET_MODE (op[1]) == HImode) |
4690 | len = (((mask & 0xff) != 0xff) | |
4691 | + ((mask & 0xff00) != 0xff00)); | |
4692 | } | |
4693 | } | |
4694 | else if (GET_CODE (op[1]) == IOR) | |
4695 | { | |
4696 | if (GET_CODE (XEXP (op[1],1)) == CONST_INT) | |
4697 | { | |
4698 | HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1)); | |
4699 | if (GET_MODE (op[1]) == SImode) | |
5fecfd8d MM |
4700 | len = (((mask & 0xff) != 0) |
4701 | + ((mask & 0xff00) != 0) | |
11f9ed1a KG |
4702 | + ((mask & 0xff0000L) != 0) |
4703 | + ((mask & 0xff000000L) != 0)); | |
90e7678c | 4704 | else if (GET_MODE (op[1]) == HImode) |
5fecfd8d MM |
4705 | len = (((mask & 0xff) != 0) |
4706 | + ((mask & 0xff00) != 0)); | |
90e7678c DC |
4707 | } |
4708 | } | |
4709 | } | |
4710 | set = single_set (insn); | |
4711 | if (set) | |
4712 | { | |
4713 | rtx op[10]; | |
155b14a8 | 4714 | |
90e7678c DC |
4715 | op[1] = SET_SRC (set); |
4716 | op[0] = SET_DEST (set); | |
155b14a8 DC |
4717 | |
4718 | if (GET_CODE (patt) == PARALLEL | |
4719 | && general_operand (op[1], VOIDmode) | |
4720 | && general_operand (op[0], VOIDmode)) | |
4721 | { | |
4722 | if (XVECLEN (patt, 0) == 2) | |
4723 | op[2] = XVECEXP (patt, 0, 1); | |
4724 | ||
4725 | switch (GET_MODE (op[0])) | |
4726 | { | |
4727 | case QImode: | |
4728 | len = 2; | |
4729 | break; | |
4730 | case HImode: | |
4731 | output_reload_inhi (insn, op, &len); | |
4732 | break; | |
4733 | case SImode: | |
4734 | case SFmode: | |
4735 | output_reload_insisf (insn, op, &len); | |
4736 | break; | |
4737 | default: | |
4738 | break; | |
4739 | } | |
4740 | } | |
4741 | else if (GET_CODE (op[1]) == ASHIFT | |
90e7678c DC |
4742 | || GET_CODE (op[1]) == ASHIFTRT |
4743 | || GET_CODE (op[1]) == LSHIFTRT) | |
4744 | { | |
4745 | rtx ops[10]; | |
4746 | ops[0] = op[0]; | |
4747 | ops[1] = XEXP (op[1],0); | |
4748 | ops[2] = XEXP (op[1],1); | |
4749 | switch (GET_CODE (op[1])) | |
4750 | { | |
4751 | case ASHIFT: | |
4752 | switch (GET_MODE (op[0])) | |
4753 | { | |
4754 | case QImode: ashlqi3_out (insn,ops,&len); break; | |
4755 | case HImode: ashlhi3_out (insn,ops,&len); break; | |
4756 | case SImode: ashlsi3_out (insn,ops,&len); break; | |
4757 | default: break; | |
4758 | } | |
4759 | break; | |
4760 | case ASHIFTRT: | |
4761 | switch (GET_MODE (op[0])) | |
4762 | { | |
4763 | case QImode: ashrqi3_out (insn,ops,&len); break; | |
4764 | case HImode: ashrhi3_out (insn,ops,&len); break; | |
4765 | case SImode: ashrsi3_out (insn,ops,&len); break; | |
4766 | default: break; | |
4767 | } | |
4768 | break; | |
4769 | case LSHIFTRT: | |
4770 | switch (GET_MODE (op[0])) | |
4771 | { | |
4772 | case QImode: lshrqi3_out (insn,ops,&len); break; | |
4773 | case HImode: lshrhi3_out (insn,ops,&len); break; | |
4774 | case SImode: lshrsi3_out (insn,ops,&len); break; | |
4775 | default: break; | |
4776 | } | |
4777 | break; | |
4778 | default: | |
4779 | break; | |
4780 | } | |
4781 | } | |
4782 | } | |
4783 | return len; | |
4784 | } | |
4785 | ||
d6b4baa4 | 4786 | /* Return nonzero if register REG dead after INSN. */ |
90e7678c DC |
4787 | |
4788 | int | |
269e3795 | 4789 | reg_unused_after (rtx insn, rtx reg) |
90e7678c | 4790 | { |
6bec29c9 | 4791 | return (dead_or_set_p (insn, reg) |
90e7678c DC |
4792 | || (REG_P(reg) && _reg_unused_after (insn, reg))); |
4793 | } | |
4794 | ||
825dda42 | 4795 | /* Return nonzero if REG is not used after INSN. |
90e7678c DC |
4796 | We assume REG is a reload reg, and therefore does |
4797 | not live past labels. It may live past calls or jumps though. */ | |
4798 | ||
4799 | int | |
269e3795 | 4800 | _reg_unused_after (rtx insn, rtx reg) |
90e7678c DC |
4801 | { |
4802 | enum rtx_code code; | |
4803 | rtx set; | |
4804 | ||
4805 | /* If the reg is set by this instruction, then it is safe for our | |
4806 | case. Disregard the case where this is a store to memory, since | |
4807 | we are checking a register used in the store address. */ | |
4808 | set = single_set (insn); | |
4809 | if (set && GET_CODE (SET_DEST (set)) != MEM | |
4810 | && reg_overlap_mentioned_p (reg, SET_DEST (set))) | |
4811 | return 1; | |
4812 | ||
4813 | while ((insn = NEXT_INSN (insn))) | |
4814 | { | |
ec8e098d | 4815 | rtx set; |
90e7678c DC |
4816 | code = GET_CODE (insn); |
4817 | ||
4818 | #if 0 | |
4819 | /* If this is a label that existed before reload, then the register | |
4820 | if dead here. However, if this is a label added by reorg, then | |
4821 | the register may still be live here. We can't tell the difference, | |
4822 | so we just ignore labels completely. */ | |
4823 | if (code == CODE_LABEL) | |
4824 | return 1; | |
4825 | /* else */ | |
4826 | #endif | |
4827 | ||
ec8e098d PB |
4828 | if (!INSN_P (insn)) |
4829 | continue; | |
4830 | ||
90e7678c DC |
4831 | if (code == JUMP_INSN) |
4832 | return 0; | |
4833 | ||
4834 | /* If this is a sequence, we must handle them all at once. | |
4835 | We could have for instance a call that sets the target register, | |
e03f5d43 | 4836 | and an insn in a delay slot that uses the register. In this case, |
90e7678c DC |
4837 | we must return 0. */ |
4838 | else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) | |
4839 | { | |
4840 | int i; | |
4841 | int retval = 0; | |
4842 | ||
4843 | for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) | |
4844 | { | |
4845 | rtx this_insn = XVECEXP (PATTERN (insn), 0, i); | |
4846 | rtx set = single_set (this_insn); | |
4847 | ||
4848 | if (GET_CODE (this_insn) == CALL_INSN) | |
4849 | code = CALL_INSN; | |
4850 | else if (GET_CODE (this_insn) == JUMP_INSN) | |
4851 | { | |
4852 | if (INSN_ANNULLED_BRANCH_P (this_insn)) | |
4853 | return 0; | |
4854 | code = JUMP_INSN; | |
4855 | } | |
4856 | ||
4857 | if (set && reg_overlap_mentioned_p (reg, SET_SRC (set))) | |
4858 | return 0; | |
4859 | if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) | |
4860 | { | |
4861 | if (GET_CODE (SET_DEST (set)) != MEM) | |
4862 | retval = 1; | |
4863 | else | |
4864 | return 0; | |
4865 | } | |
4866 | if (set == 0 | |
4867 | && reg_overlap_mentioned_p (reg, PATTERN (this_insn))) | |
4868 | return 0; | |
4869 | } | |
4870 | if (retval == 1) | |
4871 | return 1; | |
4872 | else if (code == JUMP_INSN) | |
4873 | return 0; | |
4874 | } | |
4875 | ||
4876 | if (code == CALL_INSN) | |
4877 | { | |
4878 | rtx tem; | |
4879 | for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) | |
4880 | if (GET_CODE (XEXP (tem, 0)) == USE | |
4881 | && REG_P (XEXP (XEXP (tem, 0), 0)) | |
4882 | && reg_overlap_mentioned_p (reg, XEXP (XEXP (tem, 0), 0))) | |
4883 | return 0; | |
4884 | if (call_used_regs[REGNO (reg)]) | |
4885 | return 1; | |
4886 | } | |
4887 | ||
ec8e098d | 4888 | set = single_set (insn); |
90e7678c | 4889 | |
ec8e098d PB |
4890 | if (set && reg_overlap_mentioned_p (reg, SET_SRC (set))) |
4891 | return 0; | |
4892 | if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) | |
4893 | return GET_CODE (SET_DEST (set)) != MEM; | |
4894 | if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn))) | |
4895 | return 0; | |
90e7678c DC |
4896 | } |
4897 | return 1; | |
4898 | } | |
4899 | ||
301d03af RS |
4900 | /* Target hook for assembling integer objects. The AVR version needs |
4901 | special handling for references to certain labels. */ | |
90e7678c | 4902 | |
301d03af | 4903 | static bool |
269e3795 | 4904 | avr_assemble_integer (rtx x, unsigned int size, int aligned_p) |
90e7678c | 4905 | { |
301d03af | 4906 | if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p |
846428f1 | 4907 | && text_segment_operand (x, VOIDmode) ) |
90e7678c | 4908 | { |
693092fb | 4909 | fputs ("\t.word\tgs(", asm_out_file); |
301d03af RS |
4910 | output_addr_const (asm_out_file, x); |
4911 | fputs (")\n", asm_out_file); | |
4912 | return true; | |
90e7678c | 4913 | } |
301d03af | 4914 | return default_assemble_integer (x, size, aligned_p); |
90e7678c DC |
4915 | } |
4916 | ||
2cb4817b AS |
4917 | /* Worker function for ASM_DECLARE_FUNCTION_NAME. */ |
4918 | ||
4919 | void | |
4920 | avr_asm_declare_function_name (FILE *file, const char *name, tree decl) | |
4921 | { | |
4922 | ||
4923 | /* If the function has the 'signal' or 'interrupt' attribute, test to | |
4924 | make sure that the name of the function is "__vector_NN" so as to | |
4925 | catch when the user misspells the interrupt vector name. */ | |
4926 | ||
4927 | if (cfun->machine->is_interrupt) | |
4928 | { | |
4929 | if (strncmp (name, "__vector", strlen ("__vector")) != 0) | |
4930 | { | |
4931 | warning_at (DECL_SOURCE_LOCATION (decl), 0, | |
4932 | "%qs appears to be a misspelled interrupt handler", | |
4933 | name); | |
4934 | } | |
4935 | } | |
4936 | else if (cfun->machine->is_signal) | |
4937 | { | |
4938 | if (strncmp (name, "__vector", strlen ("__vector")) != 0) | |
4939 | { | |
4940 | warning_at (DECL_SOURCE_LOCATION (decl), 0, | |
4941 | "%qs appears to be a misspelled signal handler", | |
4942 | name); | |
4943 | } | |
4944 | } | |
4945 | ||
4946 | ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function"); | |
4947 | ASM_OUTPUT_LABEL (file, name); | |
4948 | } | |
4949 | ||
90e7678c DC |
4950 | /* The routine used to output NUL terminated strings. We use a special |
4951 | version of this for most svr4 targets because doing so makes the | |
4952 | generated assembly code more compact (and thus faster to assemble) | |
4953 | as well as more readable, especially for targets like the i386 | |
4954 | (where the only alternative is to output character sequences as | |
d6b4baa4 | 4955 | comma separated lists of numbers). */ |
90e7678c DC |
4956 | |
4957 | void | |
269e3795 | 4958 | gas_output_limited_string(FILE *file, const char *str) |
90e7678c | 4959 | { |
586de218 | 4960 | const unsigned char *_limited_str = (const unsigned char *) str; |
90e7678c | 4961 | unsigned ch; |
8202cda0 | 4962 | fprintf (file, "%s\"", STRING_ASM_OP); |
90e7678c DC |
4963 | for (; (ch = *_limited_str); _limited_str++) |
4964 | { | |
4965 | int escape; | |
4966 | switch (escape = ESCAPES[ch]) | |
4967 | { | |
4968 | case 0: | |
4969 | putc (ch, file); | |
4970 | break; | |
4971 | case 1: | |
4972 | fprintf (file, "\\%03o", ch); | |
4973 | break; | |
4974 | default: | |
4975 | putc ('\\', file); | |
4976 | putc (escape, file); | |
4977 | break; | |
4978 | } | |
4979 | } | |
4980 | fprintf (file, "\"\n"); | |
4981 | } | |
4982 | ||
4983 | /* The routine used to output sequences of byte values. We use a special | |
4984 | version of this for most svr4 targets because doing so makes the | |
4985 | generated assembly code more compact (and thus faster to assemble) | |
4986 | as well as more readable. Note that if we find subparts of the | |
4987 | character sequence which end with NUL (and which are shorter than | |
4988 | STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */ | |
4989 | ||
4990 | void | |
269e3795 | 4991 | gas_output_ascii(FILE *file, const char *str, size_t length) |
90e7678c | 4992 | { |
5fecfd8d MM |
4993 | const unsigned char *_ascii_bytes = (const unsigned char *) str; |
4994 | const unsigned char *limit = _ascii_bytes + length; | |
90e7678c DC |
4995 | unsigned bytes_in_chunk = 0; |
4996 | for (; _ascii_bytes < limit; _ascii_bytes++) | |
4997 | { | |
5fecfd8d | 4998 | const unsigned char *p; |
90e7678c DC |
4999 | if (bytes_in_chunk >= 60) |
5000 | { | |
5001 | fprintf (file, "\"\n"); | |
5002 | bytes_in_chunk = 0; | |
5003 | } | |
5004 | for (p = _ascii_bytes; p < limit && *p != '\0'; p++) | |
5005 | continue; | |
5006 | if (p < limit && (p - _ascii_bytes) <= (signed)STRING_LIMIT) | |
5007 | { | |
5008 | if (bytes_in_chunk > 0) | |
5009 | { | |
5010 | fprintf (file, "\"\n"); | |
5011 | bytes_in_chunk = 0; | |
5012 | } | |
586de218 | 5013 | gas_output_limited_string (file, (const char*)_ascii_bytes); |
90e7678c DC |
5014 | _ascii_bytes = p; |
5015 | } | |
5016 | else | |
5017 | { | |
5018 | int escape; | |
5019 | unsigned ch; | |
5020 | if (bytes_in_chunk == 0) | |
5021 | fprintf (file, "\t.ascii\t\""); | |
5022 | switch (escape = ESCAPES[ch = *_ascii_bytes]) | |
5023 | { | |
5024 | case 0: | |
5025 | putc (ch, file); | |
5026 | bytes_in_chunk++; | |
5027 | break; | |
5028 | case 1: | |
5029 | fprintf (file, "\\%03o", ch); | |
5030 | bytes_in_chunk += 4; | |
5031 | break; | |
5032 | default: | |
5033 | putc ('\\', file); | |
5034 | putc (escape, file); | |
5035 | bytes_in_chunk += 2; | |
5036 | break; | |
5037 | } | |
5038 | } | |
5039 | } | |
5040 | if (bytes_in_chunk > 0) | |
5041 | fprintf (file, "\"\n"); | |
5042 | } | |
5043 | ||
5044 | /* Return value is nonzero if pseudos that have been | |
5045 | assigned to registers of class CLASS would likely be spilled | |
5046 | because registers of CLASS are needed for spill registers. */ | |
5047 | ||
c21136ee AS |
5048 | static bool |
5049 | avr_class_likely_spilled_p (reg_class_t c) | |
90e7678c DC |
5050 | { |
5051 | return (c != ALL_REGS && c != ADDW_REGS); | |
5052 | } | |
5053 | ||
91d231cb | 5054 | /* Valid attributes: |
90e7678c DC |
5055 | progmem - put data to program memory; |
5056 | signal - make a function to be hardware interrupt. After function | |
c4984bad | 5057 | prologue interrupts are disabled; |
90e7678c | 5058 | interrupt - make a function to be hardware interrupt. After function |
c4984bad | 5059 | prologue interrupts are enabled; |
91d231cb | 5060 | naked - don't generate function prologue/epilogue and `ret' command. |
90e7678c | 5061 | |
91d231cb JM |
5062 | Only `progmem' attribute valid for type. */ |
5063 | ||
91d231cb JM |
5064 | /* Handle a "progmem" attribute; arguments as in |
5065 | struct attribute_spec.handler. */ | |
5066 | static tree | |
269e3795 SB |
5067 | avr_handle_progmem_attribute (tree *node, tree name, |
5068 | tree args ATTRIBUTE_UNUSED, | |
5069 | int flags ATTRIBUTE_UNUSED, | |
5070 | bool *no_add_attrs) | |
91d231cb JM |
5071 | { |
5072 | if (DECL_P (*node)) | |
90e7678c | 5073 | { |
b47cae3d MM |
5074 | if (TREE_CODE (*node) == TYPE_DECL) |
5075 | { | |
5076 | /* This is really a decl attribute, not a type attribute, | |
5077 | but try to handle it for GCC 3.0 backwards compatibility. */ | |
5078 | ||
5079 | tree type = TREE_TYPE (*node); | |
5080 | tree attr = tree_cons (name, args, TYPE_ATTRIBUTES (type)); | |
5081 | tree newtype = build_type_attribute_variant (type, attr); | |
5082 | ||
5083 | TYPE_MAIN_VARIANT (newtype) = TYPE_MAIN_VARIANT (type); | |
5084 | TREE_TYPE (*node) = newtype; | |
5085 | *no_add_attrs = true; | |
5086 | } | |
5087 | else if (TREE_STATIC (*node) || DECL_EXTERNAL (*node)) | |
90e7678c | 5088 | { |
91d231cb JM |
5089 | if (DECL_INITIAL (*node) == NULL_TREE && !DECL_EXTERNAL (*node)) |
5090 | { | |
d4ee4d25 | 5091 | warning (0, "only initialized variables can be placed into " |
c725bd79 | 5092 | "program memory area"); |
91d231cb JM |
5093 | *no_add_attrs = true; |
5094 | } | |
5095 | } | |
5096 | else | |
5097 | { | |
29d08eba JM |
5098 | warning (OPT_Wattributes, "%qE attribute ignored", |
5099 | name); | |
91d231cb | 5100 | *no_add_attrs = true; |
90e7678c | 5101 | } |
90e7678c | 5102 | } |
91d231cb JM |
5103 | |
5104 | return NULL_TREE; | |
90e7678c DC |
5105 | } |
5106 | ||
91d231cb JM |
5107 | /* Handle an attribute requiring a FUNCTION_DECL; arguments as in |
5108 | struct attribute_spec.handler. */ | |
269e3795 | 5109 | |
91d231cb | 5110 | static tree |
269e3795 SB |
5111 | avr_handle_fndecl_attribute (tree *node, tree name, |
5112 | tree args ATTRIBUTE_UNUSED, | |
5113 | int flags ATTRIBUTE_UNUSED, | |
5114 | bool *no_add_attrs) | |
91d231cb JM |
5115 | { |
5116 | if (TREE_CODE (*node) != FUNCTION_DECL) | |
5117 | { | |
29d08eba JM |
5118 | warning (OPT_Wattributes, "%qE attribute only applies to functions", |
5119 | name); | |
91d231cb JM |
5120 | *no_add_attrs = true; |
5121 | } | |
5122 | ||
5123 | return NULL_TREE; | |
5124 | } | |
90e7678c | 5125 | |
44190aed AS |
5126 | static tree |
5127 | avr_handle_fntype_attribute (tree *node, tree name, | |
5128 | tree args ATTRIBUTE_UNUSED, | |
5129 | int flags ATTRIBUTE_UNUSED, | |
5130 | bool *no_add_attrs) | |
5131 | { | |
5132 | if (TREE_CODE (*node) != FUNCTION_TYPE) | |
5133 | { | |
29d08eba JM |
5134 | warning (OPT_Wattributes, "%qE attribute only applies to functions", |
5135 | name); | |
44190aed AS |
5136 | *no_add_attrs = true; |
5137 | } | |
5138 | ||
5139 | return NULL_TREE; | |
5140 | } | |
5141 | ||
90e7678c | 5142 | /* Look for attribute `progmem' in DECL |
c4984bad | 5143 | if found return 1, otherwise 0. */ |
90e7678c DC |
5144 | |
5145 | int | |
427ee360 | 5146 | avr_progmem_p (tree decl, tree attributes) |
90e7678c DC |
5147 | { |
5148 | tree a; | |
5149 | ||
5150 | if (TREE_CODE (decl) != VAR_DECL) | |
5151 | return 0; | |
5152 | ||
5153 | if (NULL_TREE | |
427ee360 | 5154 | != lookup_attribute ("progmem", attributes)) |
90e7678c DC |
5155 | return 1; |
5156 | ||
5157 | a=decl; | |
5158 | do | |
5159 | a = TREE_TYPE(a); | |
5160 | while (TREE_CODE (a) == ARRAY_TYPE); | |
5161 | ||
89741abb MM |
5162 | if (a == error_mark_node) |
5163 | return 0; | |
5164 | ||
90e7678c DC |
5165 | if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a))) |
5166 | return 1; | |
5167 | ||
5168 | return 0; | |
5169 | } | |
5170 | ||
74b66b3c RH |
5171 | /* Add the section attribute if the variable is in progmem. */ |
5172 | ||
fb49053f | 5173 | static void |
269e3795 | 5174 | avr_insert_attributes (tree node, tree *attributes) |
90e7678c | 5175 | { |
74b66b3c RH |
5176 | if (TREE_CODE (node) == VAR_DECL |
5177 | && (TREE_STATIC (node) || DECL_EXTERNAL (node)) | |
427ee360 | 5178 | && avr_progmem_p (node, *attributes)) |
90e7678c | 5179 | { |
8a9b55f3 GJL |
5180 | if (TREE_READONLY (node)) |
5181 | { | |
5182 | static const char dsec[] = ".progmem.data"; | |
5183 | ||
5184 | *attributes = tree_cons (get_identifier ("section"), | |
5185 | build_tree_list (NULL, build_string (strlen (dsec), dsec)), | |
5186 | *attributes); | |
5187 | } | |
5188 | else | |
5189 | { | |
5190 | error ("variable %q+D must be const in order to be put into" | |
5191 | " read-only section by means of %<__attribute__((progmem))%>", | |
5192 | node); | |
5193 | } | |
90e7678c | 5194 | } |
772c5265 | 5195 | } |
90e7678c | 5196 | |
d6b5193b RS |
5197 | /* A get_unnamed_section callback for switching to progmem_section. */ |
5198 | ||
5199 | static void | |
5200 | avr_output_progmem_section_asm_op (const void *arg ATTRIBUTE_UNUSED) | |
5201 | { | |
5202 | fprintf (asm_out_file, | |
5203 | "\t.section .progmem.gcc_sw_table, \"%s\", @progbits\n", | |
5dc77808 | 5204 | AVR_HAVE_JMP_CALL ? "a" : "ax"); |
d6b5193b RS |
5205 | /* Should already be aligned, this is just to be safe if it isn't. */ |
5206 | fprintf (asm_out_file, "\t.p2align 1\n"); | |
5207 | } | |
5208 | ||
516edfdd GJL |
5209 | |
5210 | /* Implement `ASM_OUTPUT_ALIGNED_DECL_LOCAL'. */ | |
5211 | /* Implement `ASM_OUTPUT_ALIGNED_DECL_COMMON'. */ | |
5212 | /* Track need of __do_clear_bss. */ | |
5213 | ||
5214 | void | |
5215 | avr_asm_output_aligned_decl_common (FILE * stream, const_tree decl ATTRIBUTE_UNUSED, | |
5216 | const char *name, unsigned HOST_WIDE_INT size, | |
5217 | unsigned int align, bool local_p) | |
5218 | { | |
5219 | avr_need_clear_bss_p = true; | |
5220 | ||
5221 | if (local_p) | |
5222 | { | |
5223 | fputs ("\t.local\t", stream); | |
5224 | assemble_name (stream, name); | |
5225 | fputs ("\n", stream); | |
5226 | } | |
5227 | ||
5228 | fputs ("\t.comm\t", stream); | |
5229 | assemble_name (stream, name); | |
5230 | fprintf (stream, | |
5231 | "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n", | |
5232 | size, align / BITS_PER_UNIT); | |
5233 | } | |
5234 | ||
5235 | ||
5236 | /* Unnamed section callback for data_section | |
5237 | to track need of __do_copy_data. */ | |
5238 | ||
5239 | static void | |
5240 | avr_output_data_section_asm_op (const void *data) | |
5241 | { | |
5242 | avr_need_copy_data_p = true; | |
5243 | ||
5244 | /* Dispatch to default. */ | |
5245 | output_section_asm_op (data); | |
5246 | } | |
5247 | ||
5248 | ||
5249 | /* Unnamed section callback for bss_section | |
5250 | to track need of __do_clear_bss. */ | |
5251 | ||
5252 | static void | |
5253 | avr_output_bss_section_asm_op (const void *data) | |
5254 | { | |
5255 | avr_need_clear_bss_p = true; | |
5256 | ||
5257 | /* Dispatch to default. */ | |
5258 | output_section_asm_op (data); | |
5259 | } | |
5260 | ||
5261 | ||
5262 | /* Implement `TARGET_ASM_INIT_SECTIONS'. */ | |
d6b5193b RS |
5263 | |
5264 | static void | |
5265 | avr_asm_init_sections (void) | |
5266 | { | |
5dc77808 | 5267 | progmem_section = get_unnamed_section (AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE, |
d6b5193b RS |
5268 | avr_output_progmem_section_asm_op, |
5269 | NULL); | |
5270 | readonly_data_section = data_section; | |
516edfdd GJL |
5271 | |
5272 | data_section->unnamed.callback = avr_output_data_section_asm_op; | |
5273 | bss_section->unnamed.callback = avr_output_bss_section_asm_op; | |
5274 | } | |
5275 | ||
5276 | ||
5277 | /* Implement `TARGET_ASM_NAMED_SECTION'. */ | |
5278 | /* Track need of __do_clear_bss, __do_copy_data for named sections. */ | |
5279 | ||
5280 | void | |
5281 | avr_asm_named_section (const char *name, unsigned int flags, tree decl) | |
5282 | { | |
5283 | if (!avr_need_copy_data_p) | |
5284 | avr_need_copy_data_p = (0 == strncmp (name, ".data", 5) | |
5285 | || 0 == strncmp (name, ".rodata", 7) | |
5286 | || 0 == strncmp (name, ".gnu.linkonce.d", 15)); | |
5287 | ||
5288 | if (!avr_need_clear_bss_p) | |
5289 | avr_need_clear_bss_p = (0 == strncmp (name, ".bss", 4)); | |
5290 | ||
5291 | default_elf_asm_named_section (name, flags, decl); | |
d6b5193b RS |
5292 | } |
5293 | ||
c16e5a35 | 5294 | static unsigned int |
269e3795 | 5295 | avr_section_type_flags (tree decl, const char *name, int reloc) |
c16e5a35 MM |
5296 | { |
5297 | unsigned int flags = default_section_type_flags (decl, name, reloc); | |
5298 | ||
5299 | if (strncmp (name, ".noinit", 7) == 0) | |
5300 | { | |
5301 | if (decl && TREE_CODE (decl) == VAR_DECL | |
5302 | && DECL_INITIAL (decl) == NULL_TREE) | |
5303 | flags |= SECTION_BSS; /* @nobits */ | |
5304 | else | |
d4ee4d25 | 5305 | warning (0, "only uninitialized variables can be placed in the " |
c16e5a35 MM |
5306 | ".noinit section"); |
5307 | } | |
5308 | ||
5309 | return flags; | |
5310 | } | |
5311 | ||
516edfdd GJL |
5312 | |
5313 | /* Implement `TARGET_ASM_FILE_START'. */ | |
1bc7c5b6 ZW |
5314 | /* Outputs some appropriate text to go at the start of an assembler |
5315 | file. */ | |
90e7678c | 5316 | |
1bc7c5b6 | 5317 | static void |
269e3795 | 5318 | avr_file_start (void) |
90e7678c | 5319 | { |
470a4c97 | 5320 | if (avr_current_arch->asm_only) |
9e637a26 | 5321 | error ("MCU %qs supported for assembler only", avr_mcu_name); |
bcb6a2be | 5322 | |
1bc7c5b6 ZW |
5323 | default_file_start (); |
5324 | ||
4301ec4f | 5325 | /* fprintf (asm_out_file, "\t.arch %s\n", avr_mcu_name);*/ |
90e7678c DC |
5326 | fputs ("__SREG__ = 0x3f\n" |
5327 | "__SP_H__ = 0x3e\n" | |
1bc7c5b6 | 5328 | "__SP_L__ = 0x3d\n", asm_out_file); |
90e7678c | 5329 | |
368256d8 | 5330 | fputs ("__tmp_reg__ = 0\n" |
1bc7c5b6 | 5331 | "__zero_reg__ = 1\n", asm_out_file); |
90e7678c DC |
5332 | } |
5333 | ||
516edfdd GJL |
5334 | |
5335 | /* Implement `TARGET_ASM_FILE_END'. */ | |
90e7678c DC |
5336 | /* Outputs to the stdio stream FILE some |
5337 | appropriate text to go at the end of an assembler file. */ | |
5338 | ||
a5fe455b | 5339 | static void |
269e3795 | 5340 | avr_file_end (void) |
90e7678c | 5341 | { |
516edfdd GJL |
5342 | /* Output these only if there is anything in the |
5343 | .data* / .rodata* / .gnu.linkonce.* resp. .bss* | |
5344 | input section(s) - some code size can be saved by not | |
5345 | linking in the initialization code from libgcc if resp. | |
5346 | sections are empty. */ | |
5347 | ||
5348 | if (avr_need_copy_data_p) | |
5349 | fputs (".global __do_copy_data\n", asm_out_file); | |
5350 | ||
5351 | if (avr_need_clear_bss_p) | |
5352 | fputs (".global __do_clear_bss\n", asm_out_file); | |
90e7678c DC |
5353 | } |
5354 | ||
5355 | /* Choose the order in which to allocate hard registers for | |
5356 | pseudo-registers local to a basic block. | |
5357 | ||
5358 | Store the desired register order in the array `reg_alloc_order'. | |
5359 | Element 0 should be the register to allocate first; element 1, the | |
5360 | next register; and so on. */ | |
5361 | ||
5362 | void | |
269e3795 | 5363 | order_regs_for_local_alloc (void) |
90e7678c DC |
5364 | { |
5365 | unsigned int i; | |
8b60264b | 5366 | static const int order_0[] = { |
90e7678c DC |
5367 | 24,25, |
5368 | 18,19, | |
5369 | 20,21, | |
5370 | 22,23, | |
5371 | 30,31, | |
5372 | 26,27, | |
5373 | 28,29, | |
5374 | 17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, | |
5375 | 0,1, | |
5376 | 32,33,34,35 | |
5377 | }; | |
8b60264b | 5378 | static const int order_1[] = { |
90e7678c DC |
5379 | 18,19, |
5380 | 20,21, | |
5381 | 22,23, | |
5382 | 24,25, | |
5383 | 30,31, | |
5384 | 26,27, | |
5385 | 28,29, | |
5386 | 17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, | |
5387 | 0,1, | |
5388 | 32,33,34,35 | |
5389 | }; | |
8b60264b | 5390 | static const int order_2[] = { |
90e7678c DC |
5391 | 25,24, |
5392 | 23,22, | |
5393 | 21,20, | |
5394 | 19,18, | |
5395 | 30,31, | |
5396 | 26,27, | |
5397 | 28,29, | |
5398 | 17,16, | |
5399 | 15,14,13,12,11,10,9,8,7,6,5,4,3,2, | |
5400 | 1,0, | |
5401 | 32,33,34,35 | |
5402 | }; | |
5403 | ||
8b60264b KG |
5404 | const int *order = (TARGET_ORDER_1 ? order_1 : |
5405 | TARGET_ORDER_2 ? order_2 : | |
5406 | order_0); | |
5b47282c | 5407 | for (i=0; i < ARRAY_SIZE (order_0); ++i) |
90e7678c DC |
5408 | reg_alloc_order[i] = order[i]; |
5409 | } | |
5410 | ||
8f2bf9f1 | 5411 | |
5f892aa0 GJL |
5412 | /* Implement `TARGET_REGISTER_MOVE_COST' */ |
5413 | ||
5414 | static int | |
5415 | avr_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, | |
5416 | reg_class_t from, reg_class_t to) | |
5417 | { | |
5418 | return (from == STACK_REG ? 6 | |
5419 | : to == STACK_REG ? 12 | |
5420 | : 2); | |
5421 | } | |
5422 | ||
5423 | ||
5424 | /* Implement `TARGET_MEMORY_MOVE_COST' */ | |
5425 | ||
5426 | static int | |
5427 | avr_memory_move_cost (enum machine_mode mode, reg_class_t rclass ATTRIBUTE_UNUSED, | |
5428 | bool in ATTRIBUTE_UNUSED) | |
5429 | { | |
5430 | return (mode == QImode ? 2 | |
5431 | : mode == HImode ? 4 | |
5432 | : mode == SImode ? 8 | |
5433 | : mode == SFmode ? 8 | |
5434 | : 16); | |
5435 | } | |
5436 | ||
5437 | ||
8f2bf9f1 RS |
5438 | /* Mutually recursive subroutine of avr_rtx_cost for calculating the |
5439 | cost of an RTX operand given its context. X is the rtx of the | |
5440 | operand, MODE is its mode, and OUTER is the rtx_code of this | |
5441 | operand's parent operator. */ | |
90e7678c | 5442 | |
3c50106f | 5443 | static int |
f40751dd | 5444 | avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer, |
1f494b6d | 5445 | bool speed) |
90e7678c | 5446 | { |
8f2bf9f1 RS |
5447 | enum rtx_code code = GET_CODE (x); |
5448 | int total; | |
5449 | ||
90e7678c DC |
5450 | switch (code) |
5451 | { | |
8f2bf9f1 RS |
5452 | case REG: |
5453 | case SUBREG: | |
5454 | return 0; | |
5455 | ||
90e7678c | 5456 | case CONST_INT: |
8f2bf9f1 RS |
5457 | case CONST_DOUBLE: |
5458 | return COSTS_N_INSNS (GET_MODE_SIZE (mode)); | |
5459 | ||
90e7678c DC |
5460 | default: |
5461 | break; | |
5462 | } | |
8f2bf9f1 RS |
5463 | |
5464 | total = 0; | |
1f494b6d | 5465 | avr_rtx_costs (x, code, outer, &total, speed); |
8f2bf9f1 | 5466 | return total; |
90e7678c DC |
5467 | } |
5468 | ||
8f2bf9f1 RS |
5469 | /* The AVR backend's rtx_cost function. X is rtx expression whose cost |
5470 | is to be calculated. Return true if the complete cost has been | |
5471 | computed, and false if subexpressions should be scanned. In either | |
5472 | case, *TOTAL contains the cost result. */ | |
5473 | ||
3c50106f | 5474 | static bool |
5a82ecd9 | 5475 | avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total, |
f40751dd | 5476 | bool speed) |
3c50106f | 5477 | { |
5a82ecd9 | 5478 | enum rtx_code code = (enum rtx_code) codearg; |
8f2bf9f1 RS |
5479 | enum machine_mode mode = GET_MODE (x); |
5480 | HOST_WIDE_INT val; | |
3c50106f RH |
5481 | |
5482 | switch (code) | |
5483 | { | |
5484 | case CONST_INT: | |
8f2bf9f1 RS |
5485 | case CONST_DOUBLE: |
5486 | /* Immediate constants are as cheap as registers. */ | |
5487 | *total = 0; | |
5488 | return true; | |
5489 | ||
5490 | case MEM: | |
5491 | case CONST: | |
5492 | case LABEL_REF: | |
5493 | case SYMBOL_REF: | |
5494 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); | |
5495 | return true; | |
5496 | ||
5497 | case NEG: | |
5498 | switch (mode) | |
3c50106f | 5499 | { |
8f2bf9f1 RS |
5500 | case QImode: |
5501 | case SFmode: | |
5502 | *total = COSTS_N_INSNS (1); | |
5503 | break; | |
5504 | ||
5505 | case HImode: | |
5506 | *total = COSTS_N_INSNS (3); | |
5507 | break; | |
5508 | ||
5509 | case SImode: | |
5510 | *total = COSTS_N_INSNS (7); | |
5511 | break; | |
5512 | ||
5513 | default: | |
5514 | return false; | |
3c50106f | 5515 | } |
1f494b6d | 5516 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5517 | return true; |
5518 | ||
5519 | case ABS: | |
5520 | switch (mode) | |
3c50106f | 5521 | { |
8f2bf9f1 RS |
5522 | case QImode: |
5523 | case SFmode: | |
5524 | *total = COSTS_N_INSNS (1); | |
5525 | break; | |
5526 | ||
5527 | default: | |
5528 | return false; | |
3c50106f | 5529 | } |
1f494b6d | 5530 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 | 5531 | return true; |
3c50106f | 5532 | |
8f2bf9f1 RS |
5533 | case NOT: |
5534 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); | |
1f494b6d | 5535 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
3c50106f RH |
5536 | return true; |
5537 | ||
8f2bf9f1 RS |
5538 | case ZERO_EXTEND: |
5539 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) | |
5540 | - GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))); | |
1f494b6d | 5541 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5542 | return true; |
5543 | ||
5544 | case SIGN_EXTEND: | |
5545 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) + 2 | |
5546 | - GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))); | |
1f494b6d | 5547 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5548 | return true; |
5549 | ||
5550 | case PLUS: | |
5551 | switch (mode) | |
5552 | { | |
5553 | case QImode: | |
5554 | *total = COSTS_N_INSNS (1); | |
5555 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
1f494b6d | 5556 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5557 | break; |
5558 | ||
5559 | case HImode: | |
5560 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5561 | { | |
5562 | *total = COSTS_N_INSNS (2); | |
1f494b6d | 5563 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5564 | } |
5565 | else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63) | |
5566 | *total = COSTS_N_INSNS (1); | |
5567 | else | |
5568 | *total = COSTS_N_INSNS (2); | |
5569 | break; | |
5570 | ||
5571 | case SImode: | |
5572 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5573 | { | |
5574 | *total = COSTS_N_INSNS (4); | |
1f494b6d | 5575 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5576 | } |
5577 | else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63) | |
5578 | *total = COSTS_N_INSNS (1); | |
5579 | else | |
5580 | *total = COSTS_N_INSNS (4); | |
5581 | break; | |
5582 | ||
5583 | default: | |
5584 | return false; | |
5585 | } | |
1f494b6d | 5586 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5587 | return true; |
5588 | ||
5589 | case MINUS: | |
5590 | case AND: | |
5591 | case IOR: | |
5592 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); | |
1f494b6d | 5593 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 | 5594 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) |
1f494b6d | 5595 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5596 | return true; |
5597 | ||
5598 | case XOR: | |
5599 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); | |
1f494b6d JH |
5600 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
5601 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); | |
8f2bf9f1 RS |
5602 | return true; |
5603 | ||
5604 | case MULT: | |
5605 | switch (mode) | |
5606 | { | |
5607 | case QImode: | |
dd6d1f8c | 5608 | if (AVR_HAVE_MUL) |
f40751dd JH |
5609 | *total = COSTS_N_INSNS (!speed ? 3 : 4); |
5610 | else if (!speed) | |
5dc77808 | 5611 | *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); |
8f2bf9f1 RS |
5612 | else |
5613 | return false; | |
c8bbabf2 | 5614 | break; |
8f2bf9f1 RS |
5615 | |
5616 | case HImode: | |
dd6d1f8c | 5617 | if (AVR_HAVE_MUL) |
f40751dd JH |
5618 | *total = COSTS_N_INSNS (!speed ? 7 : 10); |
5619 | else if (!speed) | |
5dc77808 | 5620 | *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); |
8f2bf9f1 RS |
5621 | else |
5622 | return false; | |
c8bbabf2 | 5623 | break; |
8f2bf9f1 RS |
5624 | |
5625 | default: | |
5626 | return false; | |
5627 | } | |
1f494b6d JH |
5628 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
5629 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); | |
8f2bf9f1 RS |
5630 | return true; |
5631 | ||
5632 | case DIV: | |
5633 | case MOD: | |
5634 | case UDIV: | |
5635 | case UMOD: | |
f40751dd | 5636 | if (!speed) |
5dc77808 | 5637 | *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); |
8f2bf9f1 RS |
5638 | else |
5639 | return false; | |
1f494b6d JH |
5640 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
5641 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); | |
8f2bf9f1 RS |
5642 | return true; |
5643 | ||
15fe850f AS |
5644 | case ROTATE: |
5645 | switch (mode) | |
5646 | { | |
5647 | case QImode: | |
5648 | if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 4) | |
5649 | *total = COSTS_N_INSNS (1); | |
5650 | ||
5651 | break; | |
5652 | ||
5653 | case HImode: | |
5654 | if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 8) | |
5655 | *total = COSTS_N_INSNS (3); | |
5656 | ||
5657 | break; | |
5658 | ||
5659 | case SImode: | |
5660 | if (CONST_INT_P (XEXP (x, 1))) | |
5661 | switch (INTVAL (XEXP (x, 1))) | |
5662 | { | |
5663 | case 8: | |
5664 | case 24: | |
5665 | *total = COSTS_N_INSNS (5); | |
5666 | break; | |
5667 | case 16: | |
5668 | *total = COSTS_N_INSNS (AVR_HAVE_MOVW ? 4 : 6); | |
5669 | break; | |
5670 | } | |
5671 | break; | |
5672 | ||
5673 | default: | |
5674 | return false; | |
5675 | } | |
5676 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); | |
5677 | return true; | |
5678 | ||
8f2bf9f1 RS |
5679 | case ASHIFT: |
5680 | switch (mode) | |
5681 | { | |
5682 | case QImode: | |
5683 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5684 | { | |
f40751dd | 5685 | *total = COSTS_N_INSNS (!speed ? 4 : 17); |
1f494b6d | 5686 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5687 | } |
5688 | else | |
5689 | { | |
5690 | val = INTVAL (XEXP (x, 1)); | |
5691 | if (val == 7) | |
5692 | *total = COSTS_N_INSNS (3); | |
5693 | else if (val >= 0 && val <= 7) | |
5694 | *total = COSTS_N_INSNS (val); | |
5695 | else | |
5696 | *total = COSTS_N_INSNS (1); | |
5697 | } | |
5698 | break; | |
5699 | ||
5700 | case HImode: | |
5701 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5702 | { | |
f40751dd | 5703 | *total = COSTS_N_INSNS (!speed ? 5 : 41); |
1f494b6d | 5704 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5705 | } |
5706 | else | |
5707 | switch (INTVAL (XEXP (x, 1))) | |
5708 | { | |
5709 | case 0: | |
5710 | *total = 0; | |
5711 | break; | |
5712 | case 1: | |
5713 | case 8: | |
5714 | *total = COSTS_N_INSNS (2); | |
5715 | break; | |
5716 | case 9: | |
5717 | *total = COSTS_N_INSNS (3); | |
5718 | break; | |
5719 | case 2: | |
5720 | case 3: | |
5721 | case 10: | |
5722 | case 15: | |
5723 | *total = COSTS_N_INSNS (4); | |
5724 | break; | |
5725 | case 7: | |
5726 | case 11: | |
5727 | case 12: | |
5728 | *total = COSTS_N_INSNS (5); | |
5729 | break; | |
5730 | case 4: | |
f40751dd | 5731 | *total = COSTS_N_INSNS (!speed ? 5 : 8); |
8f2bf9f1 RS |
5732 | break; |
5733 | case 6: | |
1f494b6d | 5734 | *total = COSTS_N_INSNS (!speed ? 5 : 9); |
8f2bf9f1 RS |
5735 | break; |
5736 | case 5: | |
f40751dd | 5737 | *total = COSTS_N_INSNS (!speed ? 5 : 10); |
8f2bf9f1 RS |
5738 | break; |
5739 | default: | |
f40751dd | 5740 | *total = COSTS_N_INSNS (!speed ? 5 : 41); |
1f494b6d | 5741 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5742 | } |
5743 | break; | |
5744 | ||
5745 | case SImode: | |
5746 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5747 | { | |
f40751dd | 5748 | *total = COSTS_N_INSNS (!speed ? 7 : 113); |
1f494b6d | 5749 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5750 | } |
5751 | else | |
5752 | switch (INTVAL (XEXP (x, 1))) | |
5753 | { | |
5754 | case 0: | |
5755 | *total = 0; | |
5756 | break; | |
5757 | case 24: | |
5758 | *total = COSTS_N_INSNS (3); | |
5759 | break; | |
5760 | case 1: | |
5761 | case 8: | |
5762 | case 16: | |
5763 | *total = COSTS_N_INSNS (4); | |
5764 | break; | |
5765 | case 31: | |
5766 | *total = COSTS_N_INSNS (6); | |
5767 | break; | |
5768 | case 2: | |
f40751dd | 5769 | *total = COSTS_N_INSNS (!speed ? 7 : 8); |
8f2bf9f1 RS |
5770 | break; |
5771 | default: | |
f40751dd | 5772 | *total = COSTS_N_INSNS (!speed ? 7 : 113); |
1f494b6d | 5773 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5774 | } |
5775 | break; | |
5776 | ||
5777 | default: | |
5778 | return false; | |
5779 | } | |
1f494b6d | 5780 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5781 | return true; |
5782 | ||
5783 | case ASHIFTRT: | |
5784 | switch (mode) | |
5785 | { | |
5786 | case QImode: | |
5787 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5788 | { | |
f40751dd | 5789 | *total = COSTS_N_INSNS (!speed ? 4 : 17); |
1f494b6d | 5790 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5791 | } |
5792 | else | |
5793 | { | |
5794 | val = INTVAL (XEXP (x, 1)); | |
5795 | if (val == 6) | |
5796 | *total = COSTS_N_INSNS (4); | |
5797 | else if (val == 7) | |
5798 | *total = COSTS_N_INSNS (2); | |
5799 | else if (val >= 0 && val <= 7) | |
5800 | *total = COSTS_N_INSNS (val); | |
5801 | else | |
5802 | *total = COSTS_N_INSNS (1); | |
5803 | } | |
5804 | break; | |
5805 | ||
5806 | case HImode: | |
5807 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5808 | { | |
f40751dd | 5809 | *total = COSTS_N_INSNS (!speed ? 5 : 41); |
1f494b6d | 5810 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5811 | } |
5812 | else | |
5813 | switch (INTVAL (XEXP (x, 1))) | |
5814 | { | |
5815 | case 0: | |
5816 | *total = 0; | |
5817 | break; | |
5818 | case 1: | |
5819 | *total = COSTS_N_INSNS (2); | |
5820 | break; | |
5821 | case 15: | |
5822 | *total = COSTS_N_INSNS (3); | |
5823 | break; | |
5824 | case 2: | |
5825 | case 7: | |
5826 | case 8: | |
5827 | case 9: | |
5828 | *total = COSTS_N_INSNS (4); | |
5829 | break; | |
5830 | case 10: | |
5831 | case 14: | |
5832 | *total = COSTS_N_INSNS (5); | |
5833 | break; | |
5834 | case 11: | |
f40751dd | 5835 | *total = COSTS_N_INSNS (!speed ? 5 : 6); |
8f2bf9f1 RS |
5836 | break; |
5837 | case 12: | |
f40751dd | 5838 | *total = COSTS_N_INSNS (!speed ? 5 : 7); |
8f2bf9f1 RS |
5839 | break; |
5840 | case 6: | |
5841 | case 13: | |
f40751dd | 5842 | *total = COSTS_N_INSNS (!speed ? 5 : 8); |
8f2bf9f1 RS |
5843 | break; |
5844 | default: | |
f40751dd | 5845 | *total = COSTS_N_INSNS (!speed ? 5 : 41); |
1f494b6d | 5846 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5847 | } |
5848 | break; | |
5849 | ||
5850 | case SImode: | |
5851 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5852 | { | |
f40751dd | 5853 | *total = COSTS_N_INSNS (!speed ? 7 : 113); |
1f494b6d | 5854 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5855 | } |
5856 | else | |
5857 | switch (INTVAL (XEXP (x, 1))) | |
5858 | { | |
5859 | case 0: | |
5860 | *total = 0; | |
5861 | break; | |
5862 | case 1: | |
5863 | *total = COSTS_N_INSNS (4); | |
5864 | break; | |
5865 | case 8: | |
5866 | case 16: | |
5867 | case 24: | |
5868 | *total = COSTS_N_INSNS (6); | |
5869 | break; | |
5870 | case 2: | |
f40751dd | 5871 | *total = COSTS_N_INSNS (!speed ? 7 : 8); |
8f2bf9f1 RS |
5872 | break; |
5873 | case 31: | |
4301ec4f | 5874 | *total = COSTS_N_INSNS (AVR_HAVE_MOVW ? 4 : 5); |
8f2bf9f1 RS |
5875 | break; |
5876 | default: | |
f40751dd | 5877 | *total = COSTS_N_INSNS (!speed ? 7 : 113); |
1f494b6d | 5878 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5879 | } |
5880 | break; | |
5881 | ||
5882 | default: | |
5883 | return false; | |
5884 | } | |
1f494b6d | 5885 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5886 | return true; |
5887 | ||
5888 | case LSHIFTRT: | |
5889 | switch (mode) | |
5890 | { | |
5891 | case QImode: | |
5892 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5893 | { | |
f40751dd | 5894 | *total = COSTS_N_INSNS (!speed ? 4 : 17); |
1f494b6d | 5895 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5896 | } |
5897 | else | |
5898 | { | |
5899 | val = INTVAL (XEXP (x, 1)); | |
5900 | if (val == 7) | |
5901 | *total = COSTS_N_INSNS (3); | |
5902 | else if (val >= 0 && val <= 7) | |
5903 | *total = COSTS_N_INSNS (val); | |
5904 | else | |
5905 | *total = COSTS_N_INSNS (1); | |
5906 | } | |
5907 | break; | |
5908 | ||
5909 | case HImode: | |
5910 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5911 | { | |
f40751dd | 5912 | *total = COSTS_N_INSNS (!speed ? 5 : 41); |
1f494b6d | 5913 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5914 | } |
5915 | else | |
5916 | switch (INTVAL (XEXP (x, 1))) | |
5917 | { | |
5918 | case 0: | |
5919 | *total = 0; | |
5920 | break; | |
5921 | case 1: | |
5922 | case 8: | |
5923 | *total = COSTS_N_INSNS (2); | |
5924 | break; | |
5925 | case 9: | |
5926 | *total = COSTS_N_INSNS (3); | |
5927 | break; | |
5928 | case 2: | |
5929 | case 10: | |
5930 | case 15: | |
5931 | *total = COSTS_N_INSNS (4); | |
5932 | break; | |
5933 | case 7: | |
5934 | case 11: | |
5935 | *total = COSTS_N_INSNS (5); | |
5936 | break; | |
5937 | case 3: | |
5938 | case 12: | |
5939 | case 13: | |
5940 | case 14: | |
f40751dd | 5941 | *total = COSTS_N_INSNS (!speed ? 5 : 6); |
8f2bf9f1 RS |
5942 | break; |
5943 | case 4: | |
f40751dd | 5944 | *total = COSTS_N_INSNS (!speed ? 5 : 7); |
8f2bf9f1 RS |
5945 | break; |
5946 | case 5: | |
5947 | case 6: | |
f40751dd | 5948 | *total = COSTS_N_INSNS (!speed ? 5 : 9); |
8f2bf9f1 RS |
5949 | break; |
5950 | default: | |
f40751dd | 5951 | *total = COSTS_N_INSNS (!speed ? 5 : 41); |
1f494b6d | 5952 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5953 | } |
5954 | break; | |
5955 | ||
5956 | case SImode: | |
5957 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
5958 | { | |
f40751dd | 5959 | *total = COSTS_N_INSNS (!speed ? 7 : 113); |
1f494b6d | 5960 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5961 | } |
5962 | else | |
5963 | switch (INTVAL (XEXP (x, 1))) | |
5964 | { | |
5965 | case 0: | |
5966 | *total = 0; | |
5967 | break; | |
5968 | case 1: | |
5969 | *total = COSTS_N_INSNS (4); | |
5970 | break; | |
5971 | case 2: | |
f40751dd | 5972 | *total = COSTS_N_INSNS (!speed ? 7 : 8); |
8f2bf9f1 RS |
5973 | break; |
5974 | case 8: | |
5975 | case 16: | |
5976 | case 24: | |
5977 | *total = COSTS_N_INSNS (4); | |
5978 | break; | |
5979 | case 31: | |
5980 | *total = COSTS_N_INSNS (6); | |
5981 | break; | |
5982 | default: | |
f40751dd | 5983 | *total = COSTS_N_INSNS (!speed ? 7 : 113); |
1f494b6d | 5984 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
5985 | } |
5986 | break; | |
5987 | ||
5988 | default: | |
5989 | return false; | |
5990 | } | |
1f494b6d | 5991 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
5992 | return true; |
5993 | ||
5994 | case COMPARE: | |
5995 | switch (GET_MODE (XEXP (x, 0))) | |
3c50106f | 5996 | { |
8f2bf9f1 RS |
5997 | case QImode: |
5998 | *total = COSTS_N_INSNS (1); | |
5999 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
1f494b6d | 6000 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
6001 | break; |
6002 | ||
6003 | case HImode: | |
6004 | *total = COSTS_N_INSNS (2); | |
6005 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
1f494b6d | 6006 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
6007 | else if (INTVAL (XEXP (x, 1)) != 0) |
6008 | *total += COSTS_N_INSNS (1); | |
6009 | break; | |
6010 | ||
6011 | case SImode: | |
6012 | *total = COSTS_N_INSNS (4); | |
6013 | if (GET_CODE (XEXP (x, 1)) != CONST_INT) | |
1f494b6d | 6014 | *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed); |
8f2bf9f1 RS |
6015 | else if (INTVAL (XEXP (x, 1)) != 0) |
6016 | *total += COSTS_N_INSNS (3); | |
6017 | break; | |
6018 | ||
6019 | default: | |
6020 | return false; | |
3c50106f | 6021 | } |
1f494b6d | 6022 | *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed); |
8f2bf9f1 RS |
6023 | return true; |
6024 | ||
6025 | default: | |
6026 | break; | |
3c50106f | 6027 | } |
8f2bf9f1 | 6028 | return false; |
3c50106f RH |
6029 | } |
6030 | ||
2c338472 | 6031 | /* Calculate the cost of a memory address. */ |
90e7678c | 6032 | |
dcefdf67 | 6033 | static int |
f40751dd | 6034 | avr_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED) |
90e7678c DC |
6035 | { |
6036 | if (GET_CODE (x) == PLUS | |
6037 | && GET_CODE (XEXP (x,1)) == CONST_INT | |
6038 | && (REG_P (XEXP (x,0)) || GET_CODE (XEXP (x,0)) == SUBREG) | |
6039 | && INTVAL (XEXP (x,1)) >= 61) | |
6040 | return 18; | |
6041 | if (CONSTANT_ADDRESS_P (x)) | |
5fecfd8d | 6042 | { |
e6b69d0e | 6043 | if (optimize > 0 && io_address_operand (x, QImode)) |
5fecfd8d MM |
6044 | return 2; |
6045 | return 4; | |
6046 | } | |
90e7678c DC |
6047 | return 4; |
6048 | } | |
6049 | ||
2d67effa DC |
6050 | /* Test for extra memory constraint 'Q'. |
6051 | It's a memory address based on Y or Z pointer with valid displacement. */ | |
90e7678c DC |
6052 | |
6053 | int | |
2d67effa | 6054 | extra_constraint_Q (rtx x) |
90e7678c | 6055 | { |
2d67effa DC |
6056 | if (GET_CODE (XEXP (x,0)) == PLUS |
6057 | && REG_P (XEXP (XEXP (x,0), 0)) | |
6058 | && GET_CODE (XEXP (XEXP (x,0), 1)) == CONST_INT | |
6059 | && (INTVAL (XEXP (XEXP (x,0), 1)) | |
6060 | <= MAX_LD_OFFSET (GET_MODE (x)))) | |
90e7678c | 6061 | { |
2d67effa DC |
6062 | rtx xx = XEXP (XEXP (x,0), 0); |
6063 | int regno = REGNO (xx); | |
6064 | if (TARGET_ALL_DEBUG) | |
90e7678c | 6065 | { |
2d67effa DC |
6066 | fprintf (stderr, ("extra_constraint:\n" |
6067 | "reload_completed: %d\n" | |
6068 | "reload_in_progress: %d\n"), | |
6069 | reload_completed, reload_in_progress); | |
6070 | debug_rtx (x); | |
90e7678c | 6071 | } |
2d67effa DC |
6072 | if (regno >= FIRST_PSEUDO_REGISTER) |
6073 | return 1; /* allocate pseudos */ | |
6074 | else if (regno == REG_Z || regno == REG_Y) | |
6075 | return 1; /* strictly check */ | |
6076 | else if (xx == frame_pointer_rtx | |
6077 | || xx == arg_pointer_rtx) | |
6078 | return 1; /* XXX frame & arg pointer checks */ | |
90e7678c DC |
6079 | } |
6080 | return 0; | |
6081 | } | |
6082 | ||
2c338472 | 6083 | /* Convert condition code CONDITION to the valid AVR condition code. */ |
90e7678c DC |
6084 | |
6085 | RTX_CODE | |
269e3795 | 6086 | avr_normalize_condition (RTX_CODE condition) |
90e7678c DC |
6087 | { |
6088 | switch (condition) | |
6089 | { | |
6090 | case GT: | |
6091 | return GE; | |
6092 | case GTU: | |
6093 | return GEU; | |
6094 | case LE: | |
6095 | return LT; | |
6096 | case LEU: | |
6097 | return LTU; | |
6098 | default: | |
25b9575b | 6099 | gcc_unreachable (); |
90e7678c DC |
6100 | } |
6101 | } | |
6102 | ||
cef1ccd6 | 6103 | /* This function optimizes conditional jumps. */ |
90e7678c | 6104 | |
18dbd950 | 6105 | static void |
269e3795 | 6106 | avr_reorg (void) |
90e7678c DC |
6107 | { |
6108 | rtx insn, pattern; | |
90e7678c | 6109 | |
18dbd950 | 6110 | for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
90e7678c | 6111 | { |
33f77355 DC |
6112 | if (! (GET_CODE (insn) == INSN |
6113 | || GET_CODE (insn) == CALL_INSN | |
6114 | || GET_CODE (insn) == JUMP_INSN) | |
90e7678c DC |
6115 | || !single_set (insn)) |
6116 | continue; | |
6117 | ||
6118 | pattern = PATTERN (insn); | |
6119 | ||
90e7678c DC |
6120 | if (GET_CODE (pattern) == PARALLEL) |
6121 | pattern = XVECEXP (pattern, 0, 0); | |
6122 | if (GET_CODE (pattern) == SET | |
6123 | && SET_DEST (pattern) == cc0_rtx | |
6124 | && compare_diff_p (insn)) | |
6125 | { | |
6126 | if (GET_CODE (SET_SRC (pattern)) == COMPARE) | |
6127 | { | |
2c338472 | 6128 | /* Now we work under compare insn. */ |
90e7678c DC |
6129 | |
6130 | pattern = SET_SRC (pattern); | |
6131 | if (true_regnum (XEXP (pattern,0)) >= 0 | |
6132 | && true_regnum (XEXP (pattern,1)) >= 0 ) | |
6133 | { | |
6134 | rtx x = XEXP (pattern,0); | |
6135 | rtx next = next_real_insn (insn); | |
6136 | rtx pat = PATTERN (next); | |
6137 | rtx src = SET_SRC (pat); | |
6138 | rtx t = XEXP (src,0); | |
6139 | PUT_CODE (t, swap_condition (GET_CODE (t))); | |
6140 | XEXP (pattern,0) = XEXP (pattern,1); | |
6141 | XEXP (pattern,1) = x; | |
6142 | INSN_CODE (next) = -1; | |
6143 | } | |
f90b7a5a PB |
6144 | else if (true_regnum (XEXP (pattern, 0)) >= 0 |
6145 | && XEXP (pattern, 1) == const0_rtx) | |
6146 | { | |
6147 | /* This is a tst insn, we can reverse it. */ | |
6148 | rtx next = next_real_insn (insn); | |
6149 | rtx pat = PATTERN (next); | |
6150 | rtx src = SET_SRC (pat); | |
6151 | rtx t = XEXP (src,0); | |
6152 | ||
6153 | PUT_CODE (t, swap_condition (GET_CODE (t))); | |
6154 | XEXP (pattern, 1) = XEXP (pattern, 0); | |
6155 | XEXP (pattern, 0) = const0_rtx; | |
6156 | INSN_CODE (next) = -1; | |
6157 | INSN_CODE (insn) = -1; | |
6158 | } | |
90e7678c DC |
6159 | else if (true_regnum (XEXP (pattern,0)) >= 0 |
6160 | && GET_CODE (XEXP (pattern,1)) == CONST_INT) | |
6161 | { | |
6162 | rtx x = XEXP (pattern,1); | |
6163 | rtx next = next_real_insn (insn); | |
6164 | rtx pat = PATTERN (next); | |
6165 | rtx src = SET_SRC (pat); | |
6166 | rtx t = XEXP (src,0); | |
651c5ed9 | 6167 | enum machine_mode mode = GET_MODE (XEXP (pattern, 0)); |
90e7678c | 6168 | |
62e4d780 | 6169 | if (avr_simplify_comparison_p (mode, GET_CODE (t), x)) |
90e7678c | 6170 | { |
651c5ed9 | 6171 | XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode); |
90e7678c DC |
6172 | PUT_CODE (t, avr_normalize_condition (GET_CODE (t))); |
6173 | INSN_CODE (next) = -1; | |
6174 | INSN_CODE (insn) = -1; | |
6175 | } | |
6176 | } | |
6177 | } | |
90e7678c DC |
6178 | } |
6179 | } | |
6180 | } | |
6181 | ||
6182 | /* Returns register number for function return value.*/ | |
6183 | ||
fd01a351 | 6184 | static inline unsigned int |
269e3795 | 6185 | avr_ret_register (void) |
90e7678c DC |
6186 | { |
6187 | return 24; | |
6188 | } | |
6189 | ||
68fd7765 AS |
6190 | /* Worker function for TARGET_FUNCTION_VALUE_REGNO_P. */ |
6191 | ||
6192 | static bool | |
6193 | avr_function_value_regno_p (const unsigned int regno) | |
6194 | { | |
6195 | return (regno == avr_ret_register ()); | |
6196 | } | |
6197 | ||
9f5ed61a | 6198 | /* Create an RTX representing the place where a |
90e7678c DC |
6199 | library function returns a value of mode MODE. */ |
6200 | ||
68fd7765 AS |
6201 | static rtx |
6202 | avr_libcall_value (enum machine_mode mode, | |
6203 | const_rtx func ATTRIBUTE_UNUSED) | |
90e7678c DC |
6204 | { |
6205 | int offs = GET_MODE_SIZE (mode); | |
6206 | if (offs < 2) | |
6207 | offs = 2; | |
68fd7765 | 6208 | return gen_rtx_REG (mode, avr_ret_register () + 2 - offs); |
90e7678c DC |
6209 | } |
6210 | ||
6211 | /* Create an RTX representing the place where a | |
6212 | function returns a value of data type VALTYPE. */ | |
6213 | ||
68fd7765 | 6214 | static rtx |
fd01a351 GJL |
6215 | avr_function_value (const_tree type, |
6216 | const_tree fn_decl_or_type ATTRIBUTE_UNUSED, | |
6217 | bool outgoing ATTRIBUTE_UNUSED) | |
90e7678c | 6218 | { |
3454eb73 | 6219 | unsigned int offs; |
68fd7765 | 6220 | |
90e7678c | 6221 | if (TYPE_MODE (type) != BLKmode) |
fd01a351 | 6222 | return avr_libcall_value (TYPE_MODE (type), NULL_RTX); |
90e7678c DC |
6223 | |
6224 | offs = int_size_in_bytes (type); | |
6225 | if (offs < 2) | |
6226 | offs = 2; | |
6227 | if (offs > 2 && offs < GET_MODE_SIZE (SImode)) | |
6228 | offs = GET_MODE_SIZE (SImode); | |
6229 | else if (offs > GET_MODE_SIZE (SImode) && offs < GET_MODE_SIZE (DImode)) | |
6230 | offs = GET_MODE_SIZE (DImode); | |
6231 | ||
68fd7765 | 6232 | return gen_rtx_REG (BLKmode, avr_ret_register () + 2 - offs); |
90e7678c DC |
6233 | } |
6234 | ||
28e801e2 | 6235 | int |
0a2aaacc | 6236 | test_hard_reg_class (enum reg_class rclass, rtx x) |
28e801e2 DC |
6237 | { |
6238 | int regno = true_regnum (x); | |
6239 | if (regno < 0) | |
6240 | return 0; | |
26af4041 | 6241 | |
0a2aaacc | 6242 | if (TEST_HARD_REG_CLASS (rclass, regno)) |
26af4041 MM |
6243 | return 1; |
6244 | ||
6245 | return 0; | |
28e801e2 DC |
6246 | } |
6247 | ||
b58c068a DC |
6248 | |
6249 | int | |
269e3795 | 6250 | jump_over_one_insn_p (rtx insn, rtx dest) |
b58c068a DC |
6251 | { |
6252 | int uid = INSN_UID (GET_CODE (dest) == LABEL_REF | |
6253 | ? XEXP (dest, 0) | |
6254 | : dest); | |
9d98a694 AO |
6255 | int jump_addr = INSN_ADDRESSES (INSN_UID (insn)); |
6256 | int dest_addr = INSN_ADDRESSES (uid); | |
331ca350 | 6257 | return dest_addr - jump_addr == get_attr_length (insn) + 1; |
b58c068a | 6258 | } |
78cf8279 MM |
6259 | |
6260 | /* Returns 1 if a value of mode MODE can be stored starting with hard | |
c4984bad MM |
6261 | register number REGNO. On the enhanced core, anything larger than |
6262 | 1 byte must start in even numbered register for "movw" to work | |
6263 | (this way we don't have to check for odd registers everywhere). */ | |
78cf8279 MM |
6264 | |
6265 | int | |
269e3795 | 6266 | avr_hard_regno_mode_ok (int regno, enum machine_mode mode) |
78cf8279 | 6267 | { |
2cd11105 AS |
6268 | /* Disallow QImode in stack pointer regs. */ |
6269 | if ((regno == REG_SP || regno == (REG_SP + 1)) && mode == QImode) | |
6270 | return 0; | |
6271 | ||
d2ebe27c RS |
6272 | /* The only thing that can go into registers r28:r29 is a Pmode. */ |
6273 | if (regno == REG_Y && mode == Pmode) | |
6274 | return 1; | |
b96c434c | 6275 | |
d2ebe27c RS |
6276 | /* Otherwise disallow all regno/mode combinations that span r28:r29. */ |
6277 | if (regno <= (REG_Y + 1) && (regno + GET_MODE_SIZE (mode)) >= (REG_Y + 1)) | |
678584fc DC |
6278 | return 0; |
6279 | ||
78cf8279 MM |
6280 | if (mode == QImode) |
6281 | return 1; | |
d2ebe27c RS |
6282 | |
6283 | /* Modes larger than QImode occupy consecutive registers. */ | |
6284 | if (regno + GET_MODE_SIZE (mode) > FIRST_PSEUDO_REGISTER) | |
6285 | return 0; | |
6286 | ||
6287 | /* All modes larger than QImode should start in an even register. */ | |
78cf8279 MM |
6288 | return !(regno & 1); |
6289 | } | |
6bec29c9 | 6290 | |
5fecfd8d | 6291 | const char * |
269e3795 | 6292 | output_reload_inhi (rtx insn ATTRIBUTE_UNUSED, rtx *operands, int *len) |
6bec29c9 | 6293 | { |
612105a6 DC |
6294 | int tmp; |
6295 | if (!len) | |
6296 | len = &tmp; | |
6297 | ||
6bec29c9 DC |
6298 | if (GET_CODE (operands[1]) == CONST_INT) |
6299 | { | |
6300 | int val = INTVAL (operands[1]); | |
6301 | if ((val & 0xff) == 0) | |
6302 | { | |
6303 | *len = 3; | |
6304 | return (AS2 (mov,%A0,__zero_reg__) CR_TAB | |
6305 | AS2 (ldi,%2,hi8(%1)) CR_TAB | |
6306 | AS2 (mov,%B0,%2)); | |
6307 | } | |
6308 | else if ((val & 0xff00) == 0) | |
6309 | { | |
6310 | *len = 3; | |
6311 | return (AS2 (ldi,%2,lo8(%1)) CR_TAB | |
6312 | AS2 (mov,%A0,%2) CR_TAB | |
6313 | AS2 (mov,%B0,__zero_reg__)); | |
6314 | } | |
6315 | else if ((val & 0xff) == ((val & 0xff00) >> 8)) | |
6316 | { | |
6317 | *len = 3; | |
6318 | return (AS2 (ldi,%2,lo8(%1)) CR_TAB | |
6319 | AS2 (mov,%A0,%2) CR_TAB | |
6320 | AS2 (mov,%B0,%2)); | |
6321 | } | |
6322 | } | |
6323 | *len = 4; | |
6324 | return (AS2 (ldi,%2,lo8(%1)) CR_TAB | |
6325 | AS2 (mov,%A0,%2) CR_TAB | |
6326 | AS2 (ldi,%2,hi8(%1)) CR_TAB | |
6327 | AS2 (mov,%B0,%2)); | |
6328 | } | |
6329 | ||
6330 | ||
5fecfd8d | 6331 | const char * |
269e3795 | 6332 | output_reload_insisf (rtx insn ATTRIBUTE_UNUSED, rtx *operands, int *len) |
6bec29c9 | 6333 | { |
5fecfd8d MM |
6334 | rtx src = operands[1]; |
6335 | int cnst = (GET_CODE (src) == CONST_INT); | |
6336 | ||
6337 | if (len) | |
6338 | { | |
6339 | if (cnst) | |
6340 | *len = 4 + ((INTVAL (src) & 0xff) != 0) | |
6341 | + ((INTVAL (src) & 0xff00) != 0) | |
6342 | + ((INTVAL (src) & 0xff0000) != 0) | |
11f9ed1a | 6343 | + ((INTVAL (src) & 0xff000000) != 0); |
5fecfd8d MM |
6344 | else |
6345 | *len = 8; | |
6346 | ||
6347 | return ""; | |
6348 | } | |
6bec29c9 | 6349 | |
5fecfd8d | 6350 | if (cnst && ((INTVAL (src) & 0xff) == 0)) |
6bec29c9 DC |
6351 | output_asm_insn (AS2 (mov, %A0, __zero_reg__), operands); |
6352 | else | |
6353 | { | |
6354 | output_asm_insn (AS2 (ldi, %2, lo8(%1)), operands); | |
6355 | output_asm_insn (AS2 (mov, %A0, %2), operands); | |
6356 | } | |
5fecfd8d | 6357 | if (cnst && ((INTVAL (src) & 0xff00) == 0)) |
6bec29c9 DC |
6358 | output_asm_insn (AS2 (mov, %B0, __zero_reg__), operands); |
6359 | else | |
6360 | { | |
6361 | output_asm_insn (AS2 (ldi, %2, hi8(%1)), operands); | |
6362 | output_asm_insn (AS2 (mov, %B0, %2), operands); | |
6363 | } | |
5fecfd8d | 6364 | if (cnst && ((INTVAL (src) & 0xff0000) == 0)) |
6bec29c9 DC |
6365 | output_asm_insn (AS2 (mov, %C0, __zero_reg__), operands); |
6366 | else | |
6367 | { | |
6368 | output_asm_insn (AS2 (ldi, %2, hlo8(%1)), operands); | |
6369 | output_asm_insn (AS2 (mov, %C0, %2), operands); | |
6370 | } | |
11f9ed1a | 6371 | if (cnst && ((INTVAL (src) & 0xff000000) == 0)) |
6bec29c9 DC |
6372 | output_asm_insn (AS2 (mov, %D0, __zero_reg__), operands); |
6373 | else | |
6374 | { | |
6375 | output_asm_insn (AS2 (ldi, %2, hhi8(%1)), operands); | |
6376 | output_asm_insn (AS2 (mov, %D0, %2), operands); | |
6377 | } | |
6378 | return ""; | |
6379 | } | |
afee2a52 MM |
6380 | |
6381 | void | |
269e3795 | 6382 | avr_output_bld (rtx operands[], int bit_nr) |
afee2a52 MM |
6383 | { |
6384 | static char s[] = "bld %A0,0"; | |
6385 | ||
6386 | s[5] = 'A' + (bit_nr >> 3); | |
6387 | s[8] = '0' + (bit_nr & 7); | |
6388 | output_asm_insn (s, operands); | |
6389 | } | |
6390 | ||
e9284adf | 6391 | void |
269e3795 | 6392 | avr_output_addr_vec_elt (FILE *stream, int value) |
e9284adf | 6393 | { |
d6b5193b | 6394 | switch_to_section (progmem_section); |
693092fb BH |
6395 | if (AVR_HAVE_JMP_CALL) |
6396 | fprintf (stream, "\t.word gs(.L%d)\n", value); | |
e9284adf MM |
6397 | else |
6398 | fprintf (stream, "\trjmp .L%d\n", value); | |
e9284adf MM |
6399 | } |
6400 | ||
1cf0a7f1 | 6401 | /* Returns true if SCRATCH are safe to be allocated as a scratch |
dcfb1a36 MM |
6402 | registers (for a define_peephole2) in the current function. */ |
6403 | ||
1cf0a7f1 AS |
6404 | bool |
6405 | avr_hard_regno_scratch_ok (unsigned int regno) | |
dcfb1a36 | 6406 | { |
1cf0a7f1 AS |
6407 | /* Interrupt functions can only use registers that have already been saved |
6408 | by the prologue, even if they would normally be call-clobbered. */ | |
dcfb1a36 | 6409 | |
1cf0a7f1 AS |
6410 | if ((cfun->machine->is_interrupt || cfun->machine->is_signal) |
6411 | && !df_regs_ever_live_p (regno)) | |
6412 | return false; | |
6413 | ||
6414 | return true; | |
dcfb1a36 | 6415 | } |
331ca350 | 6416 | |
91635d08 AS |
6417 | /* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */ |
6418 | ||
6419 | int | |
6420 | avr_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, | |
6421 | unsigned int new_reg) | |
6422 | { | |
6423 | /* Interrupt functions can only use registers that have already been | |
6424 | saved by the prologue, even if they would normally be | |
6425 | call-clobbered. */ | |
6426 | ||
6427 | if ((cfun->machine->is_interrupt || cfun->machine->is_signal) | |
6428 | && !df_regs_ever_live_p (new_reg)) | |
6429 | return 0; | |
6430 | ||
6431 | return 1; | |
6432 | } | |
6433 | ||
ed1f5d71 | 6434 | /* Output a branch that tests a single bit of a register (QI, HI, SI or DImode) |
331ca350 MM |
6435 | or memory location in the I/O space (QImode only). |
6436 | ||
6437 | Operand 0: comparison operator (must be EQ or NE, compare bit to zero). | |
6438 | Operand 1: register operand to test, or CONST_INT memory address. | |
ed1f5d71 | 6439 | Operand 2: bit number. |
331ca350 MM |
6440 | Operand 3: label to jump to if the test is true. */ |
6441 | ||
6442 | const char * | |
269e3795 | 6443 | avr_out_sbxx_branch (rtx insn, rtx operands[]) |
331ca350 MM |
6444 | { |
6445 | enum rtx_code comp = GET_CODE (operands[0]); | |
6446 | int long_jump = (get_attr_length (insn) >= 4); | |
6447 | int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]); | |
6448 | ||
6449 | if (comp == GE) | |
6450 | comp = EQ; | |
6451 | else if (comp == LT) | |
6452 | comp = NE; | |
6453 | ||
6454 | if (reverse) | |
6455 | comp = reverse_condition (comp); | |
6456 | ||
6457 | if (GET_CODE (operands[1]) == CONST_INT) | |
6458 | { | |
6459 | if (INTVAL (operands[1]) < 0x40) | |
6460 | { | |
6461 | if (comp == EQ) | |
846428f1 | 6462 | output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands); |
331ca350 | 6463 | else |
846428f1 | 6464 | output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands); |
331ca350 MM |
6465 | } |
6466 | else | |
6467 | { | |
846428f1 | 6468 | output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands); |
331ca350 MM |
6469 | if (comp == EQ) |
6470 | output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands); | |
6471 | else | |
6472 | output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands); | |
6473 | } | |
6474 | } | |
6475 | else /* GET_CODE (operands[1]) == REG */ | |
6476 | { | |
6477 | if (GET_MODE (operands[1]) == QImode) | |
6478 | { | |
6479 | if (comp == EQ) | |
6480 | output_asm_insn (AS2 (sbrs,%1,%2), operands); | |
6481 | else | |
6482 | output_asm_insn (AS2 (sbrc,%1,%2), operands); | |
6483 | } | |
6484 | else /* HImode or SImode */ | |
6485 | { | |
6486 | static char buf[] = "sbrc %A1,0"; | |
ed1f5d71 | 6487 | int bit_nr = INTVAL (operands[2]); |
331ca350 MM |
6488 | buf[3] = (comp == EQ) ? 's' : 'c'; |
6489 | buf[6] = 'A' + (bit_nr >> 3); | |
6490 | buf[9] = '0' + (bit_nr & 7); | |
6491 | output_asm_insn (buf, operands); | |
6492 | } | |
6493 | } | |
6494 | ||
6495 | if (long_jump) | |
13e8651c | 6496 | return (AS1 (rjmp,.+4) CR_TAB |
846428f1 | 6497 | AS1 (jmp,%x3)); |
331ca350 | 6498 | if (!reverse) |
846428f1 | 6499 | return AS1 (rjmp,%x3); |
331ca350 MM |
6500 | return ""; |
6501 | } | |
9af145ae | 6502 | |
bd5bd7ac KH |
6503 | /* Worker function for TARGET_ASM_CONSTRUCTOR. */ |
6504 | ||
9af145ae | 6505 | static void |
269e3795 | 6506 | avr_asm_out_ctor (rtx symbol, int priority) |
9af145ae MM |
6507 | { |
6508 | fputs ("\t.global __do_global_ctors\n", asm_out_file); | |
6509 | default_ctor_section_asm_out_constructor (symbol, priority); | |
6510 | } | |
6511 | ||
bd5bd7ac KH |
6512 | /* Worker function for TARGET_ASM_DESTRUCTOR. */ |
6513 | ||
9af145ae | 6514 | static void |
269e3795 | 6515 | avr_asm_out_dtor (rtx symbol, int priority) |
9af145ae MM |
6516 | { |
6517 | fputs ("\t.global __do_global_dtors\n", asm_out_file); | |
6518 | default_dtor_section_asm_out_destructor (symbol, priority); | |
6519 | } | |
6520 | ||
bd5bd7ac KH |
6521 | /* Worker function for TARGET_RETURN_IN_MEMORY. */ |
6522 | ||
b069302c | 6523 | static bool |
586de218 | 6524 | avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) |
b069302c | 6525 | { |
78bc94a2 KH |
6526 | if (TYPE_MODE (type) == BLKmode) |
6527 | { | |
6528 | HOST_WIDE_INT size = int_size_in_bytes (type); | |
6529 | return (size == -1 || size > 8); | |
6530 | } | |
6531 | else | |
6532 | return false; | |
b069302c KH |
6533 | } |
6534 | ||
4223ff5f AS |
6535 | /* Worker function for CASE_VALUES_THRESHOLD. */ |
6536 | ||
6537 | unsigned int avr_case_values_threshold (void) | |
6538 | { | |
6539 | return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17; | |
6540 | } | |
6541 | ||
43ea6502 AS |
6542 | /* Helper for __builtin_avr_delay_cycles */ |
6543 | ||
6544 | static void | |
6545 | avr_expand_delay_cycles (rtx operands0) | |
6546 | { | |
6547 | unsigned HOST_WIDE_INT cycles = UINTVAL (operands0); | |
6548 | unsigned HOST_WIDE_INT cycles_used; | |
6549 | unsigned HOST_WIDE_INT loop_count; | |
6550 | ||
6551 | if (IN_RANGE (cycles, 83886082, 0xFFFFFFFF)) | |
6552 | { | |
6553 | loop_count = ((cycles - 9) / 6) + 1; | |
6554 | cycles_used = ((loop_count - 1) * 6) + 9; | |
6555 | emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode))); | |
6556 | cycles -= cycles_used; | |
6557 | } | |
6558 | ||
6559 | if (IN_RANGE (cycles, 262145, 83886081)) | |
6560 | { | |
6561 | loop_count = ((cycles - 7) / 5) + 1; | |
6562 | if (loop_count > 0xFFFFFF) | |
6563 | loop_count = 0xFFFFFF; | |
6564 | cycles_used = ((loop_count - 1) * 5) + 7; | |
6565 | emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode))); | |
6566 | cycles -= cycles_used; | |
6567 | } | |
6568 | ||
6569 | if (IN_RANGE (cycles, 768, 262144)) | |
6570 | { | |
6571 | loop_count = ((cycles - 5) / 4) + 1; | |
6572 | if (loop_count > 0xFFFF) | |
6573 | loop_count = 0xFFFF; | |
6574 | cycles_used = ((loop_count - 1) * 4) + 5; | |
6575 | emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode))); | |
6576 | cycles -= cycles_used; | |
6577 | } | |
6578 | ||
6579 | if (IN_RANGE (cycles, 6, 767)) | |
6580 | { | |
6581 | loop_count = cycles / 3; | |
6582 | if (loop_count > 255) | |
6583 | loop_count = 255; | |
6584 | cycles_used = loop_count * 3; | |
6585 | emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode))); | |
6586 | cycles -= cycles_used; | |
6587 | } | |
6588 | ||
6589 | while (cycles >= 2) | |
6590 | { | |
6591 | emit_insn (gen_nopv (GEN_INT(2))); | |
6592 | cycles -= 2; | |
6593 | } | |
6594 | ||
6595 | if (cycles == 1) | |
6596 | { | |
6597 | emit_insn (gen_nopv (GEN_INT(1))); | |
6598 | cycles--; | |
6599 | } | |
6600 | } | |
6601 | ||
6602 | /* IDs for all the AVR builtins. */ | |
6603 | ||
6604 | enum avr_builtin_id | |
6605 | { | |
6606 | AVR_BUILTIN_NOP, | |
6607 | AVR_BUILTIN_SEI, | |
6608 | AVR_BUILTIN_CLI, | |
6609 | AVR_BUILTIN_WDR, | |
6610 | AVR_BUILTIN_SLEEP, | |
6611 | AVR_BUILTIN_SWAP, | |
6612 | AVR_BUILTIN_FMUL, | |
6613 | AVR_BUILTIN_FMULS, | |
6614 | AVR_BUILTIN_FMULSU, | |
6615 | AVR_BUILTIN_DELAY_CYCLES | |
6616 | }; | |
6617 | ||
6618 | #define DEF_BUILTIN(NAME, TYPE, CODE) \ | |
6619 | do \ | |
6620 | { \ | |
6621 | add_builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, \ | |
6622 | NULL, NULL_TREE); \ | |
6623 | } while (0) | |
6624 | ||
6625 | ||
6626 | /* Implement `TARGET_INIT_BUILTINS' */ | |
6627 | /* Set up all builtin functions for this target. */ | |
6628 | ||
6629 | static void | |
6630 | avr_init_builtins (void) | |
6631 | { | |
6632 | tree void_ftype_void | |
c19afe52 | 6633 | = build_function_type_list (void_type_node, NULL_TREE); |
43ea6502 AS |
6634 | tree uchar_ftype_uchar |
6635 | = build_function_type_list (unsigned_char_type_node, | |
6636 | unsigned_char_type_node, | |
6637 | NULL_TREE); | |
6638 | tree uint_ftype_uchar_uchar | |
6639 | = build_function_type_list (unsigned_type_node, | |
6640 | unsigned_char_type_node, | |
6641 | unsigned_char_type_node, | |
6642 | NULL_TREE); | |
6643 | tree int_ftype_char_char | |
6644 | = build_function_type_list (integer_type_node, | |
6645 | char_type_node, | |
6646 | char_type_node, | |
6647 | NULL_TREE); | |
6648 | tree int_ftype_char_uchar | |
6649 | = build_function_type_list (integer_type_node, | |
6650 | char_type_node, | |
6651 | unsigned_char_type_node, | |
6652 | NULL_TREE); | |
6653 | tree void_ftype_ulong | |
6654 | = build_function_type_list (void_type_node, | |
6655 | long_unsigned_type_node, | |
6656 | NULL_TREE); | |
6657 | ||
6658 | DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP); | |
6659 | DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI); | |
6660 | DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI); | |
6661 | DEF_BUILTIN ("__builtin_avr_wdr", void_ftype_void, AVR_BUILTIN_WDR); | |
6662 | DEF_BUILTIN ("__builtin_avr_sleep", void_ftype_void, AVR_BUILTIN_SLEEP); | |
6663 | DEF_BUILTIN ("__builtin_avr_swap", uchar_ftype_uchar, AVR_BUILTIN_SWAP); | |
6664 | DEF_BUILTIN ("__builtin_avr_delay_cycles", void_ftype_ulong, | |
6665 | AVR_BUILTIN_DELAY_CYCLES); | |
6666 | ||
6667 | if (AVR_HAVE_MUL) | |
6668 | { | |
6669 | /* FIXME: If !AVR_HAVE_MUL, make respective functions available | |
6670 | in libgcc. For fmul and fmuls this is straight forward with | |
6671 | upcoming fixed point support. */ | |
6672 | ||
6673 | DEF_BUILTIN ("__builtin_avr_fmul", uint_ftype_uchar_uchar, | |
6674 | AVR_BUILTIN_FMUL); | |
6675 | DEF_BUILTIN ("__builtin_avr_fmuls", int_ftype_char_char, | |
6676 | AVR_BUILTIN_FMULS); | |
6677 | DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, | |
6678 | AVR_BUILTIN_FMULSU); | |
6679 | } | |
6680 | } | |
6681 | ||
6682 | #undef DEF_BUILTIN | |
6683 | ||
6684 | struct avr_builtin_description | |
6685 | { | |
6686 | const enum insn_code icode; | |
6687 | const char *const name; | |
6688 | const enum avr_builtin_id id; | |
6689 | }; | |
6690 | ||
6691 | static const struct avr_builtin_description | |
6692 | bdesc_1arg[] = | |
6693 | { | |
6694 | { CODE_FOR_rotlqi3_4, "__builtin_avr_swap", AVR_BUILTIN_SWAP } | |
6695 | }; | |
6696 | ||
6697 | static const struct avr_builtin_description | |
6698 | bdesc_2arg[] = | |
6699 | { | |
6700 | { CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL }, | |
6701 | { CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS }, | |
6702 | { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU } | |
6703 | }; | |
6704 | ||
6705 | /* Subroutine of avr_expand_builtin to take care of unop insns. */ | |
6706 | ||
6707 | static rtx | |
6708 | avr_expand_unop_builtin (enum insn_code icode, tree exp, | |
6709 | rtx target) | |
6710 | { | |
6711 | rtx pat; | |
6712 | tree arg0 = CALL_EXPR_ARG (exp, 0); | |
fd01a351 | 6713 | rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); |
43ea6502 AS |
6714 | enum machine_mode op0mode = GET_MODE (op0); |
6715 | enum machine_mode tmode = insn_data[icode].operand[0].mode; | |
6716 | enum machine_mode mode0 = insn_data[icode].operand[1].mode; | |
6717 | ||
6718 | if (! target | |
6719 | || GET_MODE (target) != tmode | |
6720 | || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) | |
6721 | { | |
6722 | target = gen_reg_rtx (tmode); | |
6723 | } | |
6724 | ||
6725 | if (op0mode == SImode && mode0 == HImode) | |
6726 | { | |
6727 | op0mode = HImode; | |
6728 | op0 = gen_lowpart (HImode, op0); | |
6729 | } | |
6730 | ||
6731 | gcc_assert (op0mode == mode0 || op0mode == VOIDmode); | |
6732 | ||
6733 | if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) | |
6734 | op0 = copy_to_mode_reg (mode0, op0); | |
6735 | ||
6736 | pat = GEN_FCN (icode) (target, op0); | |
6737 | if (! pat) | |
6738 | return 0; | |
6739 | ||
6740 | emit_insn (pat); | |
6741 | ||
6742 | return target; | |
6743 | } | |
6744 | ||
6745 | ||
6746 | /* Subroutine of avr_expand_builtin to take care of binop insns. */ | |
6747 | ||
6748 | static rtx | |
6749 | avr_expand_binop_builtin (enum insn_code icode, tree exp, rtx target) | |
6750 | { | |
6751 | rtx pat; | |
6752 | tree arg0 = CALL_EXPR_ARG (exp, 0); | |
6753 | tree arg1 = CALL_EXPR_ARG (exp, 1); | |
fd01a351 GJL |
6754 | rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); |
6755 | rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, EXPAND_NORMAL); | |
43ea6502 AS |
6756 | enum machine_mode op0mode = GET_MODE (op0); |
6757 | enum machine_mode op1mode = GET_MODE (op1); | |
6758 | enum machine_mode tmode = insn_data[icode].operand[0].mode; | |
6759 | enum machine_mode mode0 = insn_data[icode].operand[1].mode; | |
6760 | enum machine_mode mode1 = insn_data[icode].operand[2].mode; | |
6761 | ||
6762 | if (! target | |
6763 | || GET_MODE (target) != tmode | |
6764 | || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) | |
6765 | { | |
6766 | target = gen_reg_rtx (tmode); | |
6767 | } | |
6768 | ||
6769 | if ((op0mode == SImode || op0mode == VOIDmode) && mode0 == HImode) | |
6770 | { | |
6771 | op0mode = HImode; | |
6772 | op0 = gen_lowpart (HImode, op0); | |
6773 | } | |
6774 | ||
6775 | if ((op1mode == SImode || op1mode == VOIDmode) && mode1 == HImode) | |
6776 | { | |
6777 | op1mode = HImode; | |
6778 | op1 = gen_lowpart (HImode, op1); | |
6779 | } | |
6780 | ||
6781 | /* In case the insn wants input operands in modes different from | |
6782 | the result, abort. */ | |
6783 | ||
6784 | gcc_assert ((op0mode == mode0 || op0mode == VOIDmode) | |
6785 | && (op1mode == mode1 || op1mode == VOIDmode)); | |
6786 | ||
6787 | if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) | |
6788 | op0 = copy_to_mode_reg (mode0, op0); | |
6789 | ||
6790 | if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) | |
6791 | op1 = copy_to_mode_reg (mode1, op1); | |
6792 | ||
6793 | pat = GEN_FCN (icode) (target, op0, op1); | |
6794 | ||
6795 | if (! pat) | |
6796 | return 0; | |
6797 | ||
6798 | emit_insn (pat); | |
6799 | return target; | |
6800 | } | |
6801 | ||
6802 | ||
6803 | /* Expand an expression EXP that calls a built-in function, | |
6804 | with result going to TARGET if that's convenient | |
6805 | (and in mode MODE if that's convenient). | |
6806 | SUBTARGET may be used as the target for computing one of EXP's operands. | |
6807 | IGNORE is nonzero if the value is to be ignored. */ | |
6808 | ||
6809 | static rtx | |
6810 | avr_expand_builtin (tree exp, rtx target, | |
6811 | rtx subtarget ATTRIBUTE_UNUSED, | |
6812 | enum machine_mode mode ATTRIBUTE_UNUSED, | |
6813 | int ignore ATTRIBUTE_UNUSED) | |
6814 | { | |
6815 | size_t i; | |
6816 | const struct avr_builtin_description *d; | |
6817 | tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); | |
6818 | unsigned int id = DECL_FUNCTION_CODE (fndecl); | |
6819 | tree arg0; | |
6820 | rtx op0; | |
6821 | ||
6822 | switch (id) | |
6823 | { | |
6824 | case AVR_BUILTIN_NOP: | |
6825 | emit_insn (gen_nopv (GEN_INT(1))); | |
6826 | return 0; | |
6827 | ||
6828 | case AVR_BUILTIN_SEI: | |
6829 | emit_insn (gen_enable_interrupt ()); | |
6830 | return 0; | |
6831 | ||
6832 | case AVR_BUILTIN_CLI: | |
6833 | emit_insn (gen_disable_interrupt ()); | |
6834 | return 0; | |
6835 | ||
6836 | case AVR_BUILTIN_WDR: | |
6837 | emit_insn (gen_wdr ()); | |
6838 | return 0; | |
6839 | ||
6840 | case AVR_BUILTIN_SLEEP: | |
6841 | emit_insn (gen_sleep ()); | |
6842 | return 0; | |
6843 | ||
6844 | case AVR_BUILTIN_DELAY_CYCLES: | |
6845 | { | |
6846 | arg0 = CALL_EXPR_ARG (exp, 0); | |
fd01a351 | 6847 | op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); |
43ea6502 AS |
6848 | |
6849 | if (! CONST_INT_P (op0)) | |
6850 | error ("__builtin_avr_delay_cycles expects a compile time integer constant."); | |
6851 | ||
6852 | avr_expand_delay_cycles (op0); | |
6853 | return 0; | |
6854 | } | |
6855 | } | |
6856 | ||
6857 | for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) | |
6858 | if (d->id == id) | |
6859 | return avr_expand_unop_builtin (d->icode, exp, target); | |
6860 | ||
6861 | for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) | |
6862 | if (d->id == id) | |
6863 | return avr_expand_binop_builtin (d->icode, exp, target); | |
6864 | ||
6865 | gcc_unreachable (); | |
6866 | } | |
6867 | ||
6868 | ||
baac771a | 6869 | #include "gt-avr.h" |