2009-07-17 Richard Guenther <rguenther@suse.de>
[official-gcc.git] / gcc / config / m68hc11 / m68hc11.c
blob6cb0ed5f321c3ef9b180c13eb16f274e716bc49e
1 /* Subroutines for code generation on Motorola 68HC11 and 68HC12.
2 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
3 2009 Free Software Foundation, Inc.
4 Contributed by Stephane Carrez (stcarrez@nerim.fr)
6 This file is part of GCC.
8 GCC is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
11 any later version.
13 GCC is distributed in the hope that it will be useful,
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.
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3. If not see
20 <http://www.gnu.org/licenses/>.
22 Note:
23 A first 68HC11 port was made by Otto Lind (otto@coactive.com)
24 on gcc 2.6.3. I have used it as a starting point for this port.
25 However, this new port is a complete re-write. Its internal
26 design is completely different. The generated code is not
27 compatible with the gcc 2.6.3 port.
29 The gcc 2.6.3 port is available at:
31 ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
35 #include <stdio.h>
36 #include "config.h"
37 #include "system.h"
38 #include "coretypes.h"
39 #include "tm.h"
40 #include "rtl.h"
41 #include "tree.h"
42 #include "expr.h"
43 #include "tm_p.h"
44 #include "regs.h"
45 #include "hard-reg-set.h"
46 #include "real.h"
47 #include "insn-config.h"
48 #include "conditions.h"
49 #include "output.h"
50 #include "insn-attr.h"
51 #include "flags.h"
52 #include "recog.h"
53 #include "expr.h"
54 #include "libfuncs.h"
55 #include "toplev.h"
56 #include "basic-block.h"
57 #include "function.h"
58 #include "ggc.h"
59 #include "reload.h"
60 #include "target.h"
61 #include "target-def.h"
62 #include "df.h"
64 static void emit_move_after_reload (rtx, rtx, rtx);
65 static rtx simplify_logical (enum machine_mode, int, rtx, rtx *);
66 static void m68hc11_emit_logical (enum machine_mode, enum rtx_code, rtx *);
67 static void m68hc11_reorg (void);
68 static bool m68hc11_legitimate_address_p_1 (enum machine_mode, rtx, bool);
69 static bool m68hc11_legitimate_address_p (enum machine_mode, rtx, bool);
70 static rtx m68hc11_expand_compare (enum rtx_code, rtx, rtx);
71 static int must_parenthesize (rtx);
72 static int m68hc11_address_cost (rtx, bool);
73 static int m68hc11_shift_cost (enum machine_mode, rtx, int);
74 static int m68hc11_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code);
75 static bool m68hc11_rtx_costs (rtx, int, int, int *, bool);
76 static tree m68hc11_handle_fntype_attribute (tree *, tree, tree, int, bool *);
77 static tree m68hc11_handle_page0_attribute (tree *, tree, tree, int, bool *);
79 void create_regs_rtx (void);
81 static void asm_print_register (FILE *, int);
82 static void m68hc11_output_function_epilogue (FILE *, HOST_WIDE_INT);
83 static void m68hc11_asm_out_constructor (rtx, int);
84 static void m68hc11_asm_out_destructor (rtx, int);
85 static void m68hc11_file_start (void);
86 static void m68hc11_encode_section_info (tree, rtx, int);
87 static const char *m68hc11_strip_name_encoding (const char* str);
88 static unsigned int m68hc11_section_type_flags (tree, const char*, int);
89 static int autoinc_mode (rtx);
90 static int m68hc11_make_autoinc_notes (rtx *, void *);
91 static void m68hc11_init_libfuncs (void);
92 static rtx m68hc11_struct_value_rtx (tree, int);
93 static bool m68hc11_return_in_memory (const_tree, const_tree);
95 /* Must be set to 1 to produce debug messages. */
96 int debug_m6811 = 0;
98 extern FILE *asm_out_file;
100 rtx ix_reg;
101 rtx iy_reg;
102 rtx d_reg;
103 rtx m68hc11_soft_tmp_reg;
104 static GTY(()) rtx stack_push_word;
105 static GTY(()) rtx stack_pop_word;
106 static GTY(()) rtx z_reg;
107 static GTY(()) rtx z_reg_qi;
108 static int regs_inited = 0;
110 /* Set to 1 by expand_prologue() when the function is an interrupt handler. */
111 int current_function_interrupt;
113 /* Set to 1 by expand_prologue() when the function is a trap handler. */
114 int current_function_trap;
116 /* Set to 1 when the current function is placed in 68HC12 banked
117 memory and must return with rtc. */
118 int current_function_far;
120 /* Min offset that is valid for the indirect addressing mode. */
121 HOST_WIDE_INT m68hc11_min_offset = 0;
123 /* Max offset that is valid for the indirect addressing mode. */
124 HOST_WIDE_INT m68hc11_max_offset = 256;
126 /* The class value for base registers. */
127 enum reg_class m68hc11_base_reg_class = A_REGS;
129 /* The class value for index registers. This is NO_REGS for 68HC11. */
130 enum reg_class m68hc11_index_reg_class = NO_REGS;
132 enum reg_class m68hc11_tmp_regs_class = NO_REGS;
134 /* Tables that tell whether a given hard register is valid for
135 a base or an index register. It is filled at init time depending
136 on the target processor. */
137 unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
138 unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
140 /* A correction offset which is applied to the stack pointer.
141 This is 1 for 68HC11 and 0 for 68HC12. */
142 int m68hc11_sp_correction;
144 int m68hc11_addr_mode;
145 int m68hc11_mov_addr_mode;
148 const struct processor_costs *m68hc11_cost;
150 /* Costs for a 68HC11. */
151 static const struct processor_costs m6811_cost = {
152 /* add */
153 COSTS_N_INSNS (2),
154 /* logical */
155 COSTS_N_INSNS (2),
156 /* non-constant shift */
157 COSTS_N_INSNS (20),
158 /* shiftQI const */
159 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
160 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
161 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
163 /* shiftHI const */
164 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
165 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
166 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
167 COSTS_N_INSNS (2), COSTS_N_INSNS (4),
168 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
169 COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
171 /* mulQI */
172 COSTS_N_INSNS (20),
173 /* mulHI */
174 COSTS_N_INSNS (20 * 4),
175 /* mulSI */
176 COSTS_N_INSNS (20 * 16),
177 /* divQI */
178 COSTS_N_INSNS (20),
179 /* divHI */
180 COSTS_N_INSNS (80),
181 /* divSI */
182 COSTS_N_INSNS (100)
185 /* Costs for a 68HC12. */
186 static const struct processor_costs m6812_cost = {
187 /* add */
188 COSTS_N_INSNS (2),
189 /* logical */
190 COSTS_N_INSNS (2),
191 /* non-constant shift */
192 COSTS_N_INSNS (20),
193 /* shiftQI const */
194 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
195 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
196 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
198 /* shiftHI const */
199 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
200 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
201 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
202 COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
203 COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
204 COSTS_N_INSNS (6), COSTS_N_INSNS (4)
206 /* mulQI */
207 COSTS_N_INSNS (3),
208 /* mulHI */
209 COSTS_N_INSNS (3),
210 /* mulSI */
211 COSTS_N_INSNS (3 * 4),
212 /* divQI */
213 COSTS_N_INSNS (12),
214 /* divHI */
215 COSTS_N_INSNS (12),
216 /* divSI */
217 COSTS_N_INSNS (100)
220 /* M68HC11 specific attributes. */
222 static const struct attribute_spec m68hc11_attribute_table[] =
224 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
225 { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
226 { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
227 { "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
228 { "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
229 { "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute },
230 { NULL, 0, 0, false, false, false, NULL }
233 /* Initialize the GCC target structure. */
234 #undef TARGET_ATTRIBUTE_TABLE
235 #define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
237 #undef TARGET_ASM_ALIGNED_HI_OP
238 #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
240 #undef TARGET_ASM_FUNCTION_EPILOGUE
241 #define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
243 #undef TARGET_ASM_FILE_START
244 #define TARGET_ASM_FILE_START m68hc11_file_start
245 #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
246 #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
248 #undef TARGET_DEFAULT_TARGET_FLAGS
249 #define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
251 #undef TARGET_ENCODE_SECTION_INFO
252 #define TARGET_ENCODE_SECTION_INFO m68hc11_encode_section_info
254 #undef TARGET_SECTION_TYPE_FLAGS
255 #define TARGET_SECTION_TYPE_FLAGS m68hc11_section_type_flags
257 #undef TARGET_RTX_COSTS
258 #define TARGET_RTX_COSTS m68hc11_rtx_costs
259 #undef TARGET_ADDRESS_COST
260 #define TARGET_ADDRESS_COST m68hc11_address_cost
262 #undef TARGET_MACHINE_DEPENDENT_REORG
263 #define TARGET_MACHINE_DEPENDENT_REORG m68hc11_reorg
265 #undef TARGET_INIT_LIBFUNCS
266 #define TARGET_INIT_LIBFUNCS m68hc11_init_libfuncs
268 #undef TARGET_STRUCT_VALUE_RTX
269 #define TARGET_STRUCT_VALUE_RTX m68hc11_struct_value_rtx
270 #undef TARGET_RETURN_IN_MEMORY
271 #define TARGET_RETURN_IN_MEMORY m68hc11_return_in_memory
272 #undef TARGET_CALLEE_COPIES
273 #define TARGET_CALLEE_COPIES hook_callee_copies_named
275 #undef TARGET_STRIP_NAME_ENCODING
276 #define TARGET_STRIP_NAME_ENCODING m68hc11_strip_name_encoding
278 #undef TARGET_LEGITIMATE_ADDRESS_P
279 #define TARGET_LEGITIMATE_ADDRESS_P m68hc11_legitimate_address_p
281 struct gcc_target targetm = TARGET_INITIALIZER;
284 m68hc11_override_options (void)
286 memset (m68hc11_reg_valid_for_index, 0,
287 sizeof (m68hc11_reg_valid_for_index));
288 memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
290 /* Compilation with -fpic generates a wrong code. */
291 if (flag_pic)
293 warning (0, "-f%s ignored for 68HC11/68HC12 (not supported)",
294 (flag_pic > 1) ? "PIC" : "pic");
295 flag_pic = 0;
298 /* Do not enable -fweb because it breaks the 32-bit shift patterns
299 by breaking the match_dup of those patterns. The shift patterns
300 will no longer be recognized after that. */
301 flag_web = 0;
303 /* Configure for a 68hc11 processor. */
304 if (TARGET_M6811)
306 target_flags &= ~(TARGET_AUTO_INC_DEC | TARGET_MIN_MAX);
307 m68hc11_cost = &m6811_cost;
308 m68hc11_min_offset = 0;
309 m68hc11_max_offset = 256;
310 m68hc11_index_reg_class = NO_REGS;
311 m68hc11_base_reg_class = A_REGS;
312 m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
313 m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
314 m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
315 m68hc11_sp_correction = 1;
316 m68hc11_tmp_regs_class = D_REGS;
317 m68hc11_addr_mode = ADDR_OFFSET;
318 m68hc11_mov_addr_mode = 0;
319 if (m68hc11_soft_reg_count < 0)
320 m68hc11_soft_reg_count = 4;
323 /* Configure for a 68hc12 processor. */
324 if (TARGET_M6812)
326 m68hc11_cost = &m6812_cost;
327 m68hc11_min_offset = -65536;
328 m68hc11_max_offset = 65536;
329 m68hc11_index_reg_class = D_REGS;
330 m68hc11_base_reg_class = A_OR_SP_REGS;
331 m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
332 m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
333 m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
334 m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
335 m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
336 m68hc11_sp_correction = 0;
337 m68hc11_tmp_regs_class = TMP_REGS;
338 m68hc11_addr_mode = ADDR_INDIRECT | ADDR_OFFSET | ADDR_CONST
339 | (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
340 m68hc11_mov_addr_mode = ADDR_OFFSET | ADDR_CONST
341 | (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
342 target_flags |= MASK_NO_DIRECT_MODE;
343 if (m68hc11_soft_reg_count < 0)
344 m68hc11_soft_reg_count = 0;
346 if (TARGET_LONG_CALLS)
347 current_function_far = 1;
349 return 0;
353 void
354 m68hc11_conditional_register_usage (void)
356 int i;
358 if (m68hc11_soft_reg_count > SOFT_REG_LAST - SOFT_REG_FIRST)
359 m68hc11_soft_reg_count = SOFT_REG_LAST - SOFT_REG_FIRST;
361 for (i = SOFT_REG_FIRST + m68hc11_soft_reg_count; i < SOFT_REG_LAST; i++)
363 fixed_regs[i] = 1;
364 call_used_regs[i] = 1;
367 /* For 68HC12, the Z register emulation is not necessary when the
368 frame pointer is not used. The frame pointer is eliminated and
369 replaced by the stack register (which is a BASE_REG_CLASS). */
370 if (TARGET_M6812 && flag_omit_frame_pointer && optimize)
372 fixed_regs[HARD_Z_REGNUM] = 1;
377 /* Reload and register operations. */
380 void
381 create_regs_rtx (void)
383 /* regs_inited = 1; */
384 ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
385 iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
386 d_reg = gen_rtx_REG (HImode, HARD_D_REGNUM);
387 m68hc11_soft_tmp_reg = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
389 stack_push_word = gen_rtx_MEM (HImode,
390 gen_rtx_PRE_DEC (HImode,
391 gen_rtx_REG (HImode, HARD_SP_REGNUM)));
392 stack_pop_word = gen_rtx_MEM (HImode,
393 gen_rtx_POST_INC (HImode,
394 gen_rtx_REG (HImode, HARD_SP_REGNUM)));
398 /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
399 - 8-bit values are stored anywhere (except the SP register).
400 - 16-bit values can be stored in any register whose mode is 16
401 - 32-bit values can be stored in D, X registers or in a soft register
402 (except the last one because we need 2 soft registers)
403 - Values whose size is > 32 bit are not stored in real hard
404 registers. They may be stored in soft registers if there are
405 enough of them. */
407 hard_regno_mode_ok (int regno, enum machine_mode mode)
409 switch (GET_MODE_SIZE (mode))
411 case 8:
412 return S_REGNO_P (regno) && m68hc11_soft_reg_count >= 4;
414 case 4:
415 return (X_REGNO_P (regno)
416 || (S_REGNO_P (regno) && m68hc11_soft_reg_count >= 2));
418 case 2:
419 return G_REGNO_P (regno);
421 case 1:
422 /* We have to accept a QImode in X or Y registers. Otherwise, the
423 reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
424 in the insns. Reload fails if the insn rejects the register class 'a'
425 as well as if it accepts it. Patterns that failed were
426 zero_extend_qihi2 and iorqi3. */
428 return G_REGNO_P (regno) && !SP_REGNO_P (regno);
430 default:
431 return 0;
436 m68hc11_hard_regno_rename_ok (int reg1, int reg2)
438 /* Don't accept renaming to Z register. We will replace it to
439 X,Y or D during machine reorg pass. */
440 if (reg2 == HARD_Z_REGNUM)
441 return 0;
443 /* Don't accept renaming D,X to Y register as the code will be bigger. */
444 if (TARGET_M6811 && reg2 == HARD_Y_REGNUM
445 && (D_REGNO_P (reg1) || X_REGNO_P (reg1)))
446 return 0;
448 return 1;
451 enum reg_class
452 preferred_reload_class (rtx operand, enum reg_class rclass)
454 enum machine_mode mode;
456 mode = GET_MODE (operand);
458 if (debug_m6811)
460 printf ("Preferred reload: (class=%s): ", reg_class_names[rclass]);
463 if (rclass == D_OR_A_OR_S_REGS && SP_REG_P (operand))
464 return m68hc11_base_reg_class;
466 if (rclass >= S_REGS && (GET_CODE (operand) == MEM
467 || GET_CODE (operand) == CONST_INT))
469 /* S_REGS class must not be used. The movhi template does not
470 work to move a memory to a soft register.
471 Restrict to a hard reg. */
472 switch (rclass)
474 default:
475 case G_REGS:
476 case D_OR_A_OR_S_REGS:
477 rclass = A_OR_D_REGS;
478 break;
479 case A_OR_S_REGS:
480 rclass = A_REGS;
481 break;
482 case D_OR_SP_OR_S_REGS:
483 rclass = D_OR_SP_REGS;
484 break;
485 case D_OR_Y_OR_S_REGS:
486 rclass = D_OR_Y_REGS;
487 break;
488 case D_OR_X_OR_S_REGS:
489 rclass = D_OR_X_REGS;
490 break;
491 case SP_OR_S_REGS:
492 rclass = SP_REGS;
493 break;
494 case Y_OR_S_REGS:
495 rclass = Y_REGS;
496 break;
497 case X_OR_S_REGS:
498 rclass = X_REGS;
499 break;
500 case D_OR_S_REGS:
501 rclass = D_REGS;
504 else if (rclass == Y_REGS && GET_CODE (operand) == MEM)
506 rclass = Y_REGS;
508 else if (rclass == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
510 rclass = D_OR_X_REGS;
512 else if (rclass >= S_REGS && S_REG_P (operand))
514 switch (rclass)
516 default:
517 case G_REGS:
518 case D_OR_A_OR_S_REGS:
519 rclass = A_OR_D_REGS;
520 break;
521 case A_OR_S_REGS:
522 rclass = A_REGS;
523 break;
524 case D_OR_SP_OR_S_REGS:
525 rclass = D_OR_SP_REGS;
526 break;
527 case D_OR_Y_OR_S_REGS:
528 rclass = D_OR_Y_REGS;
529 break;
530 case D_OR_X_OR_S_REGS:
531 rclass = D_OR_X_REGS;
532 break;
533 case SP_OR_S_REGS:
534 rclass = SP_REGS;
535 break;
536 case Y_OR_S_REGS:
537 rclass = Y_REGS;
538 break;
539 case X_OR_S_REGS:
540 rclass = X_REGS;
541 break;
542 case D_OR_S_REGS:
543 rclass = D_REGS;
546 else if (rclass >= S_REGS)
548 if (debug_m6811)
550 printf ("Class = %s for: ", reg_class_names[rclass]);
551 fflush (stdout);
552 debug_rtx (operand);
556 if (debug_m6811)
558 printf (" => class=%s\n", reg_class_names[rclass]);
559 fflush (stdout);
560 debug_rtx (operand);
563 return rclass;
566 /* Return 1 if the operand is a valid indexed addressing mode.
567 For 68hc11: n,r with n in [0..255] and r in A_REGS class
568 For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
570 m68hc11_valid_addressing_p (rtx operand, enum machine_mode mode, int addr_mode)
572 rtx base, offset;
574 switch (GET_CODE (operand))
576 case MEM:
577 if ((addr_mode & ADDR_INDIRECT) && GET_MODE_SIZE (mode) <= 2)
578 return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
579 addr_mode & (ADDR_STRICT | ADDR_OFFSET));
580 return 0;
582 case POST_INC:
583 case PRE_INC:
584 case POST_DEC:
585 case PRE_DEC:
586 if (addr_mode & ADDR_INCDEC)
587 return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
588 addr_mode & ADDR_STRICT);
589 return 0;
591 case PLUS:
592 base = XEXP (operand, 0);
593 if (GET_CODE (base) == MEM)
594 return 0;
596 offset = XEXP (operand, 1);
597 if (GET_CODE (offset) == MEM)
598 return 0;
600 /* Indexed addressing mode with 2 registers. */
601 if (GET_CODE (base) == REG && GET_CODE (offset) == REG)
603 if (!(addr_mode & ADDR_INDEXED))
604 return 0;
606 addr_mode &= ADDR_STRICT;
607 if (REGNO_OK_FOR_BASE_P2 (REGNO (base), addr_mode)
608 && REGNO_OK_FOR_INDEX_P2 (REGNO (offset), addr_mode))
609 return 1;
611 if (REGNO_OK_FOR_BASE_P2 (REGNO (offset), addr_mode)
612 && REGNO_OK_FOR_INDEX_P2 (REGNO (base), addr_mode))
613 return 1;
615 return 0;
618 if (!(addr_mode & ADDR_OFFSET))
619 return 0;
621 if (GET_CODE (base) == REG)
623 if (!VALID_CONSTANT_OFFSET_P (offset, mode))
624 return 0;
626 if (!(addr_mode & ADDR_STRICT))
627 return 1;
629 return REGNO_OK_FOR_BASE_P2 (REGNO (base), 1);
632 if (GET_CODE (offset) == REG)
634 if (!VALID_CONSTANT_OFFSET_P (base, mode))
635 return 0;
637 if (!(addr_mode & ADDR_STRICT))
638 return 1;
640 return REGNO_OK_FOR_BASE_P2 (REGNO (offset), 1);
642 return 0;
644 case REG:
645 return REGNO_OK_FOR_BASE_P2 (REGNO (operand), addr_mode & ADDR_STRICT);
647 case CONST_INT:
648 if (addr_mode & ADDR_CONST)
649 return VALID_CONSTANT_OFFSET_P (operand, mode);
650 return 0;
652 default:
653 return 0;
657 /* Returns 1 if the operand fits in a 68HC11 indirect mode or in
658 a 68HC12 1-byte index addressing mode. */
660 m68hc11_small_indexed_indirect_p (rtx operand, enum machine_mode mode)
662 rtx base, offset;
663 int addr_mode;
665 if (GET_CODE (operand) == REG && reload_in_progress
666 && REGNO (operand) >= FIRST_PSEUDO_REGISTER
667 && reg_equiv_memory_loc[REGNO (operand)])
669 operand = reg_equiv_memory_loc[REGNO (operand)];
670 operand = eliminate_regs (operand, VOIDmode, NULL_RTX);
673 if (GET_CODE (operand) != MEM)
674 return 0;
676 operand = XEXP (operand, 0);
677 if (CONSTANT_ADDRESS_P (operand))
678 return 1;
680 if (PUSH_POP_ADDRESS_P (operand))
681 return 1;
683 addr_mode = m68hc11_mov_addr_mode | (reload_completed ? ADDR_STRICT : 0);
684 if (!m68hc11_valid_addressing_p (operand, mode, addr_mode))
685 return 0;
687 if (TARGET_M6812 && GET_CODE (operand) == PLUS
688 && (reload_completed | reload_in_progress))
690 base = XEXP (operand, 0);
691 offset = XEXP (operand, 1);
693 /* The offset can be a symbol address and this is too big
694 for the operand constraint. */
695 if (GET_CODE (base) != CONST_INT && GET_CODE (offset) != CONST_INT)
696 return 0;
698 if (GET_CODE (base) == CONST_INT)
699 offset = base;
701 switch (GET_MODE_SIZE (mode))
703 case 8:
704 if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
705 return 0;
706 break;
708 case 4:
709 if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
710 return 0;
711 break;
713 default:
714 if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
715 return 0;
716 break;
719 return 1;
723 m68hc11_register_indirect_p (rtx operand, enum machine_mode mode)
725 int addr_mode;
727 if (GET_CODE (operand) == REG && reload_in_progress
728 && REGNO (operand) >= FIRST_PSEUDO_REGISTER
729 && reg_equiv_memory_loc[REGNO (operand)])
731 operand = reg_equiv_memory_loc[REGNO (operand)];
732 operand = eliminate_regs (operand, VOIDmode, NULL_RTX);
734 if (GET_CODE (operand) != MEM)
735 return 0;
737 operand = XEXP (operand, 0);
738 addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
739 return m68hc11_valid_addressing_p (operand, mode, addr_mode);
742 static bool
743 m68hc11_legitimate_address_p_1 (enum machine_mode mode, rtx operand,
744 bool strict)
746 int addr_mode;
748 if (CONSTANT_ADDRESS_P (operand) && TARGET_M6812)
750 /* Reject the global variables if they are too wide. This forces
751 a load of their address in a register and generates smaller code. */
752 if (GET_MODE_SIZE (mode) == 8)
753 return 0;
755 return 1;
757 addr_mode = m68hc11_addr_mode | (strict ? ADDR_STRICT : 0);
758 if (m68hc11_valid_addressing_p (operand, mode, addr_mode))
760 return 1;
762 if (PUSH_POP_ADDRESS_P (operand))
764 return 1;
766 if (symbolic_memory_operand (operand, mode))
768 return 1;
770 return 0;
773 bool
774 m68hc11_legitimate_address_p (enum machine_mode mode, rtx operand,
775 bool strict)
777 int result;
779 if (debug_m6811)
781 printf ("Checking: ");
782 fflush (stdout);
783 debug_rtx (operand);
786 result = m68hc11_legitimate_address_p_1 (mode, operand, strict);
788 if (debug_m6811)
790 printf (" -> %s\n", result == 0 ? "NO" : "YES");
793 if (result == 0)
795 if (debug_m6811)
797 printf ("go_if_legitimate%s, ret 0: %d:",
798 (strict ? "_strict" : ""), mode);
799 fflush (stdout);
800 debug_rtx (operand);
803 return result;
808 m68hc11_reload_operands (rtx operands[])
810 enum machine_mode mode;
812 if (regs_inited == 0)
813 create_regs_rtx ();
815 mode = GET_MODE (operands[1]);
817 /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
818 if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
820 rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
821 rtx base = XEXP (XEXP (operands[1], 0), 0);
823 if (GET_CODE (base) != REG)
825 rtx tmp = base;
826 base = big_offset;
827 big_offset = tmp;
830 /* If the offset is out of range, we have to compute the address
831 with a separate add instruction. We try to do this with an 8-bit
832 add on the A register. This is possible only if the lowest part
833 of the offset (i.e., big_offset % 256) is a valid constant offset
834 with respect to the mode. If it's not, we have to generate a
835 16-bit add on the D register. From:
837 (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
839 we generate:
841 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
842 (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
843 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
844 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
846 (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
847 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
850 if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
852 int vh, vl;
853 rtx reg = operands[0];
854 rtx offset;
855 int val = INTVAL (big_offset);
858 /* We use the 'operands[0]' as a scratch register to compute the
859 address. Make sure 'base' is in that register. */
860 if (!rtx_equal_p (base, operands[0]))
862 emit_move_insn (reg, base);
865 if (val > 0)
867 vh = val >> 8;
868 vl = val & 0x0FF;
870 else
872 vh = (val >> 8) & 0x0FF;
873 vl = val & 0x0FF;
876 /* Create the lowest part offset that still remains to be added.
877 If it's not a valid offset, do a 16-bit add. */
878 offset = GEN_INT (vl);
879 if (!VALID_CONSTANT_OFFSET_P (offset, mode))
881 emit_insn (gen_rtx_SET (VOIDmode, reg,
882 gen_rtx_PLUS (HImode, reg, big_offset)));
883 offset = const0_rtx;
885 else
887 emit_insn (gen_rtx_SET (VOIDmode, reg,
888 gen_rtx_PLUS (HImode, reg,
889 GEN_INT (vh << 8))));
891 emit_move_insn (operands[0],
892 gen_rtx_MEM (GET_MODE (operands[1]),
893 gen_rtx_PLUS (Pmode, reg, offset)));
894 return 1;
898 /* Use the normal gen_movhi pattern. */
899 return 0;
902 void
903 m68hc11_emit_libcall (const char *name, enum rtx_code code,
904 enum machine_mode dmode, enum machine_mode smode,
905 int noperands, rtx *operands)
907 rtx ret;
908 rtx insns;
909 rtx libcall;
910 rtx equiv;
912 start_sequence ();
913 libcall = gen_rtx_SYMBOL_REF (Pmode, name);
914 switch (noperands)
916 case 2:
917 ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
918 dmode, 1, operands[1], smode);
919 equiv = gen_rtx_fmt_e (code, dmode, operands[1]);
920 break;
922 case 3:
923 ret = emit_library_call_value (libcall, NULL_RTX,
924 LCT_CONST, dmode, 2,
925 operands[1], smode, operands[2],
926 smode);
927 equiv = gen_rtx_fmt_ee (code, dmode, operands[1], operands[2]);
928 break;
930 default:
931 gcc_unreachable ();
934 insns = get_insns ();
935 end_sequence ();
936 emit_libcall_block (insns, operands[0], ret, equiv);
939 /* Returns true if X is a PRE/POST increment decrement
940 (same as auto_inc_p() in rtlanal.c but do not take into
941 account the stack). */
943 m68hc11_auto_inc_p (rtx x)
945 return GET_CODE (x) == PRE_DEC
946 || GET_CODE (x) == POST_INC
947 || GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
951 /* Predicates for machine description. */
954 memory_reload_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
956 return GET_CODE (operand) == MEM
957 && GET_CODE (XEXP (operand, 0)) == PLUS
958 && ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
959 && GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
960 || (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
961 && GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
965 m68hc11_symbolic_p (rtx operand, enum machine_mode mode)
967 if (GET_CODE (operand) == MEM)
969 rtx op = XEXP (operand, 0);
971 if (symbolic_memory_operand (op, mode))
972 return 1;
974 return 0;
978 m68hc11_indirect_p (rtx operand, enum machine_mode mode)
980 if (GET_CODE (operand) == MEM && GET_MODE (operand) == mode)
982 rtx op = XEXP (operand, 0);
983 int addr_mode;
985 if (m68hc11_page0_symbol_p (op))
986 return 1;
988 if (symbolic_memory_operand (op, mode))
989 return TARGET_M6812;
991 if (reload_in_progress)
992 return 1;
994 operand = XEXP (operand, 0);
995 addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
996 return m68hc11_valid_addressing_p (operand, mode, addr_mode);
998 return 0;
1002 memory_indexed_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
1004 if (GET_CODE (operand) != MEM)
1005 return 0;
1007 operand = XEXP (operand, 0);
1008 if (GET_CODE (operand) == PLUS)
1010 if (GET_CODE (XEXP (operand, 0)) == REG)
1011 operand = XEXP (operand, 0);
1012 else if (GET_CODE (XEXP (operand, 1)) == REG)
1013 operand = XEXP (operand, 1);
1015 return GET_CODE (operand) == REG
1016 && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
1017 || A_REGNO_P (REGNO (operand)));
1021 push_pop_operand_p (rtx operand)
1023 if (GET_CODE (operand) != MEM)
1025 return 0;
1027 operand = XEXP (operand, 0);
1028 return PUSH_POP_ADDRESS_P (operand);
1031 /* Returns 1 if OP is either a symbol reference or a sum of a symbol
1032 reference and a constant. */
1035 symbolic_memory_operand (rtx op, enum machine_mode mode)
1037 switch (GET_CODE (op))
1039 case SYMBOL_REF:
1040 case LABEL_REF:
1041 return 1;
1043 case CONST:
1044 op = XEXP (op, 0);
1045 return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
1046 || GET_CODE (XEXP (op, 0)) == LABEL_REF)
1047 && GET_CODE (XEXP (op, 1)) == CONST_INT);
1049 /* ??? This clause seems to be irrelevant. */
1050 case CONST_DOUBLE:
1051 return GET_MODE (op) == mode;
1053 case PLUS:
1054 return symbolic_memory_operand (XEXP (op, 0), mode)
1055 && symbolic_memory_operand (XEXP (op, 1), mode);
1057 default:
1058 return 0;
1062 /* Emit the code to build the trampoline used to call a nested function.
1064 68HC11 68HC12
1066 ldy #&CXT movw #&CXT,*_.d1
1067 sty *_.d1 jmp FNADDR
1068 jmp FNADDR
1071 void
1072 m68hc11_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
1074 const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
1076 /* Skip the '*'. */
1077 if (*static_chain_reg == '*')
1078 static_chain_reg++;
1079 if (TARGET_M6811)
1081 emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
1082 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
1083 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
1084 GEN_INT (0x18df));
1085 emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
1086 gen_rtx_CONST (QImode,
1087 gen_rtx_SYMBOL_REF (Pmode,
1088 static_chain_reg)));
1089 emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
1090 GEN_INT (0x7e));
1091 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
1093 else
1095 emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
1096 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
1097 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
1098 gen_rtx_CONST (HImode,
1099 gen_rtx_SYMBOL_REF (Pmode,
1100 static_chain_reg)));
1101 emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
1102 GEN_INT (0x06));
1103 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
1107 /* Declaration of types. */
1109 /* Handle an "tiny_data" attribute; arguments as in
1110 struct attribute_spec.handler. */
1111 static tree
1112 m68hc11_handle_page0_attribute (tree *node, tree name,
1113 tree args ATTRIBUTE_UNUSED,
1114 int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
1116 tree decl = *node;
1118 if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
1120 DECL_SECTION_NAME (decl) = build_string (6, ".page0");
1122 else
1124 warning (OPT_Wattributes, "%qE attribute ignored",
1125 name);
1126 *no_add_attrs = true;
1129 return NULL_TREE;
1132 /* Keep track of the symbol which has a `trap' attribute and which uses
1133 the `swi' calling convention. Since there is only one trap, we only
1134 record one such symbol. If there are several, a warning is reported. */
1135 static rtx trap_handler_symbol = 0;
1137 /* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
1138 arguments as in struct attribute_spec.handler. */
1139 static tree
1140 m68hc11_handle_fntype_attribute (tree *node, tree name,
1141 tree args ATTRIBUTE_UNUSED,
1142 int flags ATTRIBUTE_UNUSED,
1143 bool *no_add_attrs)
1145 if (TREE_CODE (*node) != FUNCTION_TYPE
1146 && TREE_CODE (*node) != METHOD_TYPE
1147 && TREE_CODE (*node) != FIELD_DECL
1148 && TREE_CODE (*node) != TYPE_DECL)
1150 warning (OPT_Wattributes, "%qE attribute only applies to functions",
1151 name);
1152 *no_add_attrs = true;
1155 return NULL_TREE;
1157 /* Undo the effects of the above. */
1159 static const char *
1160 m68hc11_strip_name_encoding (const char *str)
1162 return str + (*str == '*' || *str == '@' || *str == '&');
1165 static void
1166 m68hc11_encode_label (tree decl)
1168 const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
1169 int len = strlen (str);
1170 char *newstr = XALLOCAVEC (char, len + 2);
1172 newstr[0] = '@';
1173 strcpy (&newstr[1], str);
1175 XSTR (XEXP (DECL_RTL (decl), 0), 0) = ggc_alloc_string (newstr, len + 1);
1178 /* Return 1 if this is a symbol in page0 */
1180 m68hc11_page0_symbol_p (rtx x)
1182 switch (GET_CODE (x))
1184 case SYMBOL_REF:
1185 return XSTR (x, 0) != 0 && XSTR (x, 0)[0] == '@';
1187 case CONST:
1188 return m68hc11_page0_symbol_p (XEXP (x, 0));
1190 case PLUS:
1191 if (!m68hc11_page0_symbol_p (XEXP (x, 0)))
1192 return 0;
1194 return GET_CODE (XEXP (x, 1)) == CONST_INT
1195 && INTVAL (XEXP (x, 1)) < 256
1196 && INTVAL (XEXP (x, 1)) >= 0;
1198 default:
1199 return 0;
1203 /* We want to recognize trap handlers so that we handle calls to traps
1204 in a special manner (by issuing the trap). This information is stored
1205 in SYMBOL_REF_FLAG. */
1207 static void
1208 m68hc11_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
1210 tree func_attr;
1211 int trap_handler;
1212 int is_far = 0;
1214 if (TREE_CODE (decl) == VAR_DECL)
1216 if (lookup_attribute ("page0", DECL_ATTRIBUTES (decl)) != 0)
1217 m68hc11_encode_label (decl);
1218 return;
1221 if (TREE_CODE (decl) != FUNCTION_DECL)
1222 return;
1224 func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
1227 if (lookup_attribute ("far", func_attr) != NULL_TREE)
1228 is_far = 1;
1229 else if (lookup_attribute ("near", func_attr) == NULL_TREE)
1230 is_far = TARGET_LONG_CALLS != 0;
1232 trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
1233 if (trap_handler && is_far)
1235 warning (OPT_Wattributes, "%<trap%> and %<far%> attributes are "
1236 "not compatible, ignoring %<far%>");
1237 trap_handler = 0;
1239 if (trap_handler)
1241 if (trap_handler_symbol != 0)
1242 warning (OPT_Wattributes, "%<trap%> attribute is already used");
1243 else
1244 trap_handler_symbol = XEXP (rtl, 0);
1246 SYMBOL_REF_FLAG (XEXP (rtl, 0)) = is_far;
1249 static unsigned int
1250 m68hc11_section_type_flags (tree decl, const char *name, int reloc)
1252 unsigned int flags = default_section_type_flags (decl, name, reloc);
1254 if (strncmp (name, ".eeprom", 7) == 0)
1256 flags |= SECTION_WRITE | SECTION_CODE | SECTION_OVERRIDE;
1259 return flags;
1263 m68hc11_is_far_symbol (rtx sym)
1265 if (GET_CODE (sym) == MEM)
1266 sym = XEXP (sym, 0);
1268 return SYMBOL_REF_FLAG (sym);
1272 m68hc11_is_trap_symbol (rtx sym)
1274 if (GET_CODE (sym) == MEM)
1275 sym = XEXP (sym, 0);
1277 return trap_handler_symbol != 0 && rtx_equal_p (trap_handler_symbol, sym);
1281 /* Argument support functions. */
1283 /* Define the offset between two registers, one to be eliminated, and the
1284 other its replacement, at the start of a routine. */
1286 m68hc11_initial_elimination_offset (int from, int to)
1288 int trap_handler;
1289 tree func_attr;
1290 int size;
1291 int regno;
1293 /* For a trap handler, we must take into account the registers which
1294 are pushed on the stack during the trap (except the PC). */
1295 func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1296 current_function_interrupt = lookup_attribute ("interrupt",
1297 func_attr) != NULL_TREE;
1298 trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
1300 if (lookup_attribute ("far", func_attr) != 0)
1301 current_function_far = 1;
1302 else if (lookup_attribute ("near", func_attr) != 0)
1303 current_function_far = 0;
1304 else
1305 current_function_far = (TARGET_LONG_CALLS != 0
1306 && !current_function_interrupt
1307 && !trap_handler);
1309 if (trap_handler && from == ARG_POINTER_REGNUM)
1310 size = 7;
1312 /* For a function using 'call/rtc' we must take into account the
1313 page register which is pushed in the call. */
1314 else if (current_function_far && from == ARG_POINTER_REGNUM)
1315 size = 1;
1316 else
1317 size = 0;
1319 if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1321 /* 2 is for the saved frame.
1322 1 is for the 'sts' correction when creating the frame. */
1323 return get_frame_size () + 2 + m68hc11_sp_correction + size;
1326 if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1328 return m68hc11_sp_correction;
1331 /* Push any 2 byte pseudo hard registers that we need to save. */
1332 for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
1334 if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
1336 size += 2;
1340 if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
1342 return get_frame_size () + size;
1345 if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
1347 return size;
1349 return 0;
1352 /* Initialize a variable CUM of type CUMULATIVE_ARGS
1353 for a call to a function whose data type is FNTYPE.
1354 For a library call, FNTYPE is 0. */
1356 void
1357 m68hc11_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
1359 tree ret_type;
1361 z_replacement_completed = 0;
1362 cum->words = 0;
1363 cum->nregs = 0;
1365 /* For a library call, we must find out the type of the return value.
1366 When the return value is bigger than 4 bytes, it is returned in
1367 memory. In that case, the first argument of the library call is a
1368 pointer to the memory location. Because the first argument is passed in
1369 register D, we have to identify this, so that the first function
1370 parameter is not passed in D either. */
1371 if (fntype == 0)
1373 const char *name;
1374 size_t len;
1376 if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
1377 return;
1379 /* If the library ends in 'di' or in 'df', we assume it's
1380 returning some DImode or some DFmode which are 64-bit wide. */
1381 name = XSTR (libname, 0);
1382 len = strlen (name);
1383 if (len > 3
1384 && ((name[len - 2] == 'd'
1385 && (name[len - 1] == 'f' || name[len - 1] == 'i'))
1386 || (name[len - 3] == 'd'
1387 && (name[len - 2] == 'i' || name[len - 2] == 'f'))))
1389 /* We are in. Mark the first parameter register as already used. */
1390 cum->words = 1;
1391 cum->nregs = 1;
1393 return;
1396 ret_type = TREE_TYPE (fntype);
1398 if (ret_type && aggregate_value_p (ret_type, fntype))
1400 cum->words = 1;
1401 cum->nregs = 1;
1405 /* Update the data in CUM to advance over an argument
1406 of mode MODE and data type TYPE.
1407 (TYPE is null for libcalls where that information may not be available.) */
1409 void
1410 m68hc11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
1411 tree type, int named ATTRIBUTE_UNUSED)
1413 if (mode != BLKmode)
1415 if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
1417 cum->nregs = 2;
1418 cum->words = GET_MODE_SIZE (mode);
1420 else
1422 cum->words += GET_MODE_SIZE (mode);
1423 if (cum->words <= HARD_REG_SIZE)
1424 cum->nregs = 1;
1427 else
1429 cum->words += int_size_in_bytes (type);
1431 return;
1434 /* Define where to put the arguments to a function.
1435 Value is zero to push the argument on the stack,
1436 or a hard register in which to store the argument.
1438 MODE is the argument's machine mode.
1439 TYPE is the data type of the argument (as a tree).
1440 This is null for libcalls where that information may
1441 not be available.
1442 CUM is a variable of type CUMULATIVE_ARGS which gives info about
1443 the preceding args and about the function being called.
1444 NAMED is nonzero if this argument is a named parameter
1445 (otherwise it is an extra parameter matching an ellipsis). */
1447 struct rtx_def *
1448 m68hc11_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
1449 tree type ATTRIBUTE_UNUSED, int named ATTRIBUTE_UNUSED)
1451 if (cum->words != 0)
1453 return NULL_RTX;
1456 if (mode != BLKmode)
1458 if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
1459 return gen_rtx_REG (mode, HARD_X_REGNUM);
1461 if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
1463 return NULL_RTX;
1465 return gen_rtx_REG (mode, HARD_D_REGNUM);
1467 return NULL_RTX;
1470 /* If defined, a C expression which determines whether, and in which direction,
1471 to pad out an argument with extra space. The value should be of type
1472 `enum direction': either `upward' to pad above the argument,
1473 `downward' to pad below, or `none' to inhibit padding.
1475 Structures are stored left shifted in their argument slot. */
1476 enum direction
1477 m68hc11_function_arg_padding (enum machine_mode mode, const_tree type)
1479 if (type != 0 && AGGREGATE_TYPE_P (type))
1480 return upward;
1482 /* Fall back to the default. */
1483 return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
1487 /* Function prologue and epilogue. */
1489 /* Emit a move after the reload pass has completed. This is used to
1490 emit the prologue and epilogue. */
1491 static void
1492 emit_move_after_reload (rtx to, rtx from, rtx scratch)
1494 rtx insn;
1496 if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
1498 insn = emit_move_insn (to, from);
1500 else
1502 emit_move_insn (scratch, from);
1503 insn = emit_move_insn (to, scratch);
1506 /* Put a REG_INC note to tell the flow analysis that the instruction
1507 is necessary. */
1508 if (IS_STACK_PUSH (to))
1509 add_reg_note (insn, REG_INC, XEXP (XEXP (to, 0), 0));
1510 else if (IS_STACK_POP (from))
1511 add_reg_note (insn, REG_INC, XEXP (XEXP (from, 0), 0));
1513 /* For 68HC11, put a REG_INC note on `sts _.frame' to prevent the cse-reg
1514 to think that sp == _.frame and later replace a x = sp with x = _.frame.
1515 The problem is that we are lying to gcc and use `txs' for x = sp
1516 (which is not really true because txs is really x = sp + 1). */
1517 else if (TARGET_M6811 && SP_REG_P (from))
1518 add_reg_note (insn, REG_INC, from);
1522 m68hc11_total_frame_size (void)
1524 int size;
1525 int regno;
1527 size = get_frame_size ();
1528 if (current_function_interrupt)
1530 size += 3 * HARD_REG_SIZE;
1532 if (frame_pointer_needed)
1533 size += HARD_REG_SIZE;
1535 for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
1536 if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
1537 size += HARD_REG_SIZE;
1539 return size;
1542 static void
1543 m68hc11_output_function_epilogue (FILE *out ATTRIBUTE_UNUSED,
1544 HOST_WIDE_INT size ATTRIBUTE_UNUSED)
1546 /* We catch the function epilogue generation to have a chance
1547 to clear the z_replacement_completed flag. */
1548 z_replacement_completed = 0;
1551 void
1552 expand_prologue (void)
1554 tree func_attr;
1555 int size;
1556 int regno;
1557 rtx scratch;
1559 gcc_assert (reload_completed == 1);
1561 size = get_frame_size ();
1563 create_regs_rtx ();
1565 /* Generate specific prologue for interrupt handlers. */
1566 func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1567 current_function_interrupt = lookup_attribute ("interrupt",
1568 func_attr) != NULL_TREE;
1569 current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
1570 if (lookup_attribute ("far", func_attr) != NULL_TREE)
1571 current_function_far = 1;
1572 else if (lookup_attribute ("near", func_attr) != NULL_TREE)
1573 current_function_far = 0;
1574 else
1575 current_function_far = (TARGET_LONG_CALLS != 0
1576 && !current_function_interrupt
1577 && !current_function_trap);
1579 /* Get the scratch register to build the frame and push registers.
1580 If the first argument is a 32-bit quantity, the D+X registers
1581 are used. Use Y to compute the frame. Otherwise, X is cheaper.
1582 For 68HC12, this scratch register is not used. */
1583 if (crtl->args.info.nregs == 2)
1584 scratch = iy_reg;
1585 else
1586 scratch = ix_reg;
1588 /* Save current stack frame. */
1589 if (frame_pointer_needed)
1590 emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
1592 /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
1593 Other soft registers in page0 need not to be saved because they
1594 will be restored by C functions. For a trap handler, we don't
1595 need to preserve these registers because this is a synchronous call. */
1596 if (current_function_interrupt)
1598 emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
1599 emit_move_after_reload (stack_push_word,
1600 gen_rtx_REG (HImode, SOFT_Z_REGNUM), scratch);
1601 emit_move_after_reload (stack_push_word,
1602 gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
1603 scratch);
1606 /* Allocate local variables. */
1607 if (TARGET_M6812 && (size > 4 || size == 3))
1609 emit_insn (gen_addhi3 (stack_pointer_rtx,
1610 stack_pointer_rtx, GEN_INT (-size)));
1612 else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
1614 rtx insn;
1616 insn = gen_rtx_PARALLEL
1617 (VOIDmode,
1618 gen_rtvec (2,
1619 gen_rtx_SET (VOIDmode,
1620 stack_pointer_rtx,
1621 gen_rtx_PLUS (HImode,
1622 stack_pointer_rtx,
1623 GEN_INT (-size))),
1624 gen_rtx_CLOBBER (VOIDmode, scratch)));
1625 emit_insn (insn);
1627 else
1629 int i;
1631 /* Allocate by pushing scratch values. */
1632 for (i = 2; i <= size; i += 2)
1633 emit_move_after_reload (stack_push_word, ix_reg, 0);
1635 if (size & 1)
1636 emit_insn (gen_addhi3 (stack_pointer_rtx,
1637 stack_pointer_rtx, constm1_rtx));
1640 /* Create the frame pointer. */
1641 if (frame_pointer_needed)
1642 emit_move_after_reload (hard_frame_pointer_rtx,
1643 stack_pointer_rtx, scratch);
1645 /* Push any 2 byte pseudo hard registers that we need to save. */
1646 for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
1648 if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
1650 emit_move_after_reload (stack_push_word,
1651 gen_rtx_REG (HImode, regno), scratch);
1656 void
1657 expand_epilogue (void)
1659 int size;
1660 register int regno;
1661 int return_size;
1662 rtx scratch;
1664 gcc_assert (reload_completed == 1);
1666 size = get_frame_size ();
1668 /* If we are returning a value in two registers, we have to preserve the
1669 X register and use the Y register to restore the stack and the saved
1670 registers. Otherwise, use X because it's faster (and smaller). */
1671 if (crtl->return_rtx == 0)
1672 return_size = 0;
1673 else if (GET_CODE (crtl->return_rtx) == MEM)
1674 return_size = HARD_REG_SIZE;
1675 else
1676 return_size = GET_MODE_SIZE (GET_MODE (crtl->return_rtx));
1678 if (return_size > HARD_REG_SIZE && return_size <= 2 * HARD_REG_SIZE)
1679 scratch = iy_reg;
1680 else
1681 scratch = ix_reg;
1683 /* Pop any 2 byte pseudo hard registers that we saved. */
1684 for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
1686 if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
1688 emit_move_after_reload (gen_rtx_REG (HImode, regno),
1689 stack_pop_word, scratch);
1693 /* de-allocate auto variables */
1694 if (TARGET_M6812 && (size > 4 || size == 3))
1696 emit_insn (gen_addhi3 (stack_pointer_rtx,
1697 stack_pointer_rtx, GEN_INT (size)));
1699 else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
1701 rtx insn;
1703 insn = gen_rtx_PARALLEL
1704 (VOIDmode,
1705 gen_rtvec (2,
1706 gen_rtx_SET (VOIDmode,
1707 stack_pointer_rtx,
1708 gen_rtx_PLUS (HImode,
1709 stack_pointer_rtx,
1710 GEN_INT (size))),
1711 gen_rtx_CLOBBER (VOIDmode, scratch)));
1712 emit_insn (insn);
1714 else
1716 int i;
1718 for (i = 2; i <= size; i += 2)
1719 emit_move_after_reload (scratch, stack_pop_word, scratch);
1720 if (size & 1)
1721 emit_insn (gen_addhi3 (stack_pointer_rtx,
1722 stack_pointer_rtx, const1_rtx));
1725 /* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
1726 if (current_function_interrupt)
1728 emit_move_after_reload (gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
1729 stack_pop_word, scratch);
1730 emit_move_after_reload (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
1731 stack_pop_word, scratch);
1732 emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
1735 /* Restore previous frame pointer. */
1736 if (frame_pointer_needed)
1737 emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
1739 /* If the trap handler returns some value, copy the value
1740 in D, X onto the stack so that the rti will pop the return value
1741 correctly. */
1742 else if (current_function_trap && return_size != 0)
1744 rtx addr_reg = stack_pointer_rtx;
1746 if (!TARGET_M6812)
1748 emit_move_after_reload (scratch, stack_pointer_rtx, 0);
1749 addr_reg = scratch;
1751 emit_move_after_reload (gen_rtx_MEM (HImode,
1752 gen_rtx_PLUS (HImode, addr_reg,
1753 const1_rtx)), d_reg, 0);
1754 if (return_size > HARD_REG_SIZE)
1755 emit_move_after_reload (gen_rtx_MEM (HImode,
1756 gen_rtx_PLUS (HImode, addr_reg,
1757 GEN_INT (3))), ix_reg, 0);
1760 emit_jump_insn (gen_return ());
1764 /* Low and High part extraction for 68HC11. These routines are
1765 similar to gen_lowpart and gen_highpart but they have been
1766 fixed to work for constants and 68HC11 specific registers. */
1769 m68hc11_gen_lowpart (enum machine_mode mode, rtx x)
1771 /* We assume that the low part of an auto-inc mode is the same with
1772 the mode changed and that the caller split the larger mode in the
1773 correct order. */
1774 if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
1776 return gen_rtx_MEM (mode, XEXP (x, 0));
1779 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1780 floating-point constant. A CONST_DOUBLE is used whenever the
1781 constant requires more than one word in order to be adequately
1782 represented. */
1783 if (GET_CODE (x) == CONST_DOUBLE)
1785 long l[2];
1787 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1789 REAL_VALUE_TYPE r;
1791 if (GET_MODE (x) == SFmode)
1793 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1794 REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
1796 else
1798 rtx first, second;
1800 split_double (x, &first, &second);
1801 return second;
1803 if (mode == SImode)
1804 return GEN_INT (l[0]);
1806 return gen_int_mode (l[0], HImode);
1808 else
1810 l[0] = CONST_DOUBLE_LOW (x);
1812 switch (mode)
1814 case SImode:
1815 return GEN_INT (l[0]);
1816 case HImode:
1817 gcc_assert (GET_MODE (x) == SFmode);
1818 return gen_int_mode (l[0], HImode);
1819 default:
1820 gcc_unreachable ();
1824 if (mode == QImode && D_REG_P (x))
1825 return gen_rtx_REG (mode, HARD_B_REGNUM);
1827 /* gen_lowpart crashes when it is called with a SUBREG. */
1828 if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
1830 switch (mode)
1832 case SImode:
1833 return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
1834 case HImode:
1835 return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
1836 default:
1837 gcc_unreachable ();
1840 x = gen_lowpart (mode, x);
1842 /* Return a different rtx to avoid to share it in several insns
1843 (when used by a split pattern). Sharing addresses within
1844 a MEM breaks the Z register replacement (and reloading). */
1845 if (GET_CODE (x) == MEM)
1846 x = copy_rtx (x);
1847 return x;
1851 m68hc11_gen_highpart (enum machine_mode mode, rtx x)
1853 /* We assume that the high part of an auto-inc mode is the same with
1854 the mode changed and that the caller split the larger mode in the
1855 correct order. */
1856 if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
1858 return gen_rtx_MEM (mode, XEXP (x, 0));
1861 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1862 floating-point constant. A CONST_DOUBLE is used whenever the
1863 constant requires more than one word in order to be adequately
1864 represented. */
1865 if (GET_CODE (x) == CONST_DOUBLE)
1867 long l[2];
1869 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1871 REAL_VALUE_TYPE r;
1873 if (GET_MODE (x) == SFmode)
1875 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1876 REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
1878 else
1880 rtx first, second;
1882 split_double (x, &first, &second);
1883 return first;
1885 if (mode == SImode)
1886 return GEN_INT (l[1]);
1888 return gen_int_mode ((l[1] >> 16), HImode);
1890 else
1892 l[1] = CONST_DOUBLE_HIGH (x);
1895 switch (mode)
1897 case SImode:
1898 return GEN_INT (l[1]);
1899 case HImode:
1900 gcc_assert (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT);
1901 return gen_int_mode ((l[0] >> 16), HImode);
1902 default:
1903 gcc_unreachable ();
1906 if (GET_CODE (x) == CONST_INT)
1908 HOST_WIDE_INT val = INTVAL (x);
1910 if (mode == QImode)
1912 return gen_int_mode (val >> 8, QImode);
1914 else if (mode == HImode)
1916 return gen_int_mode (val >> 16, HImode);
1918 else if (mode == SImode)
1920 return gen_int_mode (val >> 32, SImode);
1923 if (mode == QImode && D_REG_P (x))
1924 return gen_rtx_REG (mode, HARD_A_REGNUM);
1926 /* There is no way in GCC to represent the upper part of a word register.
1927 To obtain the 8-bit upper part of a soft register, we change the
1928 reg into a mem rtx. This is possible because they are physically
1929 located in memory. There is no offset because we are big-endian. */
1930 if (mode == QImode && S_REG_P (x))
1932 int pos;
1934 /* Avoid the '*' for direct addressing mode when this
1935 addressing mode is disabled. */
1936 pos = TARGET_NO_DIRECT_MODE ? 1 : 0;
1937 return gen_rtx_MEM (QImode,
1938 gen_rtx_SYMBOL_REF (Pmode,
1939 &reg_names[REGNO (x)][pos]));
1942 /* gen_highpart crashes when it is called with a SUBREG. */
1943 switch (GET_CODE (x))
1945 case SUBREG:
1946 return gen_rtx_SUBREG (mode, XEXP (x, 0), XINT (x, 1));
1947 case REG:
1948 if (REGNO (x) < FIRST_PSEUDO_REGISTER)
1949 return gen_rtx_REG (mode, REGNO (x));
1950 else
1951 return gen_rtx_SUBREG (mode, x, 0);
1952 case MEM:
1953 x = change_address (x, mode, 0);
1955 /* Return a different rtx to avoid to share it in several insns
1956 (when used by a split pattern). Sharing addresses within
1957 a MEM breaks the Z register replacement (and reloading). */
1958 if (GET_CODE (x) == MEM)
1959 x = copy_rtx (x);
1960 return x;
1962 default:
1963 gcc_unreachable ();
1968 /* Obscure register manipulation. */
1970 /* Finds backward in the instructions to see if register 'reg' is
1971 dead. This is used when generating code to see if we can use 'reg'
1972 as a scratch register. This allows us to choose a better generation
1973 of code when we know that some register dies or can be clobbered. */
1976 dead_register_here (rtx x, rtx reg)
1978 rtx x_reg;
1979 rtx p;
1981 if (D_REG_P (reg))
1982 x_reg = gen_rtx_REG (SImode, HARD_X_REGNUM);
1983 else
1984 x_reg = 0;
1986 for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
1987 if (INSN_P (p))
1989 rtx body;
1991 body = PATTERN (p);
1993 if (GET_CODE (body) == CALL_INSN)
1994 break;
1995 if (GET_CODE (body) == JUMP_INSN)
1996 break;
1998 if (GET_CODE (body) == SET)
2000 rtx dst = XEXP (body, 0);
2002 if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
2003 break;
2004 if (x_reg && rtx_equal_p (dst, x_reg))
2005 break;
2007 if (find_regno_note (p, REG_DEAD, REGNO (reg)))
2008 return 1;
2010 else if (reg_mentioned_p (reg, p)
2011 || (x_reg && reg_mentioned_p (x_reg, p)))
2012 break;
2015 /* Scan forward to see if the register is set in some insns and never
2016 used since then. */
2017 for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
2019 rtx body;
2021 if (GET_CODE (p) == CODE_LABEL
2022 || GET_CODE (p) == JUMP_INSN
2023 || GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
2024 break;
2026 if (GET_CODE (p) != INSN)
2027 continue;
2029 body = PATTERN (p);
2030 if (GET_CODE (body) == SET)
2032 rtx src = XEXP (body, 1);
2033 rtx dst = XEXP (body, 0);
2035 if (GET_CODE (dst) == REG
2036 && REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
2037 return 1;
2040 /* Register is used (may be in source or in dest). */
2041 if (reg_mentioned_p (reg, p)
2042 || (x_reg != 0 && GET_MODE (p) == SImode
2043 && reg_mentioned_p (x_reg, p)))
2044 break;
2046 return p == 0 ? 1 : 0;
2050 /* Code generation operations called from machine description file. */
2052 /* Print the name of register 'regno' in the assembly file. */
2053 static void
2054 asm_print_register (FILE *file, int regno)
2056 const char *name = reg_names[regno];
2058 if (TARGET_NO_DIRECT_MODE && name[0] == '*')
2059 name++;
2061 fprintf (file, "%s", name);
2064 /* A C compound statement to output to stdio stream STREAM the
2065 assembler syntax for an instruction operand X. X is an RTL
2066 expression.
2068 CODE is a value that can be used to specify one of several ways
2069 of printing the operand. It is used when identical operands
2070 must be printed differently depending on the context. CODE
2071 comes from the `%' specification that was used to request
2072 printing of the operand. If the specification was just `%DIGIT'
2073 then CODE is 0; if the specification was `%LTR DIGIT' then CODE
2074 is the ASCII code for LTR.
2076 If X is a register, this macro should print the register's name.
2077 The names can be found in an array `reg_names' whose type is
2078 `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
2080 When the machine description has a specification `%PUNCT' (a `%'
2081 followed by a punctuation character), this macro is called with
2082 a null pointer for X and the punctuation character for CODE.
2084 The M68HC11 specific codes are:
2086 'b' for the low part of the operand.
2087 'h' for the high part of the operand
2088 The 'b' or 'h' modifiers have no effect if the operand has
2089 the QImode and is not a S_REG_P (soft register). If the
2090 operand is a hard register, these two modifiers have no effect.
2091 't' generate the temporary scratch register. The operand is
2092 ignored.
2093 'T' generate the low-part temporary scratch register. The operand is
2094 ignored. */
2096 void
2097 print_operand (FILE *file, rtx op, int letter)
2099 if (letter == 't')
2101 asm_print_register (file, SOFT_TMP_REGNUM);
2102 return;
2104 else if (letter == 'T')
2106 asm_print_register (file, SOFT_TMP_REGNUM);
2107 fprintf (file, "+1");
2108 return;
2110 else if (letter == '#')
2112 asm_fprintf (file, "%I");
2115 if (GET_CODE (op) == REG)
2117 if (letter == 'b' && S_REG_P (op))
2119 asm_print_register (file, REGNO (op));
2120 fprintf (file, "+1");
2122 else if (letter == 'b' && D_REG_P (op))
2124 asm_print_register (file, HARD_B_REGNUM);
2126 else
2128 asm_print_register (file, REGNO (op));
2130 return;
2133 if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
2135 if (letter == 'b')
2136 asm_fprintf (file, "%I%%lo(");
2137 else
2138 asm_fprintf (file, "%I%%hi(");
2140 output_addr_const (file, op);
2141 fprintf (file, ")");
2142 return;
2145 /* Get the low or high part of the operand when 'b' or 'h' modifiers
2146 are specified. If we already have a QImode, there is nothing to do. */
2147 if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
2149 if (letter == 'b')
2151 op = m68hc11_gen_lowpart (QImode, op);
2153 else if (letter == 'h')
2155 op = m68hc11_gen_highpart (QImode, op);
2159 if (GET_CODE (op) == MEM)
2161 rtx base = XEXP (op, 0);
2162 switch (GET_CODE (base))
2164 case PRE_DEC:
2165 gcc_assert (TARGET_M6812);
2166 fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
2167 asm_print_register (file, REGNO (XEXP (base, 0)));
2168 break;
2170 case POST_DEC:
2171 gcc_assert (TARGET_M6812);
2172 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
2173 asm_print_register (file, REGNO (XEXP (base, 0)));
2174 fprintf (file, "-");
2175 break;
2177 case POST_INC:
2178 gcc_assert (TARGET_M6812);
2179 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
2180 asm_print_register (file, REGNO (XEXP (base, 0)));
2181 fprintf (file, "+");
2182 break;
2184 case PRE_INC:
2185 gcc_assert (TARGET_M6812);
2186 fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
2187 asm_print_register (file, REGNO (XEXP (base, 0)));
2188 break;
2190 case MEM:
2191 gcc_assert (TARGET_M6812);
2192 fprintf (file, "[");
2193 print_operand_address (file, XEXP (base, 0));
2194 fprintf (file, "]");
2195 break;
2197 default:
2198 if (m68hc11_page0_symbol_p (base))
2199 fprintf (file, "*");
2201 output_address (base);
2202 break;
2205 else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
2207 REAL_VALUE_TYPE r;
2208 long l;
2210 REAL_VALUE_FROM_CONST_DOUBLE (r, op);
2211 REAL_VALUE_TO_TARGET_SINGLE (r, l);
2212 asm_fprintf (file, "%I0x%lx", l);
2214 else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
2216 char dstr[30];
2218 real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (op),
2219 sizeof (dstr), 0, 1);
2220 asm_fprintf (file, "%I0r%s", dstr);
2222 else
2224 int need_parenthesize = 0;
2226 if (letter != 'i')
2227 asm_fprintf (file, "%I");
2228 else
2229 need_parenthesize = must_parenthesize (op);
2231 if (need_parenthesize)
2232 fprintf (file, "(");
2234 output_addr_const (file, op);
2235 if (need_parenthesize)
2236 fprintf (file, ")");
2240 /* Returns true if the operand 'op' must be printed with parenthesis
2241 around it. This must be done only if there is a symbol whose name
2242 is a processor register. */
2243 static int
2244 must_parenthesize (rtx op)
2246 const char *name;
2248 switch (GET_CODE (op))
2250 case SYMBOL_REF:
2251 name = XSTR (op, 0);
2252 /* Avoid a conflict between symbol name and a possible
2253 register. */
2254 return (strcasecmp (name, "a") == 0
2255 || strcasecmp (name, "b") == 0
2256 || strcasecmp (name, "d") == 0
2257 || strcasecmp (name, "x") == 0
2258 || strcasecmp (name, "y") == 0
2259 || strcasecmp (name, "ix") == 0
2260 || strcasecmp (name, "iy") == 0
2261 || strcasecmp (name, "pc") == 0
2262 || strcasecmp (name, "sp") == 0
2263 || strcasecmp (name, "ccr") == 0) ? 1 : 0;
2265 case PLUS:
2266 case MINUS:
2267 return must_parenthesize (XEXP (op, 0))
2268 || must_parenthesize (XEXP (op, 1));
2270 case MEM:
2271 case CONST:
2272 case ZERO_EXTEND:
2273 case SIGN_EXTEND:
2274 return must_parenthesize (XEXP (op, 0));
2276 case CONST_DOUBLE:
2277 case CONST_INT:
2278 case LABEL_REF:
2279 case CODE_LABEL:
2280 default:
2281 return 0;
2285 /* A C compound statement to output to stdio stream STREAM the
2286 assembler syntax for an instruction operand that is a memory
2287 reference whose address is ADDR. ADDR is an RTL expression. */
2289 void
2290 print_operand_address (FILE *file, rtx addr)
2292 rtx base;
2293 rtx offset;
2294 int need_parenthesis = 0;
2296 switch (GET_CODE (addr))
2298 case REG:
2299 gcc_assert (REG_P (addr) && REG_OK_FOR_BASE_STRICT_P (addr));
2301 fprintf (file, "0,");
2302 asm_print_register (file, REGNO (addr));
2303 break;
2305 case MEM:
2306 base = XEXP (addr, 0);
2307 switch (GET_CODE (base))
2309 case PRE_DEC:
2310 gcc_assert (TARGET_M6812);
2311 fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
2312 asm_print_register (file, REGNO (XEXP (base, 0)));
2313 break;
2315 case POST_DEC:
2316 gcc_assert (TARGET_M6812);
2317 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
2318 asm_print_register (file, REGNO (XEXP (base, 0)));
2319 fprintf (file, "-");
2320 break;
2322 case POST_INC:
2323 gcc_assert (TARGET_M6812);
2324 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
2325 asm_print_register (file, REGNO (XEXP (base, 0)));
2326 fprintf (file, "+");
2327 break;
2329 case PRE_INC:
2330 gcc_assert (TARGET_M6812);
2331 fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
2332 asm_print_register (file, REGNO (XEXP (base, 0)));
2333 break;
2335 default:
2336 need_parenthesis = must_parenthesize (base);
2337 if (need_parenthesis)
2338 fprintf (file, "(");
2340 output_addr_const (file, base);
2341 if (need_parenthesis)
2342 fprintf (file, ")");
2343 break;
2345 break;
2347 case PLUS:
2348 base = XEXP (addr, 0);
2349 offset = XEXP (addr, 1);
2350 if (!G_REG_P (base) && G_REG_P (offset))
2352 base = XEXP (addr, 1);
2353 offset = XEXP (addr, 0);
2355 if (CONSTANT_ADDRESS_P (base))
2357 need_parenthesis = must_parenthesize (addr);
2359 gcc_assert (CONSTANT_ADDRESS_P (offset));
2360 if (need_parenthesis)
2361 fprintf (file, "(");
2363 output_addr_const (file, base);
2364 fprintf (file, "+");
2365 output_addr_const (file, offset);
2366 if (need_parenthesis)
2367 fprintf (file, ")");
2369 else
2371 gcc_assert (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base));
2372 if (REG_P (offset))
2374 gcc_assert (TARGET_M6812);
2375 asm_print_register (file, REGNO (offset));
2376 fprintf (file, ",");
2377 asm_print_register (file, REGNO (base));
2379 else
2381 need_parenthesis = must_parenthesize (offset);
2382 if (need_parenthesis)
2383 fprintf (file, "(");
2385 output_addr_const (file, offset);
2386 if (need_parenthesis)
2387 fprintf (file, ")");
2388 fprintf (file, ",");
2389 asm_print_register (file, REGNO (base));
2392 break;
2394 default:
2395 if (GET_CODE (addr) == CONST_INT
2396 && INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
2398 fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
2400 else
2402 need_parenthesis = must_parenthesize (addr);
2403 if (need_parenthesis)
2404 fprintf (file, "(");
2406 output_addr_const (file, addr);
2407 if (need_parenthesis)
2408 fprintf (file, ")");
2410 break;
2415 /* Splitting of some instructions. */
2417 static rtx
2418 m68hc11_expand_compare (enum rtx_code code, rtx op0, rtx op1)
2420 rtx ret = 0;
2422 gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) != MODE_FLOAT);
2423 emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
2424 gen_rtx_COMPARE (VOIDmode, op0, op1)));
2425 ret = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
2427 return ret;
2431 m68hc11_expand_compare_and_branch (enum rtx_code code, rtx op0, rtx op1,
2432 rtx label)
2434 rtx tmp;
2436 switch (GET_MODE (op0))
2438 case QImode:
2439 case HImode:
2440 tmp = m68hc11_expand_compare (code, op0, op1);
2441 tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
2442 gen_rtx_LABEL_REF (VOIDmode, label),
2443 pc_rtx);
2444 emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
2445 return 0;
2446 #if 0
2448 /* SCz: from i386.c */
2449 case SFmode:
2450 case DFmode:
2451 /* Don't expand the comparison early, so that we get better code
2452 when jump or whoever decides to reverse the comparison. */
2454 rtvec vec;
2455 int use_fcomi;
2457 code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
2458 &m68hc11_compare_op1);
2460 tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
2461 m68hc11_compare_op0, m68hc11_compare_op1);
2462 tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
2463 gen_rtx_LABEL_REF (VOIDmode, label),
2464 pc_rtx);
2465 tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
2467 use_fcomi = ix86_use_fcomi_compare (code);
2468 vec = rtvec_alloc (3 + !use_fcomi);
2469 RTVEC_ELT (vec, 0) = tmp;
2470 RTVEC_ELT (vec, 1)
2471 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
2472 RTVEC_ELT (vec, 2)
2473 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
2474 if (!use_fcomi)
2475 RTVEC_ELT (vec, 3)
2476 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
2478 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
2479 return;
2481 #endif
2483 case SImode:
2484 /* Expand SImode branch into multiple compare+branch. */
2486 rtx lo[2], hi[2], label2;
2487 enum rtx_code code1, code2, code3;
2489 if (CONSTANT_P (op0) && !CONSTANT_P (op1))
2491 tmp = op0;
2492 op0 = op1;
2493 op1 = tmp;
2494 code = swap_condition (code);
2496 lo[0] = m68hc11_gen_lowpart (HImode, op0);
2497 lo[1] = m68hc11_gen_lowpart (HImode, op1);
2498 hi[0] = m68hc11_gen_highpart (HImode, op0);
2499 hi[1] = m68hc11_gen_highpart (HImode, op1);
2501 /* Otherwise, if we are doing less-than, op1 is a constant and the
2502 low word is zero, then we can just examine the high word. */
2504 if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
2505 && (code == LT || code == LTU))
2507 return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
2508 label);
2511 /* Otherwise, we need two or three jumps. */
2513 label2 = gen_label_rtx ();
2515 code1 = code;
2516 code2 = swap_condition (code);
2517 code3 = unsigned_condition (code);
2519 switch (code)
2521 case LT:
2522 case GT:
2523 case LTU:
2524 case GTU:
2525 break;
2527 case LE:
2528 code1 = LT;
2529 code2 = GT;
2530 break;
2531 case GE:
2532 code1 = GT;
2533 code2 = LT;
2534 break;
2535 case LEU:
2536 code1 = LTU;
2537 code2 = GTU;
2538 break;
2539 case GEU:
2540 code1 = GTU;
2541 code2 = LTU;
2542 break;
2544 case EQ:
2545 code1 = UNKNOWN;
2546 code2 = NE;
2547 break;
2548 case NE:
2549 code2 = UNKNOWN;
2550 break;
2552 default:
2553 gcc_unreachable ();
2557 * a < b =>
2558 * if (hi(a) < hi(b)) goto true;
2559 * if (hi(a) > hi(b)) goto false;
2560 * if (lo(a) < lo(b)) goto true;
2561 * false:
2563 if (code1 != UNKNOWN)
2564 m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
2565 if (code2 != UNKNOWN)
2566 m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
2568 m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
2570 if (code2 != UNKNOWN)
2571 emit_label (label2);
2572 return 0;
2575 default:
2576 gcc_unreachable ();
2578 return 0;
2581 /* Return the increment/decrement mode of a MEM if it is such.
2582 Return CONST if it is anything else. */
2583 static int
2584 autoinc_mode (rtx x)
2586 if (GET_CODE (x) != MEM)
2587 return CONST;
2589 x = XEXP (x, 0);
2590 if (GET_CODE (x) == PRE_INC
2591 || GET_CODE (x) == PRE_DEC
2592 || GET_CODE (x) == POST_INC
2593 || GET_CODE (x) == POST_DEC)
2594 return GET_CODE (x);
2596 return CONST;
2599 static int
2600 m68hc11_make_autoinc_notes (rtx *x, void *data)
2602 rtx insn;
2604 switch (GET_CODE (*x))
2606 case PRE_DEC:
2607 case PRE_INC:
2608 case POST_DEC:
2609 case POST_INC:
2610 insn = (rtx) data;
2611 REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, XEXP (*x, 0),
2612 REG_NOTES (insn));
2613 return -1;
2615 default:
2616 return 0;
2620 /* Split a DI, SI or HI move into several smaller move operations.
2621 The scratch register 'scratch' is used as a temporary to load
2622 store intermediate values. It must be a hard register. */
2623 void
2624 m68hc11_split_move (rtx to, rtx from, rtx scratch)
2626 rtx low_to, low_from;
2627 rtx high_to, high_from;
2628 rtx insn;
2629 enum machine_mode mode;
2630 int offset = 0;
2631 int autoinc_from = autoinc_mode (from);
2632 int autoinc_to = autoinc_mode (to);
2634 mode = GET_MODE (to);
2636 /* If the TO and FROM contain autoinc modes that are not compatible
2637 together (one pop and the other a push), we must change one to
2638 an offsetable operand and generate an appropriate add at the end. */
2639 if (TARGET_M6812 && GET_MODE_SIZE (mode) > 2)
2641 rtx reg;
2642 int code;
2644 /* The source uses an autoinc mode which is not compatible with
2645 a split (this would result in a word swap). */
2646 if (autoinc_from == PRE_INC || autoinc_from == POST_DEC)
2648 code = GET_CODE (XEXP (from, 0));
2649 reg = XEXP (XEXP (from, 0), 0);
2650 offset = GET_MODE_SIZE (GET_MODE (from));
2651 if (code == POST_DEC)
2652 offset = -offset;
2654 if (code == PRE_INC)
2655 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2657 m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
2658 if (code == POST_DEC)
2659 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2660 return;
2663 /* Likewise for destination. */
2664 if (autoinc_to == PRE_INC || autoinc_to == POST_DEC)
2666 code = GET_CODE (XEXP (to, 0));
2667 reg = XEXP (XEXP (to, 0), 0);
2668 offset = GET_MODE_SIZE (GET_MODE (to));
2669 if (code == POST_DEC)
2670 offset = -offset;
2672 if (code == PRE_INC)
2673 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2675 m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
2676 if (code == POST_DEC)
2677 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2678 return;
2681 /* The source and destination auto increment modes must be compatible
2682 with each other: same direction. */
2683 if ((autoinc_to != autoinc_from
2684 && autoinc_to != CONST && autoinc_from != CONST)
2685 /* The destination address register must not be used within
2686 the source operand because the source address would change
2687 while doing the copy. */
2688 || (autoinc_to != CONST
2689 && reg_mentioned_p (XEXP (XEXP (to, 0), 0), from)
2690 && !IS_STACK_PUSH (to)))
2692 /* Must change the destination. */
2693 code = GET_CODE (XEXP (to, 0));
2694 reg = XEXP (XEXP (to, 0), 0);
2695 offset = GET_MODE_SIZE (GET_MODE (to));
2696 if (code == PRE_DEC || code == POST_DEC)
2697 offset = -offset;
2699 if (code == PRE_DEC || code == PRE_INC)
2700 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2701 m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
2702 if (code == POST_DEC || code == POST_INC)
2703 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2705 return;
2708 /* Likewise, the source address register must not be used within
2709 the destination operand. */
2710 if (autoinc_from != CONST
2711 && reg_mentioned_p (XEXP (XEXP (from, 0), 0), to)
2712 && !IS_STACK_PUSH (to))
2714 /* Must change the source. */
2715 code = GET_CODE (XEXP (from, 0));
2716 reg = XEXP (XEXP (from, 0), 0);
2717 offset = GET_MODE_SIZE (GET_MODE (from));
2718 if (code == PRE_DEC || code == POST_DEC)
2719 offset = -offset;
2721 if (code == PRE_DEC || code == PRE_INC)
2722 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2723 m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
2724 if (code == POST_DEC || code == POST_INC)
2725 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2727 return;
2731 if (GET_MODE_SIZE (mode) == 8)
2732 mode = SImode;
2733 else if (GET_MODE_SIZE (mode) == 4)
2734 mode = HImode;
2735 else
2736 mode = QImode;
2738 if (TARGET_M6812
2739 && IS_STACK_PUSH (to)
2740 && reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM), from))
2742 if (mode == SImode)
2744 offset = 4;
2746 else if (mode == HImode)
2748 offset = 2;
2750 else
2751 offset = 0;
2754 low_to = m68hc11_gen_lowpart (mode, to);
2755 high_to = m68hc11_gen_highpart (mode, to);
2757 low_from = m68hc11_gen_lowpart (mode, from);
2758 high_from = m68hc11_gen_highpart (mode, from);
2760 if (offset)
2762 high_from = adjust_address (high_from, mode, offset);
2763 low_from = high_from;
2766 /* When copying with a POST_INC mode, we must copy the
2767 high part and then the low part to guarantee a correct
2768 32/64-bit copy. */
2769 if (TARGET_M6812
2770 && GET_MODE_SIZE (mode) >= 2
2771 && autoinc_from != autoinc_to
2772 && (autoinc_from == POST_INC || autoinc_to == POST_INC))
2774 rtx swap;
2776 swap = low_to;
2777 low_to = high_to;
2778 high_to = swap;
2780 swap = low_from;
2781 low_from = high_from;
2782 high_from = swap;
2784 if (mode == SImode)
2786 m68hc11_split_move (low_to, low_from, scratch);
2787 m68hc11_split_move (high_to, high_from, scratch);
2789 else if (H_REG_P (to) || H_REG_P (from)
2790 || (low_from == const0_rtx
2791 && high_from == const0_rtx
2792 && ! push_operand (to, GET_MODE (to))
2793 && ! H_REG_P (scratch))
2794 || (TARGET_M6812
2795 && (!m68hc11_register_indirect_p (from, GET_MODE (from))
2796 || m68hc11_small_indexed_indirect_p (from,
2797 GET_MODE (from)))
2798 && (!m68hc11_register_indirect_p (to, GET_MODE (to))
2799 || m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
2801 insn = emit_move_insn (low_to, low_from);
2802 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2804 insn = emit_move_insn (high_to, high_from);
2805 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2807 else
2809 insn = emit_move_insn (scratch, low_from);
2810 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2811 insn = emit_move_insn (low_to, scratch);
2812 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2814 insn = emit_move_insn (scratch, high_from);
2815 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2816 insn = emit_move_insn (high_to, scratch);
2817 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2821 static rtx
2822 simplify_logical (enum machine_mode mode, int code, rtx operand, rtx *result)
2824 int val;
2825 int mask;
2827 *result = 0;
2828 if (GET_CODE (operand) != CONST_INT)
2829 return operand;
2831 if (mode == HImode)
2832 mask = 0x0ffff;
2833 else
2834 mask = 0x0ff;
2836 val = INTVAL (operand);
2837 switch (code)
2839 case IOR:
2840 if ((val & mask) == 0)
2841 return 0;
2842 if ((val & mask) == mask)
2843 *result = constm1_rtx;
2844 break;
2846 case AND:
2847 if ((val & mask) == 0)
2848 *result = const0_rtx;
2849 if ((val & mask) == mask)
2850 return 0;
2851 break;
2853 case XOR:
2854 if ((val & mask) == 0)
2855 return 0;
2856 break;
2858 return operand;
2861 static void
2862 m68hc11_emit_logical (enum machine_mode mode, enum rtx_code code, rtx *operands)
2864 rtx result;
2865 int need_copy;
2867 need_copy = (rtx_equal_p (operands[0], operands[1])
2868 || rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
2870 operands[1] = simplify_logical (mode, code, operands[1], &result);
2871 operands[2] = simplify_logical (mode, code, operands[2], &result);
2873 if (result && GET_CODE (result) == CONST_INT)
2875 if (!H_REG_P (operands[0]) && operands[3]
2876 && (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
2878 emit_move_insn (operands[3], result);
2879 emit_move_insn (operands[0], operands[3]);
2881 else
2883 emit_move_insn (operands[0], result);
2886 else if (operands[1] != 0 && operands[2] != 0)
2888 rtx insn;
2890 if (!H_REG_P (operands[0]) && operands[3])
2892 emit_move_insn (operands[3], operands[1]);
2893 emit_insn (gen_rtx_SET (mode,
2894 operands[3],
2895 gen_rtx_fmt_ee (code, mode,
2896 operands[3], operands[2])));
2897 insn = emit_move_insn (operands[0], operands[3]);
2899 else
2901 insn = emit_insn (gen_rtx_SET (mode,
2902 operands[0],
2903 gen_rtx_fmt_ee (code, mode,
2904 operands[0],
2905 operands[2])));
2909 /* The logical operation is similar to a copy. */
2910 else if (need_copy)
2912 rtx src;
2914 if (GET_CODE (operands[1]) == CONST_INT)
2915 src = operands[2];
2916 else
2917 src = operands[1];
2919 if (!H_REG_P (operands[0]) && !H_REG_P (src))
2921 emit_move_insn (operands[3], src);
2922 emit_move_insn (operands[0], operands[3]);
2924 else
2926 emit_move_insn (operands[0], src);
2931 void
2932 m68hc11_split_logical (enum machine_mode mode, enum rtx_code code,
2933 rtx *operands)
2935 rtx low[4];
2936 rtx high[4];
2938 low[0] = m68hc11_gen_lowpart (mode, operands[0]);
2939 low[1] = m68hc11_gen_lowpart (mode, operands[1]);
2940 low[2] = m68hc11_gen_lowpart (mode, operands[2]);
2942 high[0] = m68hc11_gen_highpart (mode, operands[0]);
2943 high[1] = m68hc11_gen_highpart (mode, operands[1]);
2944 high[2] = m68hc11_gen_highpart (mode, operands[2]);
2946 low[3] = operands[3];
2947 high[3] = operands[3];
2948 if (mode == SImode)
2950 m68hc11_split_logical (HImode, code, low);
2951 m68hc11_split_logical (HImode, code, high);
2952 return;
2955 m68hc11_emit_logical (mode, code, low);
2956 m68hc11_emit_logical (mode, code, high);
2960 /* Code generation. */
2962 void
2963 m68hc11_output_swap (rtx insn ATTRIBUTE_UNUSED, rtx operands[])
2965 /* We have to be careful with the cc_status. An address register swap
2966 is generated for some comparison. The comparison is made with D
2967 but the branch really uses the address register. See the split
2968 pattern for compare. The xgdx/xgdy preserve the flags but after
2969 the exchange, the flags will reflect to the value of X and not D.
2970 Tell this by setting the cc_status according to the cc_prev_status. */
2971 if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
2973 if (cc_prev_status.value1 != 0
2974 && (D_REG_P (cc_prev_status.value1)
2975 || X_REG_P (cc_prev_status.value1)))
2977 cc_status = cc_prev_status;
2978 if (D_REG_P (cc_status.value1))
2979 cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
2980 HARD_X_REGNUM);
2981 else
2982 cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
2983 HARD_D_REGNUM);
2985 else
2986 CC_STATUS_INIT;
2988 output_asm_insn ("xgdx", operands);
2990 else
2992 if (cc_prev_status.value1 != 0
2993 && (D_REG_P (cc_prev_status.value1)
2994 || Y_REG_P (cc_prev_status.value1)))
2996 cc_status = cc_prev_status;
2997 if (D_REG_P (cc_status.value1))
2998 cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
2999 HARD_Y_REGNUM);
3000 else
3001 cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
3002 HARD_D_REGNUM);
3004 else
3005 CC_STATUS_INIT;
3007 output_asm_insn ("xgdy", operands);
3011 /* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
3012 This is used to decide whether a move that set flags should be used
3013 instead. */
3015 next_insn_test_reg (rtx insn, rtx reg)
3017 rtx body;
3019 insn = next_nonnote_insn (insn);
3020 if (GET_CODE (insn) != INSN)
3021 return 0;
3023 body = PATTERN (insn);
3024 if (sets_cc0_p (body) != 1)
3025 return 0;
3027 if (rtx_equal_p (XEXP (body, 1), reg) == 0)
3028 return 0;
3030 return 1;
3033 /* Generate the code to move a 16-bit operand into another one. */
3035 void
3036 m68hc11_gen_movhi (rtx insn, rtx *operands)
3038 int reg;
3040 /* Move a register or memory to the same location.
3041 This is possible because such insn can appear
3042 in a non-optimizing mode. */
3043 if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
3045 cc_status = cc_prev_status;
3046 return;
3049 if (TARGET_M6812)
3051 rtx from = operands[1];
3052 rtx to = operands[0];
3054 if (IS_STACK_PUSH (to) && H_REG_P (from))
3056 cc_status = cc_prev_status;
3057 switch (REGNO (from))
3059 case HARD_X_REGNUM:
3060 case HARD_Y_REGNUM:
3061 case HARD_D_REGNUM:
3062 output_asm_insn ("psh%1", operands);
3063 break;
3064 case HARD_SP_REGNUM:
3065 output_asm_insn ("sts\t2,-sp", operands);
3066 break;
3067 default:
3068 gcc_unreachable ();
3070 return;
3072 if (IS_STACK_POP (from) && H_REG_P (to))
3074 cc_status = cc_prev_status;
3075 switch (REGNO (to))
3077 case HARD_X_REGNUM:
3078 case HARD_Y_REGNUM:
3079 case HARD_D_REGNUM:
3080 output_asm_insn ("pul%0", operands);
3081 break;
3082 default:
3083 gcc_unreachable ();
3085 return;
3087 if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
3089 m68hc11_notice_keep_cc (operands[0]);
3090 output_asm_insn ("tfr\t%1,%0", operands);
3092 else if (H_REG_P (operands[0]))
3094 if (SP_REG_P (operands[0]))
3095 output_asm_insn ("lds\t%1", operands);
3096 else
3097 output_asm_insn ("ld%0\t%1", operands);
3099 else if (H_REG_P (operands[1]))
3101 if (SP_REG_P (operands[1]))
3102 output_asm_insn ("sts\t%0", operands);
3103 else
3104 output_asm_insn ("st%1\t%0", operands);
3107 /* The 68hc12 does not support (MEM:HI (MEM:HI)) with the movw
3108 instruction. We have to use a scratch register as temporary location.
3109 Trying to use a specific pattern or constrain failed. */
3110 else if (GET_CODE (to) == MEM && GET_CODE (XEXP (to, 0)) == MEM)
3112 rtx ops[4];
3114 ops[0] = to;
3115 ops[2] = from;
3116 ops[3] = 0;
3117 if (dead_register_here (insn, d_reg))
3118 ops[1] = d_reg;
3119 else if (dead_register_here (insn, ix_reg))
3120 ops[1] = ix_reg;
3121 else if (dead_register_here (insn, iy_reg))
3122 ops[1] = iy_reg;
3123 else
3125 ops[1] = d_reg;
3126 ops[3] = d_reg;
3127 output_asm_insn ("psh%3", ops);
3130 ops[0] = to;
3131 ops[2] = from;
3132 output_asm_insn ("ld%1\t%2", ops);
3133 output_asm_insn ("st%1\t%0", ops);
3134 if (ops[3])
3135 output_asm_insn ("pul%3", ops);
3138 /* Use movw for non-null constants or when we are clearing
3139 a volatile memory reference. However, this is possible
3140 only if the memory reference has a small offset or is an
3141 absolute address. */
3142 else if (GET_CODE (from) == CONST_INT
3143 && INTVAL (from) == 0
3144 && (MEM_VOLATILE_P (to) == 0
3145 || m68hc11_small_indexed_indirect_p (to, HImode) == 0))
3147 output_asm_insn ("clr\t%h0", operands);
3148 output_asm_insn ("clr\t%b0", operands);
3150 else
3152 if ((m68hc11_register_indirect_p (from, GET_MODE (from))
3153 && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
3154 || (m68hc11_register_indirect_p (to, GET_MODE (to))
3155 && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
3157 rtx ops[3];
3159 if (operands[2])
3161 ops[0] = operands[2];
3162 ops[1] = from;
3163 ops[2] = 0;
3164 m68hc11_gen_movhi (insn, ops);
3165 ops[0] = to;
3166 ops[1] = operands[2];
3167 m68hc11_gen_movhi (insn, ops);
3168 return;
3170 else
3172 /* !!!! SCz wrong here. */
3173 fatal_insn ("move insn not handled", insn);
3176 else
3178 m68hc11_notice_keep_cc (operands[0]);
3179 output_asm_insn ("movw\t%1,%0", operands);
3182 return;
3185 if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
3187 cc_status = cc_prev_status;
3188 switch (REGNO (operands[0]))
3190 case HARD_X_REGNUM:
3191 case HARD_Y_REGNUM:
3192 output_asm_insn ("pul%0", operands);
3193 break;
3194 case HARD_D_REGNUM:
3195 output_asm_insn ("pula", operands);
3196 output_asm_insn ("pulb", operands);
3197 break;
3198 default:
3199 gcc_unreachable ();
3201 return;
3203 /* Some moves to a hard register are special. Not all of them
3204 are really supported and we have to use a temporary
3205 location to provide them (either the stack of a temp var). */
3206 if (H_REG_P (operands[0]))
3208 switch (REGNO (operands[0]))
3210 case HARD_D_REGNUM:
3211 if (X_REG_P (operands[1]))
3213 if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
3215 m68hc11_output_swap (insn, operands);
3217 else if (next_insn_test_reg (insn, operands[0]))
3219 output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
3221 else
3223 m68hc11_notice_keep_cc (operands[0]);
3224 output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
3227 else if (Y_REG_P (operands[1]))
3229 if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
3231 m68hc11_output_swap (insn, operands);
3233 else
3235 /* %t means *ZTMP scratch register. */
3236 output_asm_insn ("sty\t%t1", operands);
3237 output_asm_insn ("ldd\t%t1", operands);
3240 else if (SP_REG_P (operands[1]))
3242 CC_STATUS_INIT;
3243 if (ix_reg == 0)
3244 create_regs_rtx ();
3245 if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
3246 output_asm_insn ("xgdx", operands);
3247 output_asm_insn ("tsx", operands);
3248 output_asm_insn ("xgdx", operands);
3250 else if (IS_STACK_POP (operands[1]))
3252 output_asm_insn ("pula\n\tpulb", operands);
3254 else if (GET_CODE (operands[1]) == CONST_INT
3255 && INTVAL (operands[1]) == 0)
3257 output_asm_insn ("clra\n\tclrb", operands);
3259 else
3261 output_asm_insn ("ldd\t%1", operands);
3263 break;
3265 case HARD_X_REGNUM:
3266 if (D_REG_P (operands[1]))
3268 if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3270 m68hc11_output_swap (insn, operands);
3272 else if (next_insn_test_reg (insn, operands[0]))
3274 output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
3276 else
3278 m68hc11_notice_keep_cc (operands[0]);
3279 output_asm_insn ("pshb", operands);
3280 output_asm_insn ("psha", operands);
3281 output_asm_insn ("pulx", operands);
3284 else if (Y_REG_P (operands[1]))
3286 /* When both D and Y are dead, use the sequence xgdy, xgdx
3287 to move Y into X. The D and Y registers are modified. */
3288 if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
3289 && dead_register_here (insn, d_reg))
3291 output_asm_insn ("xgdy", operands);
3292 output_asm_insn ("xgdx", operands);
3293 CC_STATUS_INIT;
3295 else if (!optimize_size)
3297 output_asm_insn ("sty\t%t1", operands);
3298 output_asm_insn ("ldx\t%t1", operands);
3300 else
3302 CC_STATUS_INIT;
3303 output_asm_insn ("pshy", operands);
3304 output_asm_insn ("pulx", operands);
3307 else if (SP_REG_P (operands[1]))
3309 /* tsx, tsy preserve the flags */
3310 cc_status = cc_prev_status;
3311 output_asm_insn ("tsx", operands);
3313 else
3315 output_asm_insn ("ldx\t%1", operands);
3317 break;
3319 case HARD_Y_REGNUM:
3320 if (D_REG_P (operands[1]))
3322 if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3324 m68hc11_output_swap (insn, operands);
3326 else
3328 output_asm_insn ("std\t%t1", operands);
3329 output_asm_insn ("ldy\t%t1", operands);
3332 else if (X_REG_P (operands[1]))
3334 /* When both D and X are dead, use the sequence xgdx, xgdy
3335 to move X into Y. The D and X registers are modified. */
3336 if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
3337 && dead_register_here (insn, d_reg))
3339 output_asm_insn ("xgdx", operands);
3340 output_asm_insn ("xgdy", operands);
3341 CC_STATUS_INIT;
3343 else if (!optimize_size)
3345 output_asm_insn ("stx\t%t1", operands);
3346 output_asm_insn ("ldy\t%t1", operands);
3348 else
3350 CC_STATUS_INIT;
3351 output_asm_insn ("pshx", operands);
3352 output_asm_insn ("puly", operands);
3355 else if (SP_REG_P (operands[1]))
3357 /* tsx, tsy preserve the flags */
3358 cc_status = cc_prev_status;
3359 output_asm_insn ("tsy", operands);
3361 else
3363 output_asm_insn ("ldy\t%1", operands);
3365 break;
3367 case HARD_SP_REGNUM:
3368 if (D_REG_P (operands[1]))
3370 m68hc11_notice_keep_cc (operands[0]);
3371 output_asm_insn ("xgdx", operands);
3372 output_asm_insn ("txs", operands);
3373 output_asm_insn ("xgdx", operands);
3375 else if (X_REG_P (operands[1]))
3377 /* tys, txs preserve the flags */
3378 cc_status = cc_prev_status;
3379 output_asm_insn ("txs", operands);
3381 else if (Y_REG_P (operands[1]))
3383 /* tys, txs preserve the flags */
3384 cc_status = cc_prev_status;
3385 output_asm_insn ("tys", operands);
3387 else
3389 /* lds sets the flags but the des does not. */
3390 CC_STATUS_INIT;
3391 output_asm_insn ("lds\t%1", operands);
3392 output_asm_insn ("des", operands);
3394 break;
3396 default:
3397 fatal_insn ("invalid register in the move instruction", insn);
3398 break;
3400 return;
3402 if (SP_REG_P (operands[1]) && REG_P (operands[0])
3403 && REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
3405 output_asm_insn ("sts\t%0", operands);
3406 return;
3409 if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
3411 cc_status = cc_prev_status;
3412 switch (REGNO (operands[1]))
3414 case HARD_X_REGNUM:
3415 case HARD_Y_REGNUM:
3416 output_asm_insn ("psh%1", operands);
3417 break;
3418 case HARD_D_REGNUM:
3419 output_asm_insn ("pshb", operands);
3420 output_asm_insn ("psha", operands);
3421 break;
3422 default:
3423 gcc_unreachable ();
3425 return;
3428 /* Operand 1 must be a hard register. */
3429 if (!H_REG_P (operands[1]))
3431 fatal_insn ("invalid operand in the instruction", insn);
3434 reg = REGNO (operands[1]);
3435 switch (reg)
3437 case HARD_D_REGNUM:
3438 output_asm_insn ("std\t%0", operands);
3439 break;
3441 case HARD_X_REGNUM:
3442 output_asm_insn ("stx\t%0", operands);
3443 break;
3445 case HARD_Y_REGNUM:
3446 output_asm_insn ("sty\t%0", operands);
3447 break;
3449 case HARD_SP_REGNUM:
3450 if (ix_reg == 0)
3451 create_regs_rtx ();
3453 if (REG_P (operands[0]) && REGNO (operands[0]) == SOFT_TMP_REGNUM)
3455 output_asm_insn ("pshx", operands);
3456 output_asm_insn ("tsx", operands);
3457 output_asm_insn ("inx", operands);
3458 output_asm_insn ("inx", operands);
3459 output_asm_insn ("stx\t%0", operands);
3460 output_asm_insn ("pulx", operands);
3463 else if (reg_mentioned_p (ix_reg, operands[0]))
3465 output_asm_insn ("sty\t%t0", operands);
3466 output_asm_insn ("tsy", operands);
3467 output_asm_insn ("sty\t%0", operands);
3468 output_asm_insn ("ldy\t%t0", operands);
3470 else
3472 output_asm_insn ("stx\t%t0", operands);
3473 output_asm_insn ("tsx", operands);
3474 output_asm_insn ("stx\t%0", operands);
3475 output_asm_insn ("ldx\t%t0", operands);
3477 CC_STATUS_INIT;
3478 break;
3480 default:
3481 fatal_insn ("invalid register in the move instruction", insn);
3482 break;
3486 void
3487 m68hc11_gen_movqi (rtx insn, rtx *operands)
3489 /* Move a register or memory to the same location.
3490 This is possible because such insn can appear
3491 in a non-optimizing mode. */
3492 if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
3494 cc_status = cc_prev_status;
3495 return;
3498 if (TARGET_M6812)
3501 if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
3503 m68hc11_notice_keep_cc (operands[0]);
3504 output_asm_insn ("tfr\t%1,%0", operands);
3506 else if (H_REG_P (operands[0]))
3508 if (IS_STACK_POP (operands[1]))
3509 output_asm_insn ("pul%b0", operands);
3510 else if (Q_REG_P (operands[0]))
3511 output_asm_insn ("lda%0\t%b1", operands);
3512 else if (D_REG_P (operands[0]))
3513 output_asm_insn ("ldab\t%b1", operands);
3514 else
3515 goto m6811_move;
3517 else if (H_REG_P (operands[1]))
3519 if (Q_REG_P (operands[1]))
3520 output_asm_insn ("sta%1\t%b0", operands);
3521 else if (D_REG_P (operands[1]))
3522 output_asm_insn ("stab\t%b0", operands);
3523 else
3524 goto m6811_move;
3526 else
3528 rtx from = operands[1];
3529 rtx to = operands[0];
3531 if ((m68hc11_register_indirect_p (from, GET_MODE (from))
3532 && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
3533 || (m68hc11_register_indirect_p (to, GET_MODE (to))
3534 && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
3536 rtx ops[3];
3538 if (operands[2])
3540 ops[0] = operands[2];
3541 ops[1] = from;
3542 ops[2] = 0;
3543 m68hc11_gen_movqi (insn, ops);
3544 ops[0] = to;
3545 ops[1] = operands[2];
3546 m68hc11_gen_movqi (insn, ops);
3548 else
3550 /* !!!! SCz wrong here. */
3551 fatal_insn ("move insn not handled", insn);
3554 else
3556 if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
3558 output_asm_insn ("clr\t%b0", operands);
3560 else
3562 m68hc11_notice_keep_cc (operands[0]);
3563 output_asm_insn ("movb\t%b1,%b0", operands);
3567 return;
3570 m6811_move:
3571 if (H_REG_P (operands[0]))
3573 switch (REGNO (operands[0]))
3575 case HARD_B_REGNUM:
3576 case HARD_D_REGNUM:
3577 if (X_REG_P (operands[1]))
3579 if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
3581 m68hc11_output_swap (insn, operands);
3583 else
3585 output_asm_insn ("stx\t%t1", operands);
3586 output_asm_insn ("ldab\t%T0", operands);
3589 else if (Y_REG_P (operands[1]))
3591 if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
3593 m68hc11_output_swap (insn, operands);
3595 else
3597 output_asm_insn ("sty\t%t1", operands);
3598 output_asm_insn ("ldab\t%T0", operands);
3601 else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
3602 && !DA_REG_P (operands[1]))
3604 output_asm_insn ("ldab\t%b1", operands);
3606 else if (DA_REG_P (operands[1]))
3608 output_asm_insn ("tab", operands);
3610 else
3612 cc_status = cc_prev_status;
3613 return;
3615 break;
3617 case HARD_A_REGNUM:
3618 if (X_REG_P (operands[1]))
3620 output_asm_insn ("stx\t%t1", operands);
3621 output_asm_insn ("ldaa\t%T0", operands);
3623 else if (Y_REG_P (operands[1]))
3625 output_asm_insn ("sty\t%t1", operands);
3626 output_asm_insn ("ldaa\t%T0", operands);
3628 else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
3629 && !DA_REG_P (operands[1]))
3631 output_asm_insn ("ldaa\t%b1", operands);
3633 else if (!DA_REG_P (operands[1]))
3635 output_asm_insn ("tba", operands);
3637 else
3639 cc_status = cc_prev_status;
3641 break;
3643 case HARD_X_REGNUM:
3644 if (D_REG_P (operands[1]))
3646 if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3648 m68hc11_output_swap (insn, operands);
3650 else
3652 output_asm_insn ("stab\t%T1", operands);
3653 output_asm_insn ("ldx\t%t1", operands);
3655 CC_STATUS_INIT;
3657 else if (Y_REG_P (operands[1]))
3659 output_asm_insn ("sty\t%t0", operands);
3660 output_asm_insn ("ldx\t%t0", operands);
3662 else if (GET_CODE (operands[1]) == CONST_INT)
3664 output_asm_insn ("ldx\t%1", operands);
3666 else if (dead_register_here (insn, d_reg))
3668 output_asm_insn ("ldab\t%b1", operands);
3669 output_asm_insn ("xgdx", operands);
3671 else if (!reg_mentioned_p (operands[0], operands[1]))
3673 output_asm_insn ("xgdx", operands);
3674 output_asm_insn ("ldab\t%b1", operands);
3675 output_asm_insn ("xgdx", operands);
3677 else
3679 output_asm_insn ("pshb", operands);
3680 output_asm_insn ("ldab\t%b1", operands);
3681 output_asm_insn ("stab\t%T1", operands);
3682 output_asm_insn ("ldx\t%t1", operands);
3683 output_asm_insn ("pulb", operands);
3684 CC_STATUS_INIT;
3686 break;
3688 case HARD_Y_REGNUM:
3689 if (D_REG_P (operands[1]))
3691 output_asm_insn ("stab\t%T1", operands);
3692 output_asm_insn ("ldy\t%t1", operands);
3693 CC_STATUS_INIT;
3695 else if (X_REG_P (operands[1]))
3697 output_asm_insn ("stx\t%t1", operands);
3698 output_asm_insn ("ldy\t%t1", operands);
3699 CC_STATUS_INIT;
3701 else if (GET_CODE (operands[1]) == CONST_INT)
3703 output_asm_insn ("ldy\t%1", operands);
3705 else if (dead_register_here (insn, d_reg))
3707 output_asm_insn ("ldab\t%b1", operands);
3708 output_asm_insn ("xgdy", operands);
3710 else if (!reg_mentioned_p (operands[0], operands[1]))
3712 output_asm_insn ("xgdy", operands);
3713 output_asm_insn ("ldab\t%b1", operands);
3714 output_asm_insn ("xgdy", operands);
3716 else
3718 output_asm_insn ("pshb", operands);
3719 output_asm_insn ("ldab\t%b1", operands);
3720 output_asm_insn ("stab\t%T1", operands);
3721 output_asm_insn ("ldy\t%t1", operands);
3722 output_asm_insn ("pulb", operands);
3723 CC_STATUS_INIT;
3725 break;
3727 default:
3728 fatal_insn ("invalid register in the instruction", insn);
3729 break;
3732 else if (H_REG_P (operands[1]))
3734 switch (REGNO (operands[1]))
3736 case HARD_D_REGNUM:
3737 case HARD_B_REGNUM:
3738 output_asm_insn ("stab\t%b0", operands);
3739 break;
3741 case HARD_A_REGNUM:
3742 output_asm_insn ("staa\t%b0", operands);
3743 break;
3745 case HARD_X_REGNUM:
3746 output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
3747 break;
3749 case HARD_Y_REGNUM:
3750 output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
3751 break;
3753 default:
3754 fatal_insn ("invalid register in the move instruction", insn);
3755 break;
3757 return;
3759 else
3761 fatal_insn ("operand 1 must be a hard register", insn);
3765 /* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
3766 The source and destination must be D or A and the shift must
3767 be a constant. */
3768 void
3769 m68hc11_gen_rotate (enum rtx_code code, rtx insn, rtx operands[])
3771 int val;
3773 if (GET_CODE (operands[2]) != CONST_INT
3774 || (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
3775 fatal_insn ("invalid rotate insn", insn);
3777 val = INTVAL (operands[2]);
3778 if (code == ROTATERT)
3779 val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
3781 if (GET_MODE (operands[0]) != QImode)
3782 CC_STATUS_INIT;
3784 /* Rotate by 8-bits if the shift is within [5..11]. */
3785 if (val >= 5 && val <= 11)
3787 if (TARGET_M6812)
3788 output_asm_insn ("exg\ta,b", operands);
3789 else
3791 output_asm_insn ("psha", operands);
3792 output_asm_insn ("tba", operands);
3793 output_asm_insn ("pulb", operands);
3795 val -= 8;
3798 /* If the shift is big, invert the rotation. */
3799 else if (val >= 12)
3801 val = val - 16;
3804 if (val > 0)
3806 while (--val >= 0)
3808 /* Set the carry to bit-15, but don't change D yet. */
3809 if (GET_MODE (operands[0]) != QImode)
3811 output_asm_insn ("asra", operands);
3812 output_asm_insn ("rola", operands);
3815 /* Rotate B first to move the carry to bit-0. */
3816 if (D_REG_P (operands[0]))
3817 output_asm_insn ("rolb", operands);
3819 if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
3820 output_asm_insn ("rola", operands);
3823 else
3825 while (++val <= 0)
3827 /* Set the carry to bit-8 of D. */
3828 if (GET_MODE (operands[0]) != QImode)
3829 output_asm_insn ("tap", operands);
3831 /* Rotate B first to move the carry to bit-7. */
3832 if (D_REG_P (operands[0]))
3833 output_asm_insn ("rorb", operands);
3835 if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
3836 output_asm_insn ("rora", operands);
3843 /* Store in cc_status the expressions that the condition codes will
3844 describe after execution of an instruction whose pattern is EXP.
3845 Do not alter them if the instruction would not alter the cc's. */
3847 void
3848 m68hc11_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
3850 /* recognize SET insn's. */
3851 if (GET_CODE (exp) == SET)
3853 /* Jumps do not alter the cc's. */
3854 if (SET_DEST (exp) == pc_rtx)
3857 /* NOTE: most instructions don't affect the carry bit, but the
3858 bhi/bls/bhs/blo instructions use it. This isn't mentioned in
3859 the conditions.h header. */
3861 /* Function calls clobber the cc's. */
3862 else if (GET_CODE (SET_SRC (exp)) == CALL)
3864 CC_STATUS_INIT;
3867 /* Tests and compares set the cc's in predictable ways. */
3868 else if (SET_DEST (exp) == cc0_rtx)
3870 cc_status.flags = 0;
3871 cc_status.value1 = XEXP (exp, 0);
3872 if (GET_CODE (XEXP (exp, 1)) == COMPARE
3873 && XEXP (XEXP (exp, 1), 1) == CONST0_RTX (GET_MODE (XEXP (XEXP (exp, 1), 0))))
3874 cc_status.value2 = XEXP (XEXP (exp, 1), 0);
3875 else
3876 cc_status.value2 = XEXP (exp, 1);
3878 else
3880 /* All other instructions affect the condition codes. */
3881 cc_status.flags = 0;
3882 cc_status.value1 = XEXP (exp, 0);
3883 cc_status.value2 = XEXP (exp, 1);
3886 else
3888 /* Default action if we haven't recognized something
3889 and returned earlier. */
3890 CC_STATUS_INIT;
3893 if (cc_status.value2 != 0)
3894 switch (GET_CODE (cc_status.value2))
3896 /* These logical operations can generate several insns.
3897 The flags are setup according to what is generated. */
3898 case IOR:
3899 case XOR:
3900 case AND:
3901 break;
3903 /* The (not ...) generates several 'com' instructions for
3904 non QImode. We have to invalidate the flags. */
3905 case NOT:
3906 if (GET_MODE (cc_status.value2) != QImode)
3907 CC_STATUS_INIT;
3908 break;
3910 case PLUS:
3911 case MINUS:
3912 case MULT:
3913 case DIV:
3914 case UDIV:
3915 case MOD:
3916 case UMOD:
3917 case NEG:
3918 if (GET_MODE (cc_status.value2) != VOIDmode)
3919 cc_status.flags |= CC_NO_OVERFLOW;
3920 break;
3922 /* The asl sets the overflow bit in such a way that this
3923 makes the flags unusable for a next compare insn. */
3924 case ASHIFT:
3925 case ROTATE:
3926 case ROTATERT:
3927 if (GET_MODE (cc_status.value2) != VOIDmode)
3928 cc_status.flags |= CC_NO_OVERFLOW;
3929 break;
3931 /* A load/store instruction does not affect the carry. */
3932 case MEM:
3933 case SYMBOL_REF:
3934 case REG:
3935 case CONST_INT:
3936 cc_status.flags |= CC_NO_OVERFLOW;
3937 break;
3939 default:
3940 break;
3942 if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
3943 && cc_status.value2
3944 && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
3945 cc_status.value2 = 0;
3947 else if (cc_status.value1 && side_effects_p (cc_status.value1))
3948 cc_status.value1 = 0;
3950 else if (cc_status.value2 && side_effects_p (cc_status.value2))
3951 cc_status.value2 = 0;
3954 /* The current instruction does not affect the flags but changes
3955 the register 'reg'. See if the previous flags can be kept for the
3956 next instruction to avoid a comparison. */
3957 void
3958 m68hc11_notice_keep_cc (rtx reg)
3960 if (reg == 0
3961 || cc_prev_status.value1 == 0
3962 || rtx_equal_p (reg, cc_prev_status.value1)
3963 || (cc_prev_status.value2
3964 && reg_mentioned_p (reg, cc_prev_status.value2)))
3965 CC_STATUS_INIT;
3966 else
3967 cc_status = cc_prev_status;
3972 /* Machine Specific Reorg. */
3974 /* Z register replacement:
3976 GCC treats the Z register as an index base address register like
3977 X or Y. In general, it uses it during reload to compute the address
3978 of some operand. This helps the reload pass to avoid to fall into the
3979 register spill failure.
3981 The Z register is in the A_REGS class. In the machine description,
3982 the 'A' constraint matches it. The 'x' or 'y' constraints do not.
3984 It can appear everywhere an X or Y register can appear, except for
3985 some templates in the clobber section (when a clobber of X or Y is asked).
3986 For a given instruction, the template must ensure that no more than
3987 2 'A' registers are used. Otherwise, the register replacement is not
3988 possible.
3990 To replace the Z register, the algorithm is not terrific:
3991 1. Insns that do not use the Z register are not changed
3992 2. When a Z register is used, we scan forward the insns to see
3993 a potential register to use: either X or Y and sometimes D.
3994 We stop when a call, a label or a branch is seen, or when we
3995 detect that both X and Y are used (probably at different times, but it does
3996 not matter).
3997 3. The register that will be used for the replacement of Z is saved
3998 in a .page0 register or on the stack. If the first instruction that
3999 used Z, uses Z as an input, the value is loaded from another .page0
4000 register. The replacement register is pushed on the stack in the
4001 rare cases where a compare insn uses Z and we couldn't find if X/Y
4002 are dead.
4003 4. The Z register is replaced in all instructions until we reach
4004 the end of the Z-block, as detected by step 2.
4005 5. If we detect that Z is still alive, its value is saved.
4006 If the replacement register is alive, its old value is loaded.
4008 The Z register can be disabled with -ffixed-z.
4011 struct replace_info
4013 rtx first;
4014 rtx replace_reg;
4015 int need_save_z;
4016 int must_load_z;
4017 int must_save_reg;
4018 int must_restore_reg;
4019 rtx last;
4020 int regno;
4021 int x_used;
4022 int y_used;
4023 int can_use_d;
4024 int found_call;
4025 int z_died;
4026 int z_set_count;
4027 rtx z_value;
4028 int must_push_reg;
4029 int save_before_last;
4030 int z_loaded_with_sp;
4033 static int m68hc11_check_z_replacement (rtx, struct replace_info *);
4034 static void m68hc11_find_z_replacement (rtx, struct replace_info *);
4035 static void m68hc11_z_replacement (rtx);
4036 static void m68hc11_reassign_regs (rtx);
4038 int z_replacement_completed = 0;
4040 /* Analyze the insn to find out which replacement register to use and
4041 the boundaries of the replacement.
4042 Returns 0 if we reached the last insn to be replaced, 1 if we can
4043 continue replacement in next insns. */
4045 static int
4046 m68hc11_check_z_replacement (rtx insn, struct replace_info *info)
4048 int this_insn_uses_ix;
4049 int this_insn_uses_iy;
4050 int this_insn_uses_z;
4051 int this_insn_uses_z_in_dst;
4052 int this_insn_uses_d;
4053 rtx body;
4054 int z_dies_here;
4056 /* A call is said to clobber the Z register, we don't need
4057 to save the value of Z. We also don't need to restore
4058 the replacement register (unless it is used by the call). */
4059 if (GET_CODE (insn) == CALL_INSN)
4061 body = PATTERN (insn);
4063 info->can_use_d = 0;
4065 /* If the call is an indirect call with Z, we have to use the
4066 Y register because X can be used as an input (D+X).
4067 We also must not save Z nor restore Y. */
4068 if (reg_mentioned_p (z_reg, body))
4070 insn = NEXT_INSN (insn);
4071 info->x_used = 1;
4072 info->y_used = 0;
4073 info->found_call = 1;
4074 info->must_restore_reg = 0;
4075 info->last = NEXT_INSN (insn);
4077 info->need_save_z = 0;
4078 return 0;
4080 if (GET_CODE (insn) == CODE_LABEL
4081 || GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
4082 return 0;
4084 if (GET_CODE (insn) == JUMP_INSN)
4086 if (reg_mentioned_p (z_reg, insn) == 0)
4087 return 0;
4089 info->can_use_d = 0;
4090 info->must_save_reg = 0;
4091 info->must_restore_reg = 0;
4092 info->need_save_z = 0;
4093 info->last = NEXT_INSN (insn);
4094 return 0;
4096 if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
4098 return 1;
4101 /* Z register dies here. */
4102 z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
4104 body = PATTERN (insn);
4105 if (GET_CODE (body) == SET)
4107 rtx src = XEXP (body, 1);
4108 rtx dst = XEXP (body, 0);
4110 /* Condition code is set here. We have to restore the X/Y and
4111 save into Z before any test/compare insn because once we save/restore
4112 we can change the condition codes. When the compare insn uses Z and
4113 we can't use X/Y, the comparison is made with the *ZREG soft register
4114 (this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
4115 if (dst == cc0_rtx)
4117 if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
4118 || (GET_CODE (src) == COMPARE &&
4119 ((rtx_equal_p (XEXP (src, 0), z_reg)
4120 && H_REG_P (XEXP (src, 1)))
4121 || (rtx_equal_p (XEXP (src, 1), z_reg)
4122 && H_REG_P (XEXP (src, 0))))))
4124 if (insn == info->first)
4126 info->must_load_z = 0;
4127 info->must_save_reg = 0;
4128 info->must_restore_reg = 0;
4129 info->need_save_z = 0;
4130 info->found_call = 1;
4131 info->regno = SOFT_Z_REGNUM;
4132 info->last = NEXT_INSN (insn);
4134 return 0;
4136 if (reg_mentioned_p (z_reg, src) == 0)
4138 info->can_use_d = 0;
4139 return 0;
4142 if (insn != info->first)
4143 return 0;
4145 /* Compare insn which uses Z. We have to save/restore the X/Y
4146 register without modifying the condition codes. For this
4147 we have to use a push/pop insn. */
4148 info->must_push_reg = 1;
4149 info->last = insn;
4152 /* Z reg is set to something new. We don't need to load it. */
4153 if (Z_REG_P (dst))
4155 if (!reg_mentioned_p (z_reg, src))
4157 /* Z reg is used before being set. Treat this as
4158 a new sequence of Z register replacement. */
4159 if (insn != info->first)
4161 return 0;
4163 info->must_load_z = 0;
4165 info->z_set_count++;
4166 info->z_value = src;
4167 if (SP_REG_P (src))
4168 info->z_loaded_with_sp = 1;
4170 else if (reg_mentioned_p (z_reg, dst))
4171 info->can_use_d = 0;
4173 this_insn_uses_d = reg_mentioned_p (d_reg, src)
4174 | reg_mentioned_p (d_reg, dst);
4175 this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
4176 | reg_mentioned_p (ix_reg, dst);
4177 this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
4178 | reg_mentioned_p (iy_reg, dst);
4179 this_insn_uses_z = reg_mentioned_p (z_reg, src);
4181 /* If z is used as an address operand (like (MEM (reg z))),
4182 we can't replace it with d. */
4183 if (this_insn_uses_z && !Z_REG_P (src)
4184 && !(m68hc11_arith_operator (src, GET_MODE (src))
4185 && Z_REG_P (XEXP (src, 0))
4186 && !reg_mentioned_p (z_reg, XEXP (src, 1))
4187 && insn == info->first
4188 && dead_register_here (insn, d_reg)))
4189 info->can_use_d = 0;
4191 this_insn_uses_z_in_dst = reg_mentioned_p (z_reg, dst);
4192 if (TARGET_M6812 && !z_dies_here
4193 && ((this_insn_uses_z && side_effects_p (src))
4194 || (this_insn_uses_z_in_dst && side_effects_p (dst))))
4196 info->need_save_z = 1;
4197 info->z_set_count++;
4199 this_insn_uses_z |= this_insn_uses_z_in_dst;
4201 if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
4203 fatal_insn ("registers IX, IY and Z used in the same INSN", insn);
4206 if (this_insn_uses_d)
4207 info->can_use_d = 0;
4209 /* IX and IY are used at the same time, we have to restore
4210 the value of the scratch register before this insn. */
4211 if (this_insn_uses_ix && this_insn_uses_iy)
4213 return 0;
4216 if (this_insn_uses_ix && X_REG_P (dst) && GET_MODE (dst) == SImode)
4217 info->can_use_d = 0;
4219 if (info->x_used == 0 && this_insn_uses_ix)
4221 if (info->y_used)
4223 /* We have a (set (REG:HI X) (REG:HI Z)).
4224 Since we use Z as the replacement register, this insn
4225 is no longer necessary. We turn it into a note. We must
4226 not reload the old value of X. */
4227 if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
4229 if (z_dies_here)
4231 info->need_save_z = 0;
4232 info->z_died = 1;
4234 info->must_save_reg = 0;
4235 info->must_restore_reg = 0;
4236 info->found_call = 1;
4237 info->can_use_d = 0;
4238 SET_INSN_DELETED (insn);
4239 info->last = NEXT_INSN (insn);
4240 return 0;
4243 if (X_REG_P (dst)
4244 && (rtx_equal_p (src, z_reg)
4245 || (z_dies_here && !reg_mentioned_p (ix_reg, src))))
4247 if (z_dies_here)
4249 info->need_save_z = 0;
4250 info->z_died = 1;
4252 info->last = NEXT_INSN (insn);
4253 info->must_save_reg = 0;
4254 info->must_restore_reg = 0;
4256 else if (X_REG_P (dst) && reg_mentioned_p (z_reg, src)
4257 && !reg_mentioned_p (ix_reg, src))
4259 if (z_dies_here)
4261 info->z_died = 1;
4262 info->need_save_z = 0;
4264 else if (TARGET_M6812 && side_effects_p (src))
4266 info->last = 0;
4267 info->must_restore_reg = 0;
4268 return 0;
4270 else
4272 info->save_before_last = 1;
4274 info->must_restore_reg = 0;
4275 info->last = NEXT_INSN (insn);
4277 else if (info->can_use_d)
4279 info->last = NEXT_INSN (insn);
4280 info->x_used = 1;
4282 return 0;
4284 info->x_used = 1;
4285 if (z_dies_here && !reg_mentioned_p (ix_reg, src)
4286 && GET_CODE (dst) == REG && REGNO (dst) == HARD_X_REGNUM)
4288 info->need_save_z = 0;
4289 info->z_died = 1;
4290 info->last = NEXT_INSN (insn);
4291 info->regno = HARD_X_REGNUM;
4292 info->must_save_reg = 0;
4293 info->must_restore_reg = 0;
4294 return 0;
4296 if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, ix_reg))
4298 info->regno = HARD_X_REGNUM;
4299 info->must_restore_reg = 0;
4300 info->must_save_reg = 0;
4301 return 0;
4304 if (info->y_used == 0 && this_insn_uses_iy)
4306 if (info->x_used)
4308 if (Y_REG_P (dst) && rtx_equal_p (src, z_reg))
4310 if (z_dies_here)
4312 info->need_save_z = 0;
4313 info->z_died = 1;
4315 info->must_save_reg = 0;
4316 info->must_restore_reg = 0;
4317 info->found_call = 1;
4318 info->can_use_d = 0;
4319 SET_INSN_DELETED (insn);
4320 info->last = NEXT_INSN (insn);
4321 return 0;
4324 if (Y_REG_P (dst)
4325 && (rtx_equal_p (src, z_reg)
4326 || (z_dies_here && !reg_mentioned_p (iy_reg, src))))
4328 if (z_dies_here)
4330 info->z_died = 1;
4331 info->need_save_z = 0;
4333 info->last = NEXT_INSN (insn);
4334 info->must_save_reg = 0;
4335 info->must_restore_reg = 0;
4337 else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
4338 && !reg_mentioned_p (iy_reg, src))
4340 if (z_dies_here)
4342 info->z_died = 1;
4343 info->need_save_z = 0;
4345 else if (TARGET_M6812 && side_effects_p (src))
4347 info->last = 0;
4348 info->must_restore_reg = 0;
4349 return 0;
4351 else
4353 info->save_before_last = 1;
4355 info->must_restore_reg = 0;
4356 info->last = NEXT_INSN (insn);
4358 else if (info->can_use_d)
4360 info->last = NEXT_INSN (insn);
4361 info->y_used = 1;
4364 return 0;
4366 info->y_used = 1;
4367 if (z_dies_here && !reg_mentioned_p (iy_reg, src)
4368 && GET_CODE (dst) == REG && REGNO (dst) == HARD_Y_REGNUM)
4370 info->need_save_z = 0;
4371 info->z_died = 1;
4372 info->last = NEXT_INSN (insn);
4373 info->regno = HARD_Y_REGNUM;
4374 info->must_save_reg = 0;
4375 info->must_restore_reg = 0;
4376 return 0;
4378 if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, iy_reg))
4380 info->regno = HARD_Y_REGNUM;
4381 info->must_restore_reg = 0;
4382 info->must_save_reg = 0;
4383 return 0;
4386 if (z_dies_here)
4388 info->need_save_z = 0;
4389 info->z_died = 1;
4390 if (info->last == 0)
4391 info->last = NEXT_INSN (insn);
4392 return 0;
4394 return info->last != NULL_RTX ? 0 : 1;
4396 if (GET_CODE (body) == PARALLEL)
4398 int i;
4399 char ix_clobber = 0;
4400 char iy_clobber = 0;
4401 char z_clobber = 0;
4402 this_insn_uses_iy = 0;
4403 this_insn_uses_ix = 0;
4404 this_insn_uses_z = 0;
4406 for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
4408 rtx x;
4409 int uses_ix, uses_iy, uses_z;
4411 x = XVECEXP (body, 0, i);
4413 if (info->can_use_d && reg_mentioned_p (d_reg, x))
4414 info->can_use_d = 0;
4416 uses_ix = reg_mentioned_p (ix_reg, x);
4417 uses_iy = reg_mentioned_p (iy_reg, x);
4418 uses_z = reg_mentioned_p (z_reg, x);
4419 if (GET_CODE (x) == CLOBBER)
4421 ix_clobber |= uses_ix;
4422 iy_clobber |= uses_iy;
4423 z_clobber |= uses_z;
4425 else
4427 this_insn_uses_ix |= uses_ix;
4428 this_insn_uses_iy |= uses_iy;
4429 this_insn_uses_z |= uses_z;
4431 if (uses_z && GET_CODE (x) == SET)
4433 rtx dst = XEXP (x, 0);
4435 if (Z_REG_P (dst))
4436 info->z_set_count++;
4438 if (TARGET_M6812 && uses_z && side_effects_p (x))
4439 info->need_save_z = 1;
4441 if (z_clobber)
4442 info->need_save_z = 0;
4444 if (debug_m6811)
4446 printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
4447 this_insn_uses_ix, this_insn_uses_iy,
4448 this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
4449 debug_rtx (insn);
4451 if (this_insn_uses_z)
4452 info->can_use_d = 0;
4454 if (z_clobber && info->first != insn)
4456 info->need_save_z = 0;
4457 info->last = insn;
4458 return 0;
4460 if (z_clobber && info->x_used == 0 && info->y_used == 0)
4462 if (this_insn_uses_z == 0 && insn == info->first)
4464 info->must_load_z = 0;
4466 if (dead_register_here (insn, d_reg))
4468 info->regno = HARD_D_REGNUM;
4469 info->must_save_reg = 0;
4470 info->must_restore_reg = 0;
4472 else if (dead_register_here (insn, ix_reg))
4474 info->regno = HARD_X_REGNUM;
4475 info->must_save_reg = 0;
4476 info->must_restore_reg = 0;
4478 else if (dead_register_here (insn, iy_reg))
4480 info->regno = HARD_Y_REGNUM;
4481 info->must_save_reg = 0;
4482 info->must_restore_reg = 0;
4484 if (info->regno >= 0)
4486 info->last = NEXT_INSN (insn);
4487 return 0;
4489 if (this_insn_uses_ix == 0)
4491 info->regno = HARD_X_REGNUM;
4492 info->must_save_reg = 1;
4493 info->must_restore_reg = 1;
4495 else if (this_insn_uses_iy == 0)
4497 info->regno = HARD_Y_REGNUM;
4498 info->must_save_reg = 1;
4499 info->must_restore_reg = 1;
4501 else
4503 info->regno = HARD_D_REGNUM;
4504 info->must_save_reg = 1;
4505 info->must_restore_reg = 1;
4507 info->last = NEXT_INSN (insn);
4508 return 0;
4511 if (((info->x_used || this_insn_uses_ix) && iy_clobber)
4512 || ((info->y_used || this_insn_uses_iy) && ix_clobber))
4514 if (this_insn_uses_z)
4516 if (info->y_used == 0 && iy_clobber)
4518 info->regno = HARD_Y_REGNUM;
4519 info->must_save_reg = 0;
4520 info->must_restore_reg = 0;
4522 if (info->first != insn
4523 && ((info->y_used && ix_clobber)
4524 || (info->x_used && iy_clobber)))
4525 info->last = insn;
4526 else
4527 info->last = NEXT_INSN (insn);
4528 info->save_before_last = 1;
4530 return 0;
4532 if (this_insn_uses_ix && this_insn_uses_iy)
4534 if (this_insn_uses_z)
4536 fatal_insn ("cannot do z-register replacement", insn);
4538 return 0;
4540 if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
4542 if (info->y_used)
4544 return 0;
4546 info->x_used = 1;
4547 if (iy_clobber || z_clobber)
4549 info->last = NEXT_INSN (insn);
4550 info->save_before_last = 1;
4551 return 0;
4555 if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
4557 if (info->x_used)
4559 return 0;
4561 info->y_used = 1;
4562 if (ix_clobber || z_clobber)
4564 info->last = NEXT_INSN (insn);
4565 info->save_before_last = 1;
4566 return 0;
4569 if (z_dies_here)
4571 info->z_died = 1;
4572 info->need_save_z = 0;
4574 return 1;
4576 if (GET_CODE (body) == CLOBBER)
4579 /* IX and IY are used at the same time, we have to restore
4580 the value of the scratch register before this insn. */
4581 if (this_insn_uses_ix && this_insn_uses_iy)
4583 return 0;
4585 if (info->x_used == 0 && this_insn_uses_ix)
4587 if (info->y_used)
4589 return 0;
4591 info->x_used = 1;
4593 if (info->y_used == 0 && this_insn_uses_iy)
4595 if (info->x_used)
4597 return 0;
4599 info->y_used = 1;
4601 return 1;
4603 return 1;
4606 static void
4607 m68hc11_find_z_replacement (rtx insn, struct replace_info *info)
4609 int reg;
4611 info->replace_reg = NULL_RTX;
4612 info->must_load_z = 1;
4613 info->need_save_z = 1;
4614 info->must_save_reg = 1;
4615 info->must_restore_reg = 1;
4616 info->first = insn;
4617 info->x_used = 0;
4618 info->y_used = 0;
4619 info->can_use_d = TARGET_M6811 ? 1 : 0;
4620 info->found_call = 0;
4621 info->z_died = 0;
4622 info->last = 0;
4623 info->regno = -1;
4624 info->z_set_count = 0;
4625 info->z_value = NULL_RTX;
4626 info->must_push_reg = 0;
4627 info->save_before_last = 0;
4628 info->z_loaded_with_sp = 0;
4630 /* Scan the insn forward to find an address register that is not used.
4631 Stop when:
4632 - the flow of the program changes,
4633 - when we detect that both X and Y are necessary,
4634 - when the Z register dies,
4635 - when the condition codes are set. */
4637 for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
4639 if (m68hc11_check_z_replacement (insn, info) == 0)
4640 break;
4643 /* May be we can use Y or X if they contain the same value as Z.
4644 This happens very often after the reload. */
4645 if (info->z_set_count == 1)
4647 rtx p = info->first;
4648 rtx v = 0;
4650 if (info->x_used)
4652 v = find_last_value (iy_reg, &p, insn, 1);
4654 else if (info->y_used)
4656 v = find_last_value (ix_reg, &p, insn, 1);
4658 if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
4660 if (info->x_used)
4661 info->regno = HARD_Y_REGNUM;
4662 else
4663 info->regno = HARD_X_REGNUM;
4664 info->must_load_z = 0;
4665 info->must_save_reg = 0;
4666 info->must_restore_reg = 0;
4667 info->found_call = 1;
4670 if (info->z_set_count == 0)
4671 info->need_save_z = 0;
4673 if (insn == 0)
4674 info->need_save_z = 0;
4676 if (info->last == 0)
4677 info->last = insn;
4679 if (info->regno >= 0)
4681 reg = info->regno;
4682 info->replace_reg = gen_rtx_REG (HImode, reg);
4684 else if (info->can_use_d)
4686 reg = HARD_D_REGNUM;
4687 info->replace_reg = d_reg;
4689 else if (info->x_used)
4691 reg = HARD_Y_REGNUM;
4692 info->replace_reg = iy_reg;
4694 else
4696 reg = HARD_X_REGNUM;
4697 info->replace_reg = ix_reg;
4699 info->regno = reg;
4701 if (info->must_save_reg && info->must_restore_reg)
4703 if (insn && dead_register_here (insn, info->replace_reg))
4705 info->must_save_reg = 0;
4706 info->must_restore_reg = 0;
4711 /* The insn uses the Z register. Find a replacement register for it
4712 (either X or Y) and replace it in the insn and the next ones until
4713 the flow changes or the replacement register is used. Instructions
4714 are emitted before and after the Z-block to preserve the value of
4715 Z and of the replacement register. */
4717 static void
4718 m68hc11_z_replacement (rtx insn)
4720 rtx replace_reg_qi;
4721 rtx replace_reg;
4722 struct replace_info info;
4724 /* Find trivial case where we only need to replace z with the
4725 equivalent soft register. */
4726 if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
4728 rtx body = PATTERN (insn);
4729 rtx src = XEXP (body, 1);
4730 rtx dst = XEXP (body, 0);
4732 if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
4734 XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
4735 return;
4737 else if (Z_REG_P (src)
4738 && ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
4740 XEXP (body, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
4741 return;
4743 else if (D_REG_P (dst)
4744 && m68hc11_arith_operator (src, GET_MODE (src))
4745 && D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
4747 XEXP (src, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
4748 return;
4750 else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
4751 && INTVAL (src) == 0)
4753 XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
4754 /* Force it to be re-recognized. */
4755 INSN_CODE (insn) = -1;
4756 return;
4760 m68hc11_find_z_replacement (insn, &info);
4762 replace_reg = info.replace_reg;
4763 replace_reg_qi = NULL_RTX;
4765 /* Save the X register in a .page0 location. */
4766 if (info.must_save_reg && !info.must_push_reg)
4768 rtx dst;
4770 if (info.must_push_reg && 0)
4771 dst = gen_rtx_MEM (HImode,
4772 gen_rtx_PRE_DEC (HImode,
4773 gen_rtx_REG (HImode, HARD_SP_REGNUM)));
4774 else
4775 dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
4777 emit_insn_before (gen_movhi (dst,
4778 gen_rtx_REG (HImode, info.regno)), insn);
4780 if (info.must_load_z && !info.must_push_reg)
4782 emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
4783 gen_rtx_REG (HImode, SOFT_Z_REGNUM)),
4784 insn);
4788 /* Replace all occurrence of Z by replace_reg.
4789 Stop when the last instruction to replace is reached.
4790 Also stop when we detect a change in the flow (but it's not
4791 necessary; just safeguard). */
4793 for (; insn && insn != info.last; insn = NEXT_INSN (insn))
4795 rtx body;
4797 if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
4798 break;
4800 if (GET_CODE (insn) != INSN
4801 && GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
4802 continue;
4804 body = PATTERN (insn);
4805 if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
4806 || GET_CODE (body) == ASM_OPERANDS
4807 || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
4809 rtx note;
4811 if (debug_m6811 && reg_mentioned_p (replace_reg, body))
4813 printf ("Reg mentioned here...:\n");
4814 fflush (stdout);
4815 debug_rtx (insn);
4818 /* Stack pointer was decremented by 2 due to the push.
4819 Correct that by adding 2 to the destination. */
4820 if (info.must_push_reg
4821 && info.z_loaded_with_sp && GET_CODE (body) == SET)
4823 rtx src, dst;
4825 src = SET_SRC (body);
4826 dst = SET_DEST (body);
4827 if (SP_REG_P (src) && Z_REG_P (dst))
4828 emit_insn_after (gen_addhi3 (dst, dst, const2_rtx), insn);
4831 /* Replace any (REG:HI Z) occurrence by either X or Y. */
4832 if (!validate_replace_rtx (z_reg, replace_reg, insn))
4834 INSN_CODE (insn) = -1;
4835 if (!validate_replace_rtx (z_reg, replace_reg, insn))
4836 fatal_insn ("cannot do z-register replacement", insn);
4839 /* Likewise for (REG:QI Z). */
4840 if (reg_mentioned_p (z_reg, insn))
4842 if (replace_reg_qi == NULL_RTX)
4843 replace_reg_qi = gen_rtx_REG (QImode, REGNO (replace_reg));
4844 validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
4847 /* If there is a REG_INC note on Z, replace it with a
4848 REG_INC note on the replacement register. This is necessary
4849 to make sure that the flow pass will identify the change
4850 and it will not remove a possible insn that saves Z. */
4851 for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
4853 if (REG_NOTE_KIND (note) == REG_INC
4854 && GET_CODE (XEXP (note, 0)) == REG
4855 && REGNO (XEXP (note, 0)) == REGNO (z_reg))
4857 XEXP (note, 0) = replace_reg;
4861 if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
4862 break;
4865 /* Save Z before restoring the old value. */
4866 if (insn && info.need_save_z && !info.must_push_reg)
4868 rtx save_pos_insn = insn;
4870 /* If Z is clobber by the last insn, we have to save its value
4871 before the last instruction. */
4872 if (info.save_before_last)
4873 save_pos_insn = PREV_INSN (save_pos_insn);
4875 emit_insn_before (gen_movhi (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
4876 gen_rtx_REG (HImode, info.regno)),
4877 save_pos_insn);
4880 if (info.must_push_reg && info.last)
4882 rtx new_body, body;
4884 body = PATTERN (info.last);
4885 new_body = gen_rtx_PARALLEL (VOIDmode,
4886 gen_rtvec (3, body,
4887 gen_rtx_USE (VOIDmode,
4888 replace_reg),
4889 gen_rtx_USE (VOIDmode,
4890 gen_rtx_REG (HImode,
4891 SOFT_Z_REGNUM))));
4892 PATTERN (info.last) = new_body;
4894 /* Force recognition on insn since we changed it. */
4895 INSN_CODE (insn) = -1;
4897 if (!validate_replace_rtx (z_reg, replace_reg, info.last))
4899 fatal_insn ("invalid Z register replacement for insn", insn);
4901 insn = NEXT_INSN (info.last);
4904 /* Restore replacement register unless it was died. */
4905 if (insn && info.must_restore_reg && !info.must_push_reg)
4907 rtx dst;
4909 if (info.must_push_reg && 0)
4910 dst = gen_rtx_MEM (HImode,
4911 gen_rtx_POST_INC (HImode,
4912 gen_rtx_REG (HImode, HARD_SP_REGNUM)));
4913 else
4914 dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
4916 emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
4917 dst), insn);
4923 /* Scan all the insn and re-affects some registers
4924 - The Z register (if it was used), is affected to X or Y depending
4925 on the instruction. */
4927 static void
4928 m68hc11_reassign_regs (rtx first)
4930 rtx insn;
4932 ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
4933 iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
4934 z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
4935 z_reg_qi = gen_rtx_REG (QImode, HARD_Z_REGNUM);
4937 /* Scan all insns to replace Z by X or Y preserving the old value
4938 of X/Y and restoring it afterward. */
4940 for (insn = first; insn; insn = NEXT_INSN (insn))
4942 rtx body;
4944 if (GET_CODE (insn) == CODE_LABEL
4945 || GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
4946 continue;
4948 if (!INSN_P (insn))
4949 continue;
4951 body = PATTERN (insn);
4952 if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
4953 continue;
4955 if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
4956 || GET_CODE (body) == ASM_OPERANDS
4957 || GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
4958 continue;
4960 if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
4961 || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
4964 /* If Z appears in this insn, replace it in the current insn
4965 and the next ones until the flow changes or we have to
4966 restore back the replacement register. */
4968 if (reg_mentioned_p (z_reg, body))
4970 m68hc11_z_replacement (insn);
4973 else
4975 printf ("insn not handled by Z replacement:\n");
4976 fflush (stdout);
4977 debug_rtx (insn);
4983 /* Machine-dependent reorg pass.
4984 Specific optimizations are defined here:
4985 - this pass changes the Z register into either X or Y
4986 (it preserves X/Y previous values in a memory slot in page0).
4988 When this pass is finished, the global variable
4989 'z_replacement_completed' is set to 2. */
4991 static void
4992 m68hc11_reorg (void)
4994 int split_done = 0;
4995 rtx first;
4997 z_replacement_completed = 0;
4998 z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
4999 first = get_insns ();
5001 /* Some RTX are shared at this point. This breaks the Z register
5002 replacement, unshare everything. */
5003 unshare_all_rtl_again (first);
5005 /* Force a split of all splittable insn. This is necessary for the
5006 Z register replacement mechanism because we end up with basic insns. */
5007 split_all_insns_noflow ();
5008 split_done = 1;
5010 z_replacement_completed = 1;
5011 m68hc11_reassign_regs (first);
5013 if (optimize)
5014 compute_bb_for_insn ();
5016 /* After some splitting, there are some opportunities for CSE pass.
5017 This happens quite often when 32-bit or above patterns are split. */
5018 if (optimize > 0 && split_done)
5020 reload_cse_regs (first);
5023 /* Re-create the REG_DEAD notes. These notes are used in the machine
5024 description to use the best assembly directives. */
5025 if (optimize)
5027 df_note_add_problem ();
5028 df_analyze ();
5029 df_remove_problem (df_note);
5032 z_replacement_completed = 2;
5034 /* If optimizing, then go ahead and split insns that must be
5035 split after Z register replacement. This gives more opportunities
5036 for peephole (in particular for consecutives xgdx/xgdy). */
5037 if (optimize > 0)
5038 split_all_insns_noflow ();
5040 /* Once insns are split after the z_replacement_completed == 2,
5041 we must not re-run the life_analysis. The xgdx/xgdy patterns
5042 are not recognized and the life_analysis pass removes some
5043 insns because it thinks some (SETs) are noops or made to dead
5044 stores (which is false due to the swap).
5046 Do a simple pass to eliminate the noop set that the final
5047 split could generate (because it was easier for split definition). */
5049 rtx insn;
5051 for (insn = first; insn; insn = NEXT_INSN (insn))
5053 rtx body;
5055 if (INSN_DELETED_P (insn))
5056 continue;
5057 if (!INSN_P (insn))
5058 continue;
5060 /* Remove the (set (R) (R)) insns generated by some splits. */
5061 body = PATTERN (insn);
5062 if (GET_CODE (body) == SET
5063 && rtx_equal_p (SET_SRC (body), SET_DEST (body)))
5065 SET_INSN_DELETED (insn);
5066 continue;
5072 /* Override memcpy */
5074 static void
5075 m68hc11_init_libfuncs (void)
5077 memcpy_libfunc = init_one_libfunc ("__memcpy");
5078 memcmp_libfunc = init_one_libfunc ("__memcmp");
5079 memset_libfunc = init_one_libfunc ("__memset");
5084 /* Cost functions. */
5086 /* Cost of moving memory. */
5088 m68hc11_memory_move_cost (enum machine_mode mode, enum reg_class rclass,
5089 int in ATTRIBUTE_UNUSED)
5091 if (rclass <= H_REGS && rclass > NO_REGS)
5093 if (GET_MODE_SIZE (mode) <= 2)
5094 return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
5095 else
5096 return COSTS_N_INSNS (2) + (reload_completed | reload_in_progress);
5098 else
5100 if (GET_MODE_SIZE (mode) <= 2)
5101 return COSTS_N_INSNS (3);
5102 else
5103 return COSTS_N_INSNS (4);
5108 /* Cost of moving data from a register of class 'from' to on in class 'to'.
5109 Reload does not check the constraint of set insns when the two registers
5110 have a move cost of 2. Setting a higher cost will force reload to check
5111 the constraints. */
5113 m68hc11_register_move_cost (enum machine_mode mode, enum reg_class from,
5114 enum reg_class to)
5116 /* All costs are symmetric, so reduce cases by putting the
5117 lower number class as the destination. */
5118 if (from < to)
5120 enum reg_class tmp = to;
5121 to = from, from = tmp;
5123 if (to >= S_REGS)
5124 return m68hc11_memory_move_cost (mode, S_REGS, 0);
5125 else if (from <= S_REGS)
5126 return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
5127 else
5128 return COSTS_N_INSNS (2);
5132 /* Provide the costs of an addressing mode that contains ADDR.
5133 If ADDR is not a valid address, its cost is irrelevant. */
5135 static int
5136 m68hc11_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
5138 int cost = 4;
5140 switch (GET_CODE (addr))
5142 case REG:
5143 /* Make the cost of hard registers and specially SP, FP small. */
5144 if (REGNO (addr) < FIRST_PSEUDO_REGISTER)
5145 cost = 0;
5146 else
5147 cost = 1;
5148 break;
5150 case SYMBOL_REF:
5151 cost = 8;
5152 break;
5154 case LABEL_REF:
5155 case CONST:
5156 cost = 0;
5157 break;
5159 case PLUS:
5161 register rtx plus0 = XEXP (addr, 0);
5162 register rtx plus1 = XEXP (addr, 1);
5164 if (GET_CODE (plus0) != REG)
5165 break;
5167 switch (GET_CODE (plus1))
5169 case CONST_INT:
5170 if (INTVAL (plus1) >= 2 * m68hc11_max_offset
5171 || INTVAL (plus1) < m68hc11_min_offset)
5172 cost = 3;
5173 else if (INTVAL (plus1) >= m68hc11_max_offset)
5174 cost = 2;
5175 else
5176 cost = 1;
5177 if (REGNO (plus0) < FIRST_PSEUDO_REGISTER)
5178 cost += 0;
5179 else
5180 cost += 1;
5181 break;
5183 case SYMBOL_REF:
5184 cost = 8;
5185 break;
5187 case CONST:
5188 case LABEL_REF:
5189 cost = 0;
5190 break;
5192 default:
5193 break;
5195 break;
5197 case PRE_DEC:
5198 case PRE_INC:
5199 if (SP_REG_P (XEXP (addr, 0)))
5200 cost = 1;
5201 break;
5203 default:
5204 break;
5206 if (debug_m6811)
5208 printf ("Address cost: %d for :", cost);
5209 fflush (stdout);
5210 debug_rtx (addr);
5213 return cost;
5216 static int
5217 m68hc11_shift_cost (enum machine_mode mode, rtx x, int shift)
5219 int total;
5221 total = rtx_cost (x, SET, !optimize_size);
5222 if (mode == QImode)
5223 total += m68hc11_cost->shiftQI_const[shift % 8];
5224 else if (mode == HImode)
5225 total += m68hc11_cost->shiftHI_const[shift % 16];
5226 else if (shift == 8 || shift == 16 || shift == 32)
5227 total += m68hc11_cost->shiftHI_const[8];
5228 else if (shift != 0 && shift != 16 && shift != 32)
5230 total += m68hc11_cost->shiftHI_const[1] * shift;
5233 /* For SI and others, the cost is higher. */
5234 if (GET_MODE_SIZE (mode) > 2 && (shift % 16) != 0)
5235 total *= GET_MODE_SIZE (mode) / 2;
5237 /* When optimizing for size, make shift more costly so that
5238 multiplications are preferred. */
5239 if (optimize_size && (shift % 8) != 0)
5240 total *= 2;
5242 return total;
5245 static int
5246 m68hc11_rtx_costs_1 (rtx x, enum rtx_code code,
5247 enum rtx_code outer_code ATTRIBUTE_UNUSED)
5249 enum machine_mode mode = GET_MODE (x);
5250 int extra_cost = 0;
5251 int total;
5253 switch (code)
5255 case ROTATE:
5256 case ROTATERT:
5257 case ASHIFT:
5258 case LSHIFTRT:
5259 case ASHIFTRT:
5260 if (GET_CODE (XEXP (x, 1)) == CONST_INT)
5262 return m68hc11_shift_cost (mode, XEXP (x, 0), INTVAL (XEXP (x, 1)));
5265 total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
5266 total += m68hc11_cost->shift_var;
5267 return total;
5269 case AND:
5270 case XOR:
5271 case IOR:
5272 total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
5273 total += m68hc11_cost->logical;
5275 /* Logical instructions are byte instructions only. */
5276 total *= GET_MODE_SIZE (mode);
5277 return total;
5279 case MINUS:
5280 case PLUS:
5281 total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
5282 total += m68hc11_cost->add;
5283 if (GET_MODE_SIZE (mode) > 2)
5285 total *= GET_MODE_SIZE (mode) / 2;
5287 return total;
5289 case UDIV:
5290 case DIV:
5291 case MOD:
5292 total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
5293 switch (mode)
5295 case QImode:
5296 total += m68hc11_cost->divQI;
5297 break;
5299 case HImode:
5300 total += m68hc11_cost->divHI;
5301 break;
5303 case SImode:
5304 default:
5305 total += m68hc11_cost->divSI;
5306 break;
5308 return total;
5310 case MULT:
5311 /* mul instruction produces 16-bit result. */
5312 if (mode == HImode && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
5313 && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
5314 return m68hc11_cost->multQI
5315 + rtx_cost (XEXP (XEXP (x, 0), 0), code, !optimize_size)
5316 + rtx_cost (XEXP (XEXP (x, 1), 0), code, !optimize_size);
5318 /* emul instruction produces 32-bit result for 68HC12. */
5319 if (TARGET_M6812 && mode == SImode
5320 && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
5321 && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
5322 return m68hc11_cost->multHI
5323 + rtx_cost (XEXP (XEXP (x, 0), 0), code, !optimize_size)
5324 + rtx_cost (XEXP (XEXP (x, 1), 0), code, !optimize_size);
5326 total = rtx_cost (XEXP (x, 0), code, !optimize_size)
5327 + rtx_cost (XEXP (x, 1), code, !optimize_size);
5328 switch (mode)
5330 case QImode:
5331 total += m68hc11_cost->multQI;
5332 break;
5334 case HImode:
5335 total += m68hc11_cost->multHI;
5336 break;
5338 case SImode:
5339 default:
5340 total += m68hc11_cost->multSI;
5341 break;
5343 return total;
5345 case NEG:
5346 case SIGN_EXTEND:
5347 extra_cost = COSTS_N_INSNS (2);
5349 /* Fall through */
5350 case NOT:
5351 case COMPARE:
5352 case ABS:
5353 case ZERO_EXTEND:
5354 case ZERO_EXTRACT:
5355 total = extra_cost + rtx_cost (XEXP (x, 0), code, !optimize_size);
5356 if (mode == QImode)
5358 return total + COSTS_N_INSNS (1);
5360 if (mode == HImode)
5362 return total + COSTS_N_INSNS (2);
5364 if (mode == SImode)
5366 return total + COSTS_N_INSNS (4);
5368 return total + COSTS_N_INSNS (8);
5370 case IF_THEN_ELSE:
5371 if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
5372 return COSTS_N_INSNS (1);
5374 return COSTS_N_INSNS (1);
5376 default:
5377 return COSTS_N_INSNS (4);
5381 static bool
5382 m68hc11_rtx_costs (rtx x, int codearg, int outer_code_arg, int *total,
5383 bool speed ATTRIBUTE_UNUSED)
5385 enum rtx_code code = (enum rtx_code) codearg;
5386 enum rtx_code outer_code = (enum rtx_code) outer_code_arg;
5388 switch (code)
5390 /* Constants are cheap. Moving them in registers must be avoided
5391 because most instructions do not handle two register operands. */
5392 case CONST_INT:
5393 case CONST:
5394 case LABEL_REF:
5395 case SYMBOL_REF:
5396 case CONST_DOUBLE:
5397 /* Logical and arithmetic operations with a constant operand are
5398 better because they are not supported with two registers. */
5399 /* 'clr' is slow */
5400 if (outer_code == SET && x == const0_rtx)
5401 /* After reload, the reload_cse pass checks the cost to change
5402 a SET into a PLUS. Make const0 cheap then. */
5403 *total = 1 - reload_completed;
5404 else
5405 *total = 0;
5406 return true;
5408 case ZERO_EXTRACT:
5409 if (outer_code != COMPARE)
5410 return false;
5412 case ROTATE:
5413 case ROTATERT:
5414 case ASHIFT:
5415 case LSHIFTRT:
5416 case ASHIFTRT:
5417 case MINUS:
5418 case PLUS:
5419 case AND:
5420 case XOR:
5421 case IOR:
5422 case UDIV:
5423 case DIV:
5424 case MOD:
5425 case MULT:
5426 case NEG:
5427 case SIGN_EXTEND:
5428 case NOT:
5429 case COMPARE:
5430 case ZERO_EXTEND:
5431 case IF_THEN_ELSE:
5432 *total = m68hc11_rtx_costs_1 (x, code, outer_code);
5433 return true;
5435 default:
5436 return false;
5441 /* Worker function for TARGET_ASM_FILE_START. */
5443 static void
5444 m68hc11_file_start (void)
5446 default_file_start ();
5448 fprintf (asm_out_file, "\t.mode %s\n", TARGET_SHORT ? "mshort" : "mlong");
5452 /* Worker function for TARGET_ASM_CONSTRUCTOR. */
5454 static void
5455 m68hc11_asm_out_constructor (rtx symbol, int priority)
5457 default_ctor_section_asm_out_constructor (symbol, priority);
5458 fprintf (asm_out_file, "\t.globl\t__do_global_ctors\n");
5461 /* Worker function for TARGET_ASM_DESTRUCTOR. */
5463 static void
5464 m68hc11_asm_out_destructor (rtx symbol, int priority)
5466 default_dtor_section_asm_out_destructor (symbol, priority);
5467 fprintf (asm_out_file, "\t.globl\t__do_global_dtors\n");
5470 /* Worker function for TARGET_STRUCT_VALUE_RTX. */
5472 static rtx
5473 m68hc11_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
5474 int incoming ATTRIBUTE_UNUSED)
5476 return gen_rtx_REG (Pmode, HARD_D_REGNUM);
5479 /* Return true if type TYPE should be returned in memory.
5480 Blocks and data types largers than 4 bytes cannot be returned
5481 in the register (D + X = 4). */
5483 static bool
5484 m68hc11_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
5486 if (TYPE_MODE (type) == BLKmode)
5488 HOST_WIDE_INT size = int_size_in_bytes (type);
5489 return (size == -1 || size > 4);
5491 else
5492 return GET_MODE_SIZE (TYPE_MODE (type)) > 4;
5495 #include "gt-m68hc11.h"