1 /* Subroutines for code generation on Motorola 68HC11 and 68HC12.
2 Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3 Contributed by Stephane Carrez (stcarrez@worldnet.fr)
5 This file is part of GNU CC.
7 GNU CC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU CC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU CC; see the file COPYING. If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
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
42 #include "hard-reg-set.h"
44 #include "insn-config.h"
45 #include "conditions.h"
47 #include "insn-attr.h"
52 #include "basic-block.h"
56 #include "target-def.h"
58 static void print_options
PARAMS ((FILE *));
59 static void emit_move_after_reload
PARAMS ((rtx
, rtx
, rtx
));
60 static rtx simplify_logical
PARAMS ((enum machine_mode
, int, rtx
, rtx
*));
61 static void m68hc11_emit_logical
PARAMS ((enum machine_mode
, int, rtx
*));
62 static int go_if_legitimate_address_internal
PARAMS((rtx
, enum machine_mode
,
64 static int register_indirect_p
PARAMS((rtx
, enum machine_mode
, int));
65 static rtx m68hc11_expand_compare
PARAMS((enum rtx_code
, rtx
, rtx
));
66 static int must_parenthesize
PARAMS ((rtx
));
67 static int m68hc11_shift_cost
PARAMS ((enum machine_mode
, rtx
, int));
68 static int m68hc11_auto_inc_p
PARAMS ((rtx
));
69 static tree m68hc11_handle_fntype_attribute
PARAMS ((tree
*, tree
, tree
, int, bool *));
70 const struct attribute_spec m68hc11_attribute_table
[];
72 void create_regs_rtx
PARAMS ((void));
73 static void m68hc11_add_gc_roots
PARAMS ((void));
75 static void asm_print_register
PARAMS ((FILE *, int));
76 static void m68hc11_output_function_epilogue
PARAMS ((FILE *, HOST_WIDE_INT
));
77 static void m68hc11_asm_out_constructor
PARAMS ((rtx
, int));
78 static void m68hc11_asm_out_destructor
PARAMS ((rtx
, int));
80 rtx m68hc11_soft_tmp_reg
;
82 /* Must be set to 1 to produce debug messages. */
85 extern FILE *asm_out_file
;
93 static int regs_inited
= 0;
96 /* Set to 1 by expand_prologue() when the function is an interrupt handler. */
97 int current_function_interrupt
;
99 /* Set to 1 by expand_prologue() when the function is a trap handler. */
100 int current_function_trap
;
102 /* Min offset that is valid for the indirect addressing mode. */
103 HOST_WIDE_INT m68hc11_min_offset
= 0;
105 /* Max offset that is valid for the indirect addressing mode. */
106 HOST_WIDE_INT m68hc11_max_offset
= 256;
108 /* The class value for base registers. */
109 enum reg_class m68hc11_base_reg_class
= A_REGS
;
111 /* The class value for index registers. This is NO_REGS for 68HC11. */
112 enum reg_class m68hc11_index_reg_class
= NO_REGS
;
114 enum reg_class m68hc11_tmp_regs_class
= NO_REGS
;
116 /* Tables that tell whether a given hard register is valid for
117 a base or an index register. It is filled at init time depending
118 on the target processor. */
119 unsigned char m68hc11_reg_valid_for_base
[FIRST_PSEUDO_REGISTER
];
120 unsigned char m68hc11_reg_valid_for_index
[FIRST_PSEUDO_REGISTER
];
122 /* A correction offset which is applied to the stack pointer.
123 This is 1 for 68HC11 and 0 for 68HC12. */
124 int m68hc11_sp_correction
;
126 /* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns. */
127 rtx m68hc11_compare_op0
;
128 rtx m68hc11_compare_op1
;
131 struct processor_costs
*m68hc11_cost
;
133 /* Costs for a 68HC11. */
134 struct processor_costs m6811_cost
= {
139 /* non-constant shift */
142 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
143 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
144 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
147 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
148 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
149 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
150 COSTS_N_INSNS (2), COSTS_N_INSNS (4),
151 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
152 COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
157 COSTS_N_INSNS (20 * 4),
159 COSTS_N_INSNS (20 * 16),
168 /* Costs for a 68HC12. */
169 struct processor_costs m6812_cost
= {
174 /* non-constant shift */
177 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
178 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
179 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
182 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
183 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
184 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
185 COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
186 COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
187 COSTS_N_INSNS (6), COSTS_N_INSNS (4)
194 COSTS_N_INSNS (3 * 4),
203 /* Machine specific options */
205 const char *m68hc11_regparm_string
;
206 const char *m68hc11_reg_alloc_order
;
207 const char *m68hc11_soft_reg_count
;
209 static int nb_soft_regs
;
211 /* Initialize the GCC target structure. */
212 #undef TARGET_ATTRIBUTE_TABLE
213 #define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
215 #undef TARGET_ASM_FUNCTION_EPILOGUE
216 #define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
218 struct gcc_target targetm
= TARGET_INITIALIZER
;
221 m68hc11_override_options ()
223 m68hc11_add_gc_roots ();
225 memset (m68hc11_reg_valid_for_index
, 0,
226 sizeof (m68hc11_reg_valid_for_index
));
227 memset (m68hc11_reg_valid_for_base
, 0, sizeof (m68hc11_reg_valid_for_base
));
229 /* Compilation with -fpic generates a wrong code. */
232 warning ("-f%s ignored for 68HC11/68HC12 (not supported)",
233 (flag_pic
> 1) ? "PIC" : "pic");
237 /* Configure for a 68hc11 processor. */
240 /* If gcc was built for a 68hc12, invalidate that because
241 a -m68hc11 option was specified on the command line. */
242 if (TARGET_DEFAULT
!= MASK_M6811
)
243 target_flags
&= ~TARGET_DEFAULT
;
245 m68hc11_cost
= &m6811_cost
;
246 m68hc11_min_offset
= 0;
247 m68hc11_max_offset
= 256;
248 m68hc11_index_reg_class
= NO_REGS
;
249 m68hc11_base_reg_class
= A_REGS
;
250 m68hc11_reg_valid_for_base
[HARD_X_REGNUM
] = 1;
251 m68hc11_reg_valid_for_base
[HARD_Y_REGNUM
] = 1;
252 m68hc11_reg_valid_for_base
[HARD_Z_REGNUM
] = 1;
253 m68hc11_sp_correction
= 1;
254 m68hc11_tmp_regs_class
= D_REGS
;
255 if (m68hc11_soft_reg_count
== 0 && !TARGET_M6812
)
256 m68hc11_soft_reg_count
= "4";
259 /* Configure for a 68hc12 processor. */
262 m68hc11_cost
= &m6812_cost
;
263 m68hc11_min_offset
= -65536;
264 m68hc11_max_offset
= 65536;
265 m68hc11_index_reg_class
= D_REGS
;
266 m68hc11_base_reg_class
= A_OR_SP_REGS
;
267 m68hc11_reg_valid_for_base
[HARD_X_REGNUM
] = 1;
268 m68hc11_reg_valid_for_base
[HARD_Y_REGNUM
] = 1;
269 m68hc11_reg_valid_for_base
[HARD_Z_REGNUM
] = 1;
270 m68hc11_reg_valid_for_base
[HARD_SP_REGNUM
] = 1;
271 m68hc11_reg_valid_for_index
[HARD_D_REGNUM
] = 1;
272 m68hc11_sp_correction
= 0;
273 m68hc11_tmp_regs_class
= TMP_REGS
;
274 target_flags
&= ~MASK_M6811
;
275 if (m68hc11_soft_reg_count
== 0)
276 m68hc11_soft_reg_count
= "2";
283 m68hc11_conditional_register_usage ()
286 int cnt
= atoi (m68hc11_soft_reg_count
);
290 if (cnt
> SOFT_REG_LAST
- SOFT_REG_FIRST
)
291 cnt
= SOFT_REG_LAST
- SOFT_REG_FIRST
;
294 for (i
= SOFT_REG_FIRST
+ cnt
; i
< SOFT_REG_LAST
; i
++)
297 call_used_regs
[i
] = 1;
302 /* Reload and register operations. */
304 static const char *const reg_class_names
[] = REG_CLASS_NAMES
;
310 /* regs_inited = 1; */
311 ix_reg
= gen_rtx (REG
, HImode
, HARD_X_REGNUM
);
312 iy_reg
= gen_rtx (REG
, HImode
, HARD_Y_REGNUM
);
313 d_reg
= gen_rtx (REG
, HImode
, HARD_D_REGNUM
);
314 da_reg
= gen_rtx (REG
, QImode
, HARD_A_REGNUM
);
315 m68hc11_soft_tmp_reg
= gen_rtx (REG
, HImode
, SOFT_TMP_REGNUM
);
317 stack_push_word
= gen_rtx (MEM
, HImode
,
318 gen_rtx (PRE_DEC
, HImode
,
319 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
320 stack_pop_word
= gen_rtx (MEM
, HImode
,
321 gen_rtx (POST_INC
, HImode
,
322 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
326 /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
327 - 8 bit values are stored anywhere (except the SP register).
328 - 16 bit values can be stored in any register whose mode is 16
329 - 32 bit values can be stored in D, X registers or in a soft register
330 (except the last one because we need 2 soft registers)
331 - Values whose size is > 32 bit are not stored in real hard
332 registers. They may be stored in soft registers if there are
335 hard_regno_mode_ok (regno
, mode
)
337 enum machine_mode mode
;
339 switch (GET_MODE_SIZE (mode
))
342 return S_REGNO_P (regno
) && nb_soft_regs
>= 4;
345 return X_REGNO_P (regno
) || (S_REGNO_P (regno
) && nb_soft_regs
>= 2);
348 return G_REGNO_P (regno
);
351 /* We have to accept a QImode in X or Y registers. Otherwise, the
352 reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
353 in the insns. Reload fails if the insn rejects the register class 'a'
354 as well as if it accepts it. Patterns that failed were
355 zero_extend_qihi2 and iorqi3. */
357 return G_REGNO_P (regno
) && !SP_REGNO_P (regno
);
365 preferred_reload_class (operand
, class)
367 enum reg_class
class;
369 enum machine_mode mode
;
371 mode
= GET_MODE (operand
);
375 printf ("Preferred reload: (class=%s): ", reg_class_names
[class]);
378 if (class == D_OR_A_OR_S_REGS
&& SP_REG_P (operand
))
379 return m68hc11_base_reg_class
;
381 if (class >= S_REGS
&& (GET_CODE (operand
) == MEM
382 || GET_CODE (operand
) == CONST_INT
))
384 /* S_REGS class must not be used. The movhi template does not
385 work to move a memory to a soft register.
386 Restrict to a hard reg. */
391 case D_OR_A_OR_S_REGS
:
397 case D_OR_SP_OR_S_REGS
:
398 class = D_OR_SP_REGS
;
400 case D_OR_Y_OR_S_REGS
:
403 case D_OR_X_OR_S_REGS
:
419 else if (class == Y_REGS
&& GET_CODE (operand
) == MEM
)
423 else if (class == A_OR_D_REGS
&& GET_MODE_SIZE (mode
) == 4)
427 else if (class >= S_REGS
&& S_REG_P (operand
))
433 case D_OR_A_OR_S_REGS
:
439 case D_OR_SP_OR_S_REGS
:
440 class = D_OR_SP_REGS
;
442 case D_OR_Y_OR_S_REGS
:
445 case D_OR_X_OR_S_REGS
:
461 else if (class >= S_REGS
)
465 printf ("Class = %s for: ", reg_class_names
[class]);
473 printf (" => class=%s\n", reg_class_names
[class]);
481 /* Return 1 if the operand is a valid indexed addressing mode.
482 For 68hc11: n,r with n in [0..255] and r in A_REGS class
483 For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
485 register_indirect_p (operand
, mode
, strict
)
487 enum machine_mode mode
;
492 switch (GET_CODE (operand
))
498 if (TARGET_M6812
&& TARGET_AUTO_INC_DEC
)
499 return register_indirect_p (XEXP (operand
, 0), mode
, strict
);
503 base
= XEXP (operand
, 0);
504 if (GET_CODE (base
) == MEM
)
507 offset
= XEXP (operand
, 1);
508 if (GET_CODE (offset
) == MEM
)
511 if (GET_CODE (base
) == REG
)
513 if (!VALID_CONSTANT_OFFSET_P (offset
, mode
))
519 return REGNO_OK_FOR_BASE_P2 (REGNO (base
), strict
);
521 if (GET_CODE (offset
) == REG
)
523 if (!VALID_CONSTANT_OFFSET_P (base
, mode
))
529 return REGNO_OK_FOR_BASE_P2 (REGNO (offset
), strict
);
534 return REGNO_OK_FOR_BASE_P2 (REGNO (operand
), strict
);
541 /* Returns 1 if the operand fits in a 68HC11 indirect mode or in
542 a 68HC12 1-byte index addressing mode. */
544 m68hc11_small_indexed_indirect_p (operand
, mode
)
546 enum machine_mode mode
;
550 if (GET_CODE (operand
) != MEM
)
553 operand
= XEXP (operand
, 0);
554 if (CONSTANT_ADDRESS_P (operand
))
557 if (PUSH_POP_ADDRESS_P (operand
))
560 if (!register_indirect_p (operand
, mode
,
561 (reload_completed
| reload_in_progress
)))
564 if (TARGET_M6812
&& GET_CODE (operand
) == PLUS
565 && (reload_completed
| reload_in_progress
))
567 base
= XEXP (operand
, 0);
568 offset
= XEXP (operand
, 1);
569 if (GET_CODE (base
) == CONST_INT
)
572 switch (GET_MODE_SIZE (mode
))
575 if (INTVAL (offset
) < -16 + 6 || INTVAL (offset
) > 15 - 6)
580 if (INTVAL (offset
) < -16 + 2 || INTVAL (offset
) > 15 - 2)
585 if (INTVAL (offset
) < -16 || INTVAL (offset
) > 15)
594 m68hc11_register_indirect_p (operand
, mode
)
596 enum machine_mode mode
;
598 if (GET_CODE (operand
) != MEM
)
601 operand
= XEXP (operand
, 0);
602 return register_indirect_p (operand
, mode
,
603 (reload_completed
| reload_in_progress
));
607 go_if_legitimate_address_internal (operand
, mode
, strict
)
609 enum machine_mode mode
;
612 if (CONSTANT_ADDRESS_P (operand
))
614 /* Reject the global variables if they are too wide. This forces
615 a load of their address in a register and generates smaller code. */
616 if (GET_MODE_SIZE (mode
) == 8)
621 if (register_indirect_p (operand
, mode
, strict
))
625 if (PUSH_POP_ADDRESS_P (operand
))
629 if (symbolic_memory_operand (operand
, mode
))
637 m68hc11_go_if_legitimate_address (operand
, mode
, strict
)
639 enum machine_mode mode
;
646 printf ("Checking: ");
651 result
= go_if_legitimate_address_internal (operand
, mode
, strict
);
655 printf (" -> %s\n", result
== 0 ? "NO" : "YES");
662 printf ("go_if_legitimate%s, ret 0: %d:",
663 (strict
? "_strict" : ""), mode
);
672 m68hc11_legitimize_address (operand
, old_operand
, mode
)
673 rtx
*operand ATTRIBUTE_UNUSED
;
674 rtx old_operand ATTRIBUTE_UNUSED
;
675 enum machine_mode mode ATTRIBUTE_UNUSED
;
682 m68hc11_reload_operands (operands
)
685 enum machine_mode mode
;
687 if (regs_inited
== 0)
690 mode
= GET_MODE (operands
[1]);
692 /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
693 if (A_REG_P (operands
[0]) && memory_reload_operand (operands
[1], mode
))
695 rtx big_offset
= XEXP (XEXP (operands
[1], 0), 1);
696 rtx base
= XEXP (XEXP (operands
[1], 0), 0);
698 if (GET_CODE (base
) != REG
)
705 /* If the offset is out of range, we have to compute the address
706 with a separate add instruction. We try to do with with an 8-bit
707 add on the A register. This is possible only if the lowest part
708 of the offset (ie, big_offset % 256) is a valid constant offset
709 with respect to the mode. If it's not, we have to generate a
710 16-bit add on the D register. From:
712 (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
716 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
717 (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
718 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
719 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
721 (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
722 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
725 if (!VALID_CONSTANT_OFFSET_P (big_offset
, mode
))
728 rtx reg
= operands
[0];
730 int val
= INTVAL (big_offset
);
733 /* We use the 'operands[0]' as a scratch register to compute the
734 address. Make sure 'base' is in that register. */
735 if (!rtx_equal_p (base
, operands
[0]))
737 emit_move_insn (reg
, base
);
747 vh
= (val
>> 8) & 0x0FF;
751 /* Create the lowest part offset that still remains to be added.
752 If it's not a valid offset, do a 16-bit add. */
753 offset
= gen_rtx (CONST_INT
, VOIDmode
, vl
);
754 if (!VALID_CONSTANT_OFFSET_P (offset
, mode
))
756 emit_insn (gen_rtx (SET
, VOIDmode
, reg
,
757 gen_rtx (PLUS
, HImode
, reg
, big_offset
)));
762 emit_insn (gen_rtx (SET
, VOIDmode
, reg
,
763 gen_rtx (PLUS
, HImode
, reg
,
765 VOIDmode
, vh
<< 8))));
767 emit_move_insn (operands
[0],
768 gen_rtx (MEM
, GET_MODE (operands
[1]),
769 gen_rtx (PLUS
, Pmode
, reg
, offset
)));
774 /* Use the normal gen_movhi pattern. */
779 m68hc11_emit_libcall (name
, code
, dmode
, smode
, noperands
, operands
)
782 enum machine_mode dmode
;
783 enum machine_mode smode
;
793 libcall
= gen_rtx_SYMBOL_REF (Pmode
, name
);
797 ret
= emit_library_call_value (libcall
, NULL_RTX
, LCT_CONST
,
798 dmode
, 1, operands
[1], smode
);
799 equiv
= gen_rtx (code
, dmode
, operands
[1]);
803 ret
= emit_library_call_value (libcall
, NULL_RTX
,
805 operands
[1], smode
, operands
[2],
807 equiv
= gen_rtx (code
, dmode
, operands
[1], operands
[2]);
814 insns
= get_insns ();
816 emit_libcall_block (insns
, operands
[0], ret
, equiv
);
819 /* Returns true if X is a PRE/POST increment decrement
820 (same as auto_inc_p() in rtlanal.c but do not take into
821 account the stack). */
823 m68hc11_auto_inc_p (x
)
826 return GET_CODE (x
) == PRE_DEC
827 || GET_CODE (x
) == POST_INC
828 || GET_CODE (x
) == POST_DEC
|| GET_CODE (x
) == PRE_INC
;
832 /* Predicates for machine description. */
835 memory_reload_operand (operand
, mode
)
837 enum machine_mode mode ATTRIBUTE_UNUSED
;
839 return GET_CODE (operand
) == MEM
840 && GET_CODE (XEXP (operand
, 0)) == PLUS
841 && ((GET_CODE (XEXP (XEXP (operand
, 0), 0)) == REG
842 && GET_CODE (XEXP (XEXP (operand
, 0), 1)) == CONST_INT
)
843 || (GET_CODE (XEXP (XEXP (operand
, 0), 1)) == REG
844 && GET_CODE (XEXP (XEXP (operand
, 0), 0)) == CONST_INT
));
848 tst_operand (operand
, mode
)
850 enum machine_mode mode
;
852 if (GET_CODE (operand
) == MEM
)
854 rtx addr
= XEXP (operand
, 0);
855 if (m68hc11_auto_inc_p (addr
))
858 return nonimmediate_operand (operand
, mode
);
862 cmp_operand (operand
, mode
)
864 enum machine_mode mode
;
866 if (GET_CODE (operand
) == MEM
)
868 rtx addr
= XEXP (operand
, 0);
869 if (m68hc11_auto_inc_p (addr
))
872 return general_operand (operand
, mode
);
876 non_push_operand (operand
, mode
)
878 enum machine_mode mode
;
880 if (general_operand (operand
, mode
) == 0)
883 if (push_operand (operand
, mode
) == 1)
889 reg_or_some_mem_operand (operand
, mode
)
891 enum machine_mode mode
;
893 if (GET_CODE (operand
) == MEM
)
895 rtx op
= XEXP (operand
, 0);
897 if (symbolic_memory_operand (op
, mode
))
900 if (IS_STACK_PUSH (operand
))
903 if (m68hc11_register_indirect_p (operand
, mode
))
909 return register_operand (operand
, mode
);
913 stack_register_operand (operand
, mode
)
915 enum machine_mode mode ATTRIBUTE_UNUSED
;
917 return SP_REG_P (operand
);
921 d_register_operand (operand
, mode
)
923 enum machine_mode mode ATTRIBUTE_UNUSED
;
925 if (GET_CODE (operand
) == SUBREG
)
926 operand
= XEXP (operand
, 0);
928 return GET_CODE (operand
) == REG
929 && (REGNO (operand
) >= FIRST_PSEUDO_REGISTER
930 || REGNO (operand
) == HARD_D_REGNUM
);
934 hard_addr_reg_operand (operand
, mode
)
936 enum machine_mode mode ATTRIBUTE_UNUSED
;
938 if (GET_CODE (operand
) == SUBREG
)
939 operand
= XEXP (operand
, 0);
941 return GET_CODE (operand
) == REG
942 && (REGNO (operand
) == HARD_X_REGNUM
943 || REGNO (operand
) == HARD_Y_REGNUM
944 || REGNO (operand
) == HARD_Z_REGNUM
);
948 hard_reg_operand (operand
, mode
)
950 enum machine_mode mode ATTRIBUTE_UNUSED
;
952 if (GET_CODE (operand
) == SUBREG
)
953 operand
= XEXP (operand
, 0);
955 return GET_CODE (operand
) == REG
956 && (REGNO (operand
) >= FIRST_PSEUDO_REGISTER
957 || H_REGNO_P (REGNO (operand
)));
961 memory_indexed_operand (operand
, mode
)
963 enum machine_mode mode ATTRIBUTE_UNUSED
;
965 if (GET_CODE (operand
) != MEM
)
968 operand
= XEXP (operand
, 0);
969 if (GET_CODE (operand
) == PLUS
)
971 if (GET_CODE (XEXP (operand
, 0)) == REG
)
972 operand
= XEXP (operand
, 0);
973 else if (GET_CODE (XEXP (operand
, 1)) == REG
)
974 operand
= XEXP (operand
, 1);
976 return GET_CODE (operand
) == REG
977 && (REGNO (operand
) >= FIRST_PSEUDO_REGISTER
978 || A_REGNO_P (REGNO (operand
)));
982 push_pop_operand_p (operand
)
985 if (GET_CODE (operand
) != MEM
)
989 operand
= XEXP (operand
, 0);
990 return PUSH_POP_ADDRESS_P (operand
);
993 /* Returns 1 if OP is either a symbol reference or a sum of a symbol
994 reference and a constant. */
997 symbolic_memory_operand (op
, mode
)
999 enum machine_mode mode
;
1001 switch (GET_CODE (op
))
1008 return ((GET_CODE (XEXP (op
, 0)) == SYMBOL_REF
1009 || GET_CODE (XEXP (op
, 0)) == LABEL_REF
)
1010 && GET_CODE (XEXP (op
, 1)) == CONST_INT
);
1012 /* ??? This clause seems to be irrelevant. */
1014 return GET_MODE (op
) == mode
;
1017 return symbolic_memory_operand (XEXP (op
, 0), mode
)
1018 && symbolic_memory_operand (XEXP (op
, 1), mode
);
1026 m68hc11_logical_operator (op
, mode
)
1028 enum machine_mode mode ATTRIBUTE_UNUSED
;
1030 return GET_CODE (op
) == AND
|| GET_CODE (op
) == IOR
|| GET_CODE (op
) == XOR
;
1034 m68hc11_arith_operator (op
, mode
)
1036 enum machine_mode mode ATTRIBUTE_UNUSED
;
1038 return GET_CODE (op
) == AND
|| GET_CODE (op
) == IOR
|| GET_CODE (op
) == XOR
1039 || GET_CODE (op
) == PLUS
|| GET_CODE (op
) == MINUS
1040 || GET_CODE (op
) == ASHIFT
|| GET_CODE (op
) == ASHIFTRT
1041 || GET_CODE (op
) == LSHIFTRT
|| GET_CODE (op
) == ROTATE
1042 || GET_CODE (op
) == ROTATERT
;
1046 m68hc11_non_shift_operator (op
, mode
)
1048 enum machine_mode mode ATTRIBUTE_UNUSED
;
1050 return GET_CODE (op
) == AND
|| GET_CODE (op
) == IOR
|| GET_CODE (op
) == XOR
1051 || GET_CODE (op
) == PLUS
|| GET_CODE (op
) == MINUS
;
1056 m68hc11_unary_operator (op
, mode
)
1058 enum machine_mode mode ATTRIBUTE_UNUSED
;
1060 return GET_CODE (op
) == NEG
|| GET_CODE (op
) == NOT
1061 || GET_CODE (op
) == SIGN_EXTEND
|| GET_CODE (op
) == ZERO_EXTEND
;
1068 m68hc11_block_profiler (out
, blockno
)
1069 FILE *out ATTRIBUTE_UNUSED
;
1070 int blockno ATTRIBUTE_UNUSED
;
1076 m68hc11_function_block_profiler (out
, block_or_label
)
1077 FILE *out ATTRIBUTE_UNUSED
;
1078 int block_or_label ATTRIBUTE_UNUSED
;
1083 /* Emit the code to build the trampoline used to call a nested function.
1087 ldy #&CXT movw #&CXT,*_.d1
1088 sty *_.d1 jmp FNADDR
1093 m68hc11_initialize_trampoline (tramp
, fnaddr
, cxt
)
1098 char *static_chain_reg
= reg_names
[STATIC_CHAIN_REGNUM
];
1101 if (*static_chain_reg
== '*')
1105 emit_move_insn (gen_rtx_MEM (HImode
, tramp
), GEN_INT (0x18ce));
1106 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 2)), cxt
);
1107 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 4)),
1109 emit_move_insn (gen_rtx_MEM (QImode
, plus_constant (tramp
, 6)),
1110 gen_rtx_CONST (QImode
,
1111 gen_rtx_SYMBOL_REF (Pmode
,
1112 static_chain_reg
)));
1113 emit_move_insn (gen_rtx_MEM (QImode
, plus_constant (tramp
, 7)),
1115 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 8)), fnaddr
);
1119 emit_move_insn (gen_rtx_MEM (HImode
, tramp
), GEN_INT (0x1803));
1120 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 2)), cxt
);
1121 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 4)),
1122 gen_rtx_CONST (HImode
,
1123 gen_rtx_SYMBOL_REF (Pmode
,
1124 static_chain_reg
)));
1125 emit_move_insn (gen_rtx_MEM (QImode
, plus_constant (tramp
, 6)),
1127 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 7)), fnaddr
);
1131 /* Declaration of types. */
1133 const struct attribute_spec m68hc11_attribute_table
[] =
1135 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
1136 { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute
},
1137 { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute
},
1138 { NULL
, 0, 0, false, false, false, NULL
}
1141 /* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
1142 arguments as in struct attribute_spec.handler. */
1144 m68hc11_handle_fntype_attribute (node
, name
, args
, flags
, no_add_attrs
)
1147 tree args ATTRIBUTE_UNUSED
;
1148 int flags ATTRIBUTE_UNUSED
;
1151 if (TREE_CODE (*node
) != FUNCTION_TYPE
1152 && TREE_CODE (*node
) != FIELD_DECL
1153 && TREE_CODE (*node
) != TYPE_DECL
)
1155 warning ("`%s' attribute only applies to functions",
1156 IDENTIFIER_POINTER (name
));
1157 *no_add_attrs
= true;
1163 /* Define this macro if references to a symbol must be treated
1164 differently depending on something about the variable or function
1165 named by the symbol (such as what section it is in).
1167 For the 68HC11, we want to recognize trap handlers so that we
1168 handle calls to traps in a special manner (by issuing the trap).
1169 This information is stored in SYMBOL_REF_FLAG. */
1171 m68hc11_encode_section_info (decl
)
1178 if (TREE_CODE (decl
) != FUNCTION_DECL
)
1181 rtl
= DECL_RTL (decl
);
1183 func_attr
= TYPE_ATTRIBUTES (TREE_TYPE (decl
));
1184 trap_handler
= lookup_attribute ("trap", func_attr
) != NULL_TREE
;
1185 SYMBOL_REF_FLAG (XEXP (rtl
, 0)) = trap_handler
;
1189 /* Argument support functions. */
1191 /* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
1192 Arrays are passed by references and other types by value.
1194 SCz: I tried to pass DImode by reference but it seems that this
1195 does not work very well. */
1197 m68hc11_function_arg_pass_by_reference (cum
, mode
, type
, named
)
1198 const CUMULATIVE_ARGS
*cum ATTRIBUTE_UNUSED
;
1199 enum machine_mode mode ATTRIBUTE_UNUSED
;
1201 int named ATTRIBUTE_UNUSED
;
1203 return ((type
&& TREE_CODE (type
) == ARRAY_TYPE
)
1204 /* Consider complex values as aggregates, so care for TCmode. */
1205 /*|| GET_MODE_SIZE (mode) > 4 SCz, temporary */
1206 /*|| (type && AGGREGATE_TYPE_P (type))) */ );
1210 /* Define the offset between two registers, one to be eliminated, and the
1211 other its replacement, at the start of a routine. */
1213 m68hc11_initial_elimination_offset (from
, to
)
1222 /* For a trap handler, we must take into account the registers which
1223 are pushed on the stack during the trap (except the PC). */
1224 func_attr
= TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl
));
1225 trap_handler
= lookup_attribute ("trap", func_attr
) != NULL_TREE
;
1226 if (trap_handler
&& from
== ARG_POINTER_REGNUM
)
1231 if (from
== ARG_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
1233 /* 2 is for the saved frame.
1234 1 is for the 'sts' correction when creating the frame. */
1235 return get_frame_size () + 2 + m68hc11_sp_correction
+ size
;
1238 if (from
== FRAME_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
1240 return m68hc11_sp_correction
;
1243 /* Push any 2 byte pseudo hard registers that we need to save. */
1244 for (regno
= SOFT_REG_FIRST
; regno
< SOFT_REG_LAST
; regno
++)
1246 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1252 if (from
== ARG_POINTER_REGNUM
&& to
== HARD_SP_REGNUM
)
1254 return get_frame_size () + size
;
1257 if (from
== FRAME_POINTER_REGNUM
&& to
== HARD_SP_REGNUM
)
1264 /* Initialize a variable CUM of type CUMULATIVE_ARGS
1265 for a call to a function whose data type is FNTYPE.
1266 For a library call, FNTYPE is 0. */
1269 m68hc11_init_cumulative_args (cum
, fntype
, libname
)
1270 CUMULATIVE_ARGS
*cum
;
1276 z_replacement_completed
= 0;
1280 /* For a library call, we must find out the type of the return value.
1281 When the return value is bigger than 4 bytes, it is returned in
1282 memory. In that case, the first argument of the library call is a
1283 pointer to the memory location. Because the first argument is passed in
1284 register D, we have to identify this, so that the first function
1285 parameter is not passed in D either. */
1291 if (libname
== 0 || GET_CODE (libname
) != SYMBOL_REF
)
1294 /* If the library ends in 'di' or in 'df', we assume it's
1295 returning some DImode or some DFmode which are 64-bit wide. */
1296 name
= XSTR (libname
, 0);
1297 len
= strlen (name
);
1299 && ((name
[len
- 2] == 'd'
1300 && (name
[len
- 1] == 'f' || name
[len
- 1] == 'i'))
1301 || (name
[len
- 3] == 'd'
1302 && (name
[len
- 2] == 'i' || name
[len
- 2] == 'f'))))
1304 /* We are in. Mark the first parameter register as already used. */
1311 ret_type
= TREE_TYPE (fntype
);
1313 if (ret_type
&& aggregate_value_p (ret_type
))
1320 /* Update the data in CUM to advance over an argument
1321 of mode MODE and data type TYPE.
1322 (TYPE is null for libcalls where that information may not be available.) */
1325 m68hc11_function_arg_advance (cum
, mode
, type
, named
)
1326 CUMULATIVE_ARGS
*cum
;
1327 enum machine_mode mode
;
1329 int named ATTRIBUTE_UNUSED
;
1331 if (mode
!= BLKmode
)
1333 if (cum
->words
== 0 && GET_MODE_SIZE (mode
) == 4)
1336 cum
->words
= GET_MODE_SIZE (mode
);
1340 cum
->words
+= GET_MODE_SIZE (mode
);
1341 if (cum
->words
<= HARD_REG_SIZE
)
1347 cum
->words
+= int_size_in_bytes (type
);
1352 /* Define where to put the arguments to a function.
1353 Value is zero to push the argument on the stack,
1354 or a hard register in which to store the argument.
1356 MODE is the argument's machine mode.
1357 TYPE is the data type of the argument (as a tree).
1358 This is null for libcalls where that information may
1360 CUM is a variable of type CUMULATIVE_ARGS which gives info about
1361 the preceding args and about the function being called.
1362 NAMED is nonzero if this argument is a named parameter
1363 (otherwise it is an extra parameter matching an ellipsis). */
1366 m68hc11_function_arg (cum
, mode
, type
, named
)
1367 const CUMULATIVE_ARGS
*cum
;
1368 enum machine_mode mode
;
1369 tree type ATTRIBUTE_UNUSED
;
1370 int named ATTRIBUTE_UNUSED
;
1372 if (cum
->words
!= 0)
1377 if (mode
!= BLKmode
)
1379 if (GET_MODE_SIZE (mode
) == 2 * HARD_REG_SIZE
)
1380 return gen_rtx (REG
, mode
, HARD_X_REGNUM
);
1382 if (GET_MODE_SIZE (mode
) > HARD_REG_SIZE
)
1386 return gen_rtx (REG
, mode
, HARD_D_REGNUM
);
1391 /* The "standard" implementation of va_start: just assign `nextarg' to
1394 m68hc11_expand_builtin_va_start (stdarg_p
, valist
, nextarg
)
1395 int stdarg_p ATTRIBUTE_UNUSED
;
1401 /* SCz: the default implementation in builtins.c adjust the
1402 nextarg using UNITS_PER_WORD. This works only with -mshort
1403 and fails when integers are 32-bit. Here is the correct way. */
1405 nextarg
= plus_constant (nextarg
, -INT_TYPE_SIZE
/ 8);
1407 t
= build (MODIFY_EXPR
, TREE_TYPE (valist
), valist
,
1408 make_tree (ptr_type_node
, nextarg
));
1409 TREE_SIDE_EFFECTS (t
) = 1;
1411 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1415 m68hc11_va_arg (valist
, type
)
1420 HOST_WIDE_INT align
;
1421 HOST_WIDE_INT rounded_size
;
1425 /* Compute the rounded size of the type. */
1426 align
= PARM_BOUNDARY
/ BITS_PER_UNIT
;
1427 rounded_size
= (((int_size_in_bytes (type
) + align
- 1) / align
) * align
);
1431 pad_direction
= m68hc11_function_arg_padding (TYPE_MODE (type
), type
);
1433 if (pad_direction
== downward
)
1435 /* Small args are padded downward. */
1438 adj
= TREE_INT_CST_LOW (TYPE_SIZE (type
)) / BITS_PER_UNIT
;
1439 if (rounded_size
> align
)
1442 addr_tree
= build (PLUS_EXPR
, TREE_TYPE (addr_tree
), addr_tree
,
1443 build_int_2 (rounded_size
- adj
, 0));
1446 addr
= expand_expr (addr_tree
, NULL_RTX
, Pmode
, EXPAND_NORMAL
);
1447 addr
= copy_to_reg (addr
);
1449 /* Compute new value for AP. */
1450 t
= build (MODIFY_EXPR
, TREE_TYPE (valist
), valist
,
1451 build (PLUS_EXPR
, TREE_TYPE (valist
), valist
,
1452 build_int_2 (rounded_size
, 0)));
1453 TREE_SIDE_EFFECTS (t
) = 1;
1454 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1459 /* If defined, a C expression which determines whether, and in which direction,
1460 to pad out an argument with extra space. The value should be of type
1461 `enum direction': either `upward' to pad above the argument,
1462 `downward' to pad below, or `none' to inhibit padding.
1464 Structures are stored left shifted in their argument slot. */
1466 m68hc11_function_arg_padding (mode
, type
)
1467 enum machine_mode mode
;
1470 if (type
!= 0 && AGGREGATE_TYPE_P (type
))
1473 /* This is the default definition. */
1474 return (!BYTES_BIG_ENDIAN
1477 ? (type
&& TREE_CODE (TYPE_SIZE (type
)) == INTEGER_CST
1478 && int_size_in_bytes (type
) <
1479 (PARM_BOUNDARY
/ BITS_PER_UNIT
)) : GET_MODE_BITSIZE (mode
) <
1480 PARM_BOUNDARY
) ? downward
: upward
));
1484 /* Function prologue and epilogue. */
1486 /* Emit a move after the reload pass has completed. This is used to
1487 emit the prologue and epilogue. */
1489 emit_move_after_reload (to
, from
, scratch
)
1490 rtx to
, from
, scratch
;
1494 if (TARGET_M6812
|| H_REG_P (to
) || H_REG_P (from
))
1496 insn
= emit_move_insn (to
, from
);
1500 emit_move_insn (scratch
, from
);
1501 insn
= emit_move_insn (to
, scratch
);
1504 /* Put a REG_INC note to tell the flow analysis that the instruction
1506 if (IS_STACK_PUSH (to
))
1508 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_INC
,
1509 XEXP (XEXP (to
, 0), 0),
1512 else if (IS_STACK_POP (from
))
1514 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_INC
,
1515 XEXP (XEXP (from
, 0), 0),
1521 m68hc11_total_frame_size ()
1526 size
= get_frame_size ();
1527 if (current_function_interrupt
)
1529 size
+= 3 * HARD_REG_SIZE
;
1531 if (frame_pointer_needed
)
1532 size
+= HARD_REG_SIZE
;
1534 for (regno
= SOFT_REG_FIRST
; regno
<= SOFT_REG_LAST
; regno
++)
1535 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1536 size
+= HARD_REG_SIZE
;
1542 m68hc11_output_function_epilogue (out
, size
)
1543 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;
1559 if (reload_completed
!= 1)
1562 size
= get_frame_size ();
1566 /* Generate specific prologue for interrupt handlers. */
1567 func_attr
= TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl
));
1568 current_function_interrupt
= lookup_attribute ("interrupt",
1569 func_attr
) != NULL_TREE
;
1570 current_function_trap
= lookup_attribute ("trap", func_attr
) != NULL_TREE
;
1572 /* Get the scratch register to build the frame and push registers.
1573 If the first argument is a 32-bit quantity, the D+X registers
1574 are used. Use Y to compute the frame. Otherwise, X is cheaper.
1575 For 68HC12, this scratch register is not used. */
1576 if (current_function_args_info
.nregs
== 2)
1581 /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
1582 Other soft registers in page0 need not to be saved because they
1583 will be restored by C functions. For a trap handler, we don't
1584 need to preserve these registers because this is a synchronous call. */
1585 if (current_function_interrupt
)
1587 emit_move_after_reload (stack_push_word
, m68hc11_soft_tmp_reg
, scratch
);
1588 emit_move_after_reload (stack_push_word
,
1589 gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
), scratch
);
1590 emit_move_after_reload (stack_push_word
,
1591 gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
),
1595 /* Save current stack frame. */
1596 if (frame_pointer_needed
)
1597 emit_move_after_reload (stack_push_word
, hard_frame_pointer_rtx
, scratch
);
1599 /* Allocate local variables. */
1600 if (TARGET_M6812
&& size
>= 2)
1602 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1603 stack_pointer_rtx
, GEN_INT (-size
)));
1609 insn
= gen_rtx_PARALLEL
1612 gen_rtx_SET (VOIDmode
,
1614 gen_rtx_PLUS (HImode
,
1617 gen_rtx_CLOBBER (VOIDmode
, scratch
)));
1624 /* Allocate by pushing scratch values. */
1625 for (i
= 2; i
<= size
; i
+= 2)
1626 emit_move_after_reload (stack_push_word
, ix_reg
, 0);
1629 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1630 stack_pointer_rtx
, GEN_INT (-1)));
1633 /* Create the frame pointer. */
1634 if (frame_pointer_needed
)
1635 emit_move_after_reload (hard_frame_pointer_rtx
,
1636 stack_pointer_rtx
, scratch
);
1638 /* Push any 2 byte pseudo hard registers that we need to save. */
1639 for (regno
= SOFT_REG_FIRST
; regno
<= SOFT_REG_LAST
; regno
++)
1641 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1643 emit_move_after_reload (stack_push_word
,
1644 gen_rtx (REG
, HImode
, regno
), scratch
);
1657 if (reload_completed
!= 1)
1660 size
= get_frame_size ();
1662 /* If we are returning a value in two registers, we have to preserve the
1663 X register and use the Y register to restore the stack and the saved
1664 registers. Otherwise, use X because it's faster (and smaller). */
1665 if (current_function_return_rtx
== 0)
1667 else if (GET_CODE (current_function_return_rtx
) == MEM
)
1668 return_size
= HARD_REG_SIZE
;
1670 return_size
= GET_MODE_SIZE (GET_MODE (current_function_return_rtx
));
1672 if (return_size
> HARD_REG_SIZE
)
1677 /* Pop any 2 byte pseudo hard registers that we saved. */
1678 for (regno
= SOFT_REG_LAST
; regno
>= SOFT_REG_FIRST
; regno
--)
1680 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1682 emit_move_after_reload (gen_rtx (REG
, HImode
, regno
),
1683 stack_pop_word
, scratch
);
1687 /* de-allocate auto variables */
1688 if (TARGET_M6812
&& size
>= 2)
1690 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1691 stack_pointer_rtx
, GEN_INT (size
)));
1697 insn
= gen_rtx_PARALLEL
1700 gen_rtx_SET (VOIDmode
,
1702 gen_rtx_PLUS (HImode
,
1705 gen_rtx_CLOBBER (VOIDmode
, scratch
)));
1712 for (i
= 2; i
<= size
; i
+= 2)
1713 emit_move_after_reload (scratch
, stack_pop_word
, scratch
);
1715 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1716 stack_pointer_rtx
, GEN_INT (1)));
1719 /* Restore previous frame pointer. */
1720 if (frame_pointer_needed
)
1721 emit_move_after_reload (hard_frame_pointer_rtx
, stack_pop_word
, scratch
);
1723 /* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
1724 if (current_function_interrupt
)
1726 emit_move_after_reload (gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
),
1727 stack_pop_word
, scratch
);
1728 emit_move_after_reload (gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
),
1729 stack_pop_word
, scratch
);
1730 emit_move_after_reload (m68hc11_soft_tmp_reg
, stack_pop_word
, scratch
);
1733 /* If the trap handler returns some value, copy the value
1734 in D, X onto the stack so that the rti will pop the return value
1736 else if (current_function_trap
&& return_size
!= 0)
1738 rtx addr_reg
= stack_pointer_rtx
;
1742 emit_move_after_reload (scratch
, stack_pointer_rtx
, 0);
1745 emit_move_after_reload (gen_rtx (MEM
, HImode
,
1746 gen_rtx (PLUS
, HImode
, addr_reg
,
1747 GEN_INT (1))), d_reg
, 0);
1748 if (return_size
> HARD_REG_SIZE
)
1749 emit_move_after_reload (gen_rtx (MEM
, HImode
,
1750 gen_rtx (PLUS
, HImode
, addr_reg
,
1751 GEN_INT (3))), ix_reg
, 0);
1754 emit_jump_insn (gen_return ());
1758 /* Low and High part extraction for 68HC11. These routines are
1759 similar to gen_lowpart and gen_highpart but they have been
1760 fixed to work for constants and 68HC11 specific registers. */
1763 m68hc11_gen_lowpart (mode
, x
)
1764 enum machine_mode mode
;
1767 /* We assume that the low part of an auto-inc mode is the same with
1768 the mode changed and that the caller split the larger mode in the
1770 if (GET_CODE (x
) == MEM
&& m68hc11_auto_inc_p (XEXP (x
, 0)))
1772 return gen_rtx (MEM
, mode
, XEXP (x
, 0));
1775 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1776 floating-point constant. A CONST_DOUBLE is used whenever the
1777 constant requires more than one word in order to be adequately
1779 if (GET_CODE (x
) == CONST_DOUBLE
)
1783 if (GET_MODE_CLASS (GET_MODE (x
)) == MODE_FLOAT
)
1787 if (GET_MODE (x
) == SFmode
)
1789 REAL_VALUE_FROM_CONST_DOUBLE (r
, x
);
1790 REAL_VALUE_TO_TARGET_SINGLE (r
, l
[0]);
1796 split_double (x
, &first
, &second
);
1800 return gen_rtx (CONST_INT
, VOIDmode
, l
[0]);
1802 return gen_rtx (CONST_INT
, VOIDmode
,
1803 trunc_int_for_mode (l
[0], HImode
));
1807 l
[0] = CONST_DOUBLE_LOW (x
);
1810 return gen_rtx (CONST_INT
, VOIDmode
, l
[0]);
1811 else if (mode
== HImode
&& GET_MODE (x
) == SFmode
)
1812 return gen_rtx (CONST_INT
, VOIDmode
,
1813 trunc_int_for_mode (l
[0], HImode
));
1818 if (mode
== QImode
&& D_REG_P (x
))
1819 return gen_rtx (REG
, mode
, HARD_B_REGNUM
);
1821 /* gen_lowpart crashes when it is called with a SUBREG. */
1822 if (GET_CODE (x
) == SUBREG
&& SUBREG_BYTE (x
) != 0)
1825 return gen_rtx_SUBREG (mode
, SUBREG_REG (x
), SUBREG_BYTE (x
) + 4);
1826 else if (mode
== HImode
)
1827 return gen_rtx_SUBREG (mode
, SUBREG_REG (x
), SUBREG_BYTE (x
) + 2);
1831 x
= gen_lowpart (mode
, x
);
1833 /* Return a different rtx to avoid to share it in several insns
1834 (when used by a split pattern). Sharing addresses within
1835 a MEM breaks the Z register replacement (and reloading). */
1836 if (GET_CODE (x
) == MEM
)
1842 m68hc11_gen_highpart (mode
, x
)
1843 enum machine_mode mode
;
1846 /* We assume that the high part of an auto-inc mode is the same with
1847 the mode changed and that the caller split the larger mode in the
1849 if (GET_CODE (x
) == MEM
&& m68hc11_auto_inc_p (XEXP (x
, 0)))
1851 return gen_rtx (MEM
, mode
, XEXP (x
, 0));
1854 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1855 floating-point constant. A CONST_DOUBLE is used whenever the
1856 constant requires more than one word in order to be adequately
1858 if (GET_CODE (x
) == CONST_DOUBLE
)
1862 if (GET_MODE_CLASS (GET_MODE (x
)) == MODE_FLOAT
)
1866 if (GET_MODE (x
) == SFmode
)
1868 REAL_VALUE_FROM_CONST_DOUBLE (r
, x
);
1869 REAL_VALUE_TO_TARGET_SINGLE (r
, l
[1]);
1875 split_double (x
, &first
, &second
);
1879 return gen_rtx (CONST_INT
, VOIDmode
, l
[1]);
1881 return gen_rtx (CONST_INT
, VOIDmode
,
1882 trunc_int_for_mode ((l
[1] >> 16), HImode
));
1886 l
[1] = CONST_DOUBLE_HIGH (x
);
1890 return gen_rtx (CONST_INT
, VOIDmode
, l
[1]);
1891 else if (mode
== HImode
&& GET_MODE_CLASS (GET_MODE (x
)) == MODE_FLOAT
)
1892 return gen_rtx (CONST_INT
, VOIDmode
,
1893 trunc_int_for_mode ((l
[0] >> 16), HImode
));
1897 if (GET_CODE (x
) == CONST_INT
)
1899 HOST_WIDE_INT val
= INTVAL (x
);
1903 return gen_rtx (CONST_INT
, VOIDmode
,
1904 trunc_int_for_mode (val
>> 8, QImode
));
1906 else if (mode
== HImode
)
1908 return gen_rtx (CONST_INT
, VOIDmode
,
1909 trunc_int_for_mode (val
>> 16, HImode
));
1912 if (mode
== QImode
&& D_REG_P (x
))
1913 return gen_rtx (REG
, mode
, HARD_A_REGNUM
);
1915 /* There is no way in GCC to represent the upper part of a word register.
1916 To obtain the 8-bit upper part of a soft register, we change the
1917 reg into a mem rtx. This is possible because they are physically
1918 located in memory. There is no offset because we are big-endian. */
1919 if (mode
== QImode
&& S_REG_P (x
))
1923 /* For 68HC12, avoid the '*' for direct addressing mode. */
1924 pos
= TARGET_M6812
? 1 : 0;
1925 return gen_rtx (MEM
, QImode
,
1926 gen_rtx (SYMBOL_REF
, Pmode
,
1927 ®_names
[REGNO (x
)][pos
]));
1930 /* gen_highpart crashes when it is called with a SUBREG. */
1931 if (GET_CODE (x
) == SUBREG
)
1933 return gen_rtx (SUBREG
, mode
, XEXP (x
, 0), XEXP (x
, 1));
1935 if (GET_CODE (x
) == REG
)
1937 if (REGNO (x
) < FIRST_PSEUDO_REGISTER
)
1938 return gen_rtx (REG
, mode
, REGNO (x
));
1940 return gen_rtx_SUBREG (mode
, x
, 0);
1943 if (GET_CODE (x
) == MEM
)
1945 x
= change_address (x
, mode
, 0);
1947 /* Return a different rtx to avoid to share it in several insns
1948 (when used by a split pattern). Sharing addresses within
1949 a MEM breaks the Z register replacement (and reloading). */
1950 if (GET_CODE (x
) == MEM
)
1958 /* Obscure register manipulation. */
1960 /* Finds backward in the instructions to see if register 'reg' is
1961 dead. This is used when generating code to see if we can use 'reg'
1962 as a scratch register. This allows us to choose a better generation
1963 of code when we know that some register dies or can be clobbered. */
1966 dead_register_here (x
, reg
)
1974 x_reg
= gen_rtx (REG
, SImode
, HARD_X_REGNUM
);
1978 for (p
= PREV_INSN (x
); p
&& GET_CODE (p
) != CODE_LABEL
; p
= PREV_INSN (p
))
1979 if (GET_RTX_CLASS (GET_CODE (p
)) == 'i')
1985 if (GET_CODE (body
) == CALL_INSN
)
1987 if (GET_CODE (body
) == JUMP_INSN
)
1990 if (GET_CODE (body
) == SET
)
1992 rtx dst
= XEXP (body
, 0);
1994 if (GET_CODE (dst
) == REG
&& REGNO (dst
) == REGNO (reg
))
1996 if (x_reg
&& rtx_equal_p (dst
, x_reg
))
1999 if (find_regno_note (p
, REG_DEAD
, REGNO (reg
)))
2002 else if (reg_mentioned_p (reg
, p
)
2003 || (x_reg
&& reg_mentioned_p (x_reg
, p
)))
2007 /* Scan forward to see if the register is set in some insns and never
2009 for (p
= x
/*NEXT_INSN (x) */ ; p
; p
= NEXT_INSN (p
))
2013 if (GET_CODE (p
) == CODE_LABEL
2014 || GET_CODE (p
) == JUMP_INSN
2015 || GET_CODE (p
) == CALL_INSN
|| GET_CODE (p
) == BARRIER
)
2018 if (GET_CODE (p
) != INSN
)
2022 if (GET_CODE (body
) == SET
)
2024 rtx src
= XEXP (body
, 1);
2025 rtx dst
= XEXP (body
, 0);
2027 if (GET_CODE (dst
) == REG
2028 && REGNO (dst
) == REGNO (reg
) && !reg_mentioned_p (reg
, src
))
2032 /* Register is used (may be in source or in dest). */
2033 if (reg_mentioned_p (reg
, p
)
2034 || (x_reg
!= 0 && GET_MODE (p
) == SImode
2035 && reg_mentioned_p (x_reg
, p
)))
2038 return p
== 0 ? 1 : 0;
2042 /* Code generation operations called from machine description file. */
2044 /* Print the name of register 'regno' in the assembly file. */
2046 asm_print_register (file
, regno
)
2050 const char *name
= reg_names
[regno
];
2052 if (TARGET_M6812
&& name
[0] == '*')
2055 asm_fprintf (file
, "%s", name
);
2058 /* A C compound statement to output to stdio stream STREAM the
2059 assembler syntax for an instruction operand X. X is an RTL
2062 CODE is a value that can be used to specify one of several ways
2063 of printing the operand. It is used when identical operands
2064 must be printed differently depending on the context. CODE
2065 comes from the `%' specification that was used to request
2066 printing of the operand. If the specification was just `%DIGIT'
2067 then CODE is 0; if the specification was `%LTR DIGIT' then CODE
2068 is the ASCII code for LTR.
2070 If X is a register, this macro should print the register's name.
2071 The names can be found in an array `reg_names' whose type is
2072 `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
2074 When the machine description has a specification `%PUNCT' (a `%'
2075 followed by a punctuation character), this macro is called with
2076 a null pointer for X and the punctuation character for CODE.
2078 The M68HC11 specific codes are:
2080 'b' for the low part of the operand.
2081 'h' for the high part of the operand
2082 The 'b' or 'h' modifiers have no effect if the operand has
2083 the QImode and is not a S_REG_P (soft register). If the
2084 operand is a hard register, these two modifiers have no effect.
2085 't' generate the temporary scratch register. The operand is
2087 'T' generate the low-part temporary scratch register. The operand is
2091 print_operand (file
, op
, letter
)
2098 asm_print_register (file
, SOFT_TMP_REGNUM
);
2101 else if (letter
== 'T')
2103 asm_print_register (file
, SOFT_TMP_REGNUM
);
2104 asm_fprintf (file
, "+1");
2107 else if (letter
== '#')
2109 asm_fprintf (file
, "%0I");
2112 if (GET_CODE (op
) == REG
)
2114 if (letter
== 'b' && S_REG_P (op
))
2116 asm_print_register (file
, REGNO (op
));
2117 asm_fprintf (file
, "+1");
2121 asm_print_register (file
, REGNO (op
));
2126 if (GET_CODE (op
) == SYMBOL_REF
&& (letter
== 'b' || letter
== 'h'))
2129 asm_fprintf (file
, "%0I%%lo(");
2131 asm_fprintf (file
, "%0I%%hi(");
2133 output_addr_const (file
, op
);
2134 asm_fprintf (file
, ")");
2138 /* Get the low or high part of the operand when 'b' or 'h' modifiers
2139 are specified. If we already have a QImode, there is nothing to do. */
2140 if (GET_MODE (op
) == HImode
|| GET_MODE (op
) == VOIDmode
)
2144 op
= m68hc11_gen_lowpart (QImode
, op
);
2146 else if (letter
== 'h')
2148 op
= m68hc11_gen_highpart (QImode
, op
);
2152 if (GET_CODE (op
) == MEM
)
2154 rtx base
= XEXP (op
, 0);
2155 switch (GET_CODE (base
))
2160 asm_fprintf (file
, "%u,-", GET_MODE_SIZE (GET_MODE (op
)));
2161 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2170 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (op
)));
2171 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2172 asm_fprintf (file
, "-");
2181 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (op
)));
2182 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2183 asm_fprintf (file
, "+");
2192 asm_fprintf (file
, "%u,+", GET_MODE_SIZE (GET_MODE (op
)));
2193 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2200 output_address (base
);
2204 else if (GET_CODE (op
) == CONST_DOUBLE
&& GET_MODE (op
) == SFmode
)
2207 REAL_VALUE_FROM_CONST_DOUBLE (r
, op
);
2208 ASM_OUTPUT_FLOAT_OPERAND (letter
, file
, r
);
2210 else if (GET_CODE (op
) == CONST_DOUBLE
&& GET_MODE (op
) == XFmode
)
2213 REAL_VALUE_FROM_CONST_DOUBLE (r
, op
);
2214 ASM_OUTPUT_LONG_DOUBLE_OPERAND (file
, r
);
2216 else if (GET_CODE (op
) == CONST_DOUBLE
&& GET_MODE (op
) == DFmode
)
2219 REAL_VALUE_FROM_CONST_DOUBLE (r
, op
);
2220 ASM_OUTPUT_DOUBLE_OPERAND (file
, r
);
2224 int need_parenthesize
= 0;
2227 asm_fprintf (file
, "%0I");
2229 need_parenthesize
= must_parenthesize (op
);
2231 if (need_parenthesize
)
2232 asm_fprintf (file
, "(");
2234 output_addr_const (file
, op
);
2235 if (need_parenthesize
)
2236 asm_fprintf (file
, ")");
2240 /* Returns true if the operand 'op' must be printed with parenthesis
2241 arround it. This must be done only if there is a symbol whose name
2242 is a processor register. */
2244 must_parenthesize (op
)
2249 switch (GET_CODE (op
))
2252 name
= XSTR (op
, 0);
2253 /* Avoid a conflict between symbol name and a possible
2255 return (strcasecmp (name
, "a") == 0
2256 || strcasecmp (name
, "b") == 0
2257 || strcasecmp (name
, "d") == 0
2258 || strcasecmp (name
, "x") == 0
2259 || strcasecmp (name
, "y") == 0
2260 || strcasecmp (name
, "pc") == 0
2261 || strcasecmp (name
, "sp") == 0
2262 || strcasecmp (name
, "ccr") == 0) ? 1 : 0;
2266 return must_parenthesize (XEXP (op
, 0))
2267 || must_parenthesize (XEXP (op
, 1));
2273 return must_parenthesize (XEXP (op
, 0));
2284 /* A C compound statement to output to stdio stream STREAM the
2285 assembler syntax for an instruction operand that is a memory
2286 reference whose address is ADDR. ADDR is an RTL expression. */
2289 print_operand_address (file
, addr
)
2295 int need_parenthesis
= 0;
2297 switch (GET_CODE (addr
))
2300 if (!REG_P (addr
) || !REG_OK_FOR_BASE_STRICT_P (addr
))
2303 asm_fprintf (file
, "0,");
2304 asm_print_register (file
, REGNO (addr
));
2308 base
= XEXP (addr
, 0);
2309 switch (GET_CODE (base
))
2314 asm_fprintf (file
, "%u,-", GET_MODE_SIZE (GET_MODE (addr
)));
2315 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2324 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (addr
)));
2325 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2326 asm_fprintf (file
, "-");
2335 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (addr
)));
2336 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2337 asm_fprintf (file
, "+");
2346 asm_fprintf (file
, "%u,+", GET_MODE_SIZE (GET_MODE (addr
)));
2347 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2354 need_parenthesis
= must_parenthesize (base
);
2355 if (need_parenthesis
)
2356 asm_fprintf (file
, "(");
2358 output_addr_const (file
, base
);
2359 if (need_parenthesis
)
2360 asm_fprintf (file
, ")");
2366 base
= XEXP (addr
, 0);
2367 offset
= XEXP (addr
, 1);
2368 if (!G_REG_P (base
) && G_REG_P (offset
))
2370 base
= XEXP (addr
, 1);
2371 offset
= XEXP (addr
, 0);
2373 if ((CONSTANT_ADDRESS_P (base
)) && (CONSTANT_ADDRESS_P (offset
)))
2375 need_parenthesis
= must_parenthesize (addr
);
2377 if (need_parenthesis
)
2378 asm_fprintf (file
, "(");
2380 output_addr_const (file
, base
);
2381 asm_fprintf (file
, "+");
2382 output_addr_const (file
, offset
);
2383 if (need_parenthesis
)
2384 asm_fprintf (file
, ")");
2386 else if (REG_P (base
) && REG_OK_FOR_BASE_STRICT_P (base
))
2392 asm_print_register (file
, REGNO (offset
));
2393 asm_fprintf (file
, ",");
2394 asm_print_register (file
, REGNO (base
));
2401 output_addr_const (file
, offset
);
2402 asm_fprintf (file
, ",");
2403 asm_print_register (file
, REGNO (base
));
2413 if (GET_CODE (addr
) == CONST_INT
2414 && INTVAL (addr
) < 0x8000 && INTVAL (addr
) >= -0x8000)
2416 asm_fprintf (file
, "%d", INTVAL (addr
));
2420 need_parenthesis
= must_parenthesize (addr
);
2421 if (need_parenthesis
)
2422 asm_fprintf (file
, "(");
2424 output_addr_const (file
, addr
);
2425 if (need_parenthesis
)
2426 asm_fprintf (file
, ")");
2433 /* Splitting of some instructions. */
2436 m68hc11_expand_compare (code
, op0
, op1
)
2442 if (GET_MODE_CLASS (GET_MODE (op0
)) == MODE_FLOAT
)
2446 emit_insn (gen_rtx_SET (VOIDmode
, cc0_rtx
,
2447 gen_rtx_COMPARE (VOIDmode
, op0
, op1
)));
2448 ret
= gen_rtx (code
, VOIDmode
, cc0_rtx
, const0_rtx
);
2455 m68hc11_expand_compare_and_branch (code
, op0
, op1
, label
)
2457 rtx op0
, op1
, label
;
2461 switch (GET_MODE (op0
))
2465 tmp
= m68hc11_expand_compare (code
, op0
, op1
);
2466 tmp
= gen_rtx_IF_THEN_ELSE (VOIDmode
, tmp
,
2467 gen_rtx_LABEL_REF (VOIDmode
, label
),
2469 emit_jump_insn (gen_rtx_SET (VOIDmode
, pc_rtx
, tmp
));
2473 /* SCz: from i386.c */
2476 /* Don't expand the comparison early, so that we get better code
2477 when jump or whoever decides to reverse the comparison. */
2482 code
= m68hc11_prepare_fp_compare_args (code
, &m68hc11_compare_op0
,
2483 &m68hc11_compare_op1
);
2485 tmp
= gen_rtx_fmt_ee (code
, m68hc11_fp_compare_mode (code
),
2486 m68hc11_compare_op0
, m68hc11_compare_op1
);
2487 tmp
= gen_rtx_IF_THEN_ELSE (VOIDmode
, tmp
,
2488 gen_rtx_LABEL_REF (VOIDmode
, label
),
2490 tmp
= gen_rtx_SET (VOIDmode
, pc_rtx
, tmp
);
2492 use_fcomi
= ix86_use_fcomi_compare (code
);
2493 vec
= rtvec_alloc (3 + !use_fcomi
);
2494 RTVEC_ELT (vec
, 0) = tmp
;
2496 = gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (CCFPmode
, 18));
2498 = gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (CCFPmode
, 17));
2501 = gen_rtx_CLOBBER (VOIDmode
, gen_rtx_SCRATCH (HImode
));
2503 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode
, vec
));
2509 /* Expand SImode branch into multiple compare+branch. */
2511 rtx lo
[2], hi
[2], label2
;
2512 enum rtx_code code1
, code2
, code3
;
2514 if (CONSTANT_P (op0
) && !CONSTANT_P (op1
))
2519 code
= swap_condition (code
);
2521 lo
[0] = m68hc11_gen_lowpart (HImode
, op0
);
2522 lo
[1] = m68hc11_gen_lowpart (HImode
, op1
);
2523 hi
[0] = m68hc11_gen_highpart (HImode
, op0
);
2524 hi
[1] = m68hc11_gen_highpart (HImode
, op1
);
2526 /* Otherwise, if we are doing less-than, op1 is a constant and the
2527 low word is zero, then we can just examine the high word. */
2529 if (GET_CODE (hi
[1]) == CONST_INT
&& lo
[1] == const0_rtx
2530 && (code
== LT
|| code
== LTU
))
2532 return m68hc11_expand_compare_and_branch (code
, hi
[0], hi
[1],
2536 /* Otherwise, we need two or three jumps. */
2538 label2
= gen_label_rtx ();
2541 code2
= swap_condition (code
);
2542 code3
= unsigned_condition (code
);
2583 * if (hi(a) < hi(b)) goto true;
2584 * if (hi(a) > hi(b)) goto false;
2585 * if (lo(a) < lo(b)) goto true;
2589 m68hc11_expand_compare_and_branch (code1
, hi
[0], hi
[1], label
);
2591 m68hc11_expand_compare_and_branch (code2
, hi
[0], hi
[1], label2
);
2593 m68hc11_expand_compare_and_branch (code3
, lo
[0], lo
[1], label
);
2596 emit_label (label2
);
2607 /* Split a DI, SI or HI move into several smaller move operations.
2608 The scratch register 'scratch' is used as a temporary to load
2609 store intermediate values. It must be a hard register. */
2611 m68hc11_split_move (to
, from
, scratch
)
2612 rtx to
, from
, scratch
;
2614 rtx low_to
, low_from
;
2615 rtx high_to
, high_from
;
2616 enum machine_mode mode
;
2619 mode
= GET_MODE (to
);
2620 if (GET_MODE_SIZE (mode
) == 8)
2622 else if (GET_MODE_SIZE (mode
) == 4)
2628 && IS_STACK_PUSH (to
)
2629 && reg_mentioned_p (gen_rtx (REG
, HImode
, HARD_SP_REGNUM
), from
))
2635 else if (mode
== HImode
)
2643 low_to
= m68hc11_gen_lowpart (mode
, to
);
2644 high_to
= m68hc11_gen_highpart (mode
, to
);
2646 low_from
= m68hc11_gen_lowpart (mode
, from
);
2647 if (mode
== SImode
&& GET_CODE (from
) == CONST_INT
)
2649 if (INTVAL (from
) >= 0)
2650 high_from
= const0_rtx
;
2652 high_from
= constm1_rtx
;
2655 high_from
= m68hc11_gen_highpart (mode
, from
);
2659 high_from
= adjust_address (high_from
, mode
, offset
);
2660 low_from
= high_from
;
2664 m68hc11_split_move (low_to
, low_from
, scratch
);
2665 m68hc11_split_move (high_to
, high_from
, scratch
);
2667 else if (H_REG_P (to
) || H_REG_P (from
)
2669 && (!m68hc11_register_indirect_p (from
, GET_MODE (from
))
2670 || m68hc11_small_indexed_indirect_p (from
,
2672 && (!m68hc11_register_indirect_p (to
, GET_MODE (to
))
2673 || m68hc11_small_indexed_indirect_p (to
, GET_MODE (to
)))))
2675 emit_move_insn (low_to
, low_from
);
2676 emit_move_insn (high_to
, high_from
);
2682 emit_move_insn (scratch
, low_from
);
2683 insn
= emit_move_insn (low_to
, scratch
);
2685 emit_move_insn (scratch
, high_from
);
2686 insn
= emit_move_insn (high_to
, scratch
);
2691 simplify_logical (mode
, code
, operand
, result
)
2692 enum machine_mode mode
;
2701 if (GET_CODE (operand
) != CONST_INT
)
2709 val
= INTVAL (operand
);
2713 if ((val
& mask
) == 0)
2715 if ((val
& mask
) == mask
)
2716 *result
= constm1_rtx
;
2720 if ((val
& mask
) == 0)
2721 *result
= const0_rtx
;
2722 if ((val
& mask
) == mask
)
2727 if ((val
& mask
) == 0)
2735 m68hc11_emit_logical (mode
, code
, operands
)
2736 enum machine_mode mode
;
2743 need_copy
= (rtx_equal_p (operands
[0], operands
[1])
2744 || rtx_equal_p (operands
[0], operands
[2])) ? 0 : 1;
2746 operands
[1] = simplify_logical (mode
, code
, operands
[1], &result
);
2747 operands
[2] = simplify_logical (mode
, code
, operands
[2], &result
);
2749 if (result
&& GET_CODE (result
) == CONST_INT
)
2751 if (!H_REG_P (operands
[0]) && operands
[3]
2752 && (INTVAL (result
) != 0 || IS_STACK_PUSH (operands
[0])))
2754 emit_move_insn (operands
[3], result
);
2755 emit_move_insn (operands
[0], operands
[3]);
2759 emit_move_insn (operands
[0], result
);
2762 else if (operands
[1] != 0 && operands
[2] != 0)
2766 if (!H_REG_P (operands
[0]) && operands
[3])
2768 emit_move_insn (operands
[3], operands
[1]);
2769 emit_insn (gen_rtx (SET
, mode
,
2771 gen_rtx (code
, mode
,
2772 operands
[3], operands
[2])));
2773 insn
= emit_move_insn (operands
[0], operands
[3]);
2777 insn
= emit_insn (gen_rtx (SET
, mode
,
2779 gen_rtx (code
, mode
,
2780 operands
[0], operands
[2])));
2784 /* The logical operation is similar to a copy. */
2789 if (GET_CODE (operands
[1]) == CONST_INT
)
2794 if (!H_REG_P (operands
[0]) && !H_REG_P (src
))
2796 emit_move_insn (operands
[3], src
);
2797 emit_move_insn (operands
[0], operands
[3]);
2801 emit_move_insn (operands
[0], src
);
2807 m68hc11_split_logical (mode
, code
, operands
)
2808 enum machine_mode mode
;
2815 low
[0] = m68hc11_gen_lowpart (mode
, operands
[0]);
2816 low
[1] = m68hc11_gen_lowpart (mode
, operands
[1]);
2817 low
[2] = m68hc11_gen_lowpart (mode
, operands
[2]);
2819 high
[0] = m68hc11_gen_highpart (mode
, operands
[0]);
2821 if (mode
== SImode
&& GET_CODE (operands
[1]) == CONST_INT
)
2823 if (INTVAL (operands
[1]) >= 0)
2824 high
[1] = const0_rtx
;
2826 high
[1] = constm1_rtx
;
2829 high
[1] = m68hc11_gen_highpart (mode
, operands
[1]);
2831 if (mode
== SImode
&& GET_CODE (operands
[2]) == CONST_INT
)
2833 if (INTVAL (operands
[2]) >= 0)
2834 high
[2] = const0_rtx
;
2836 high
[2] = constm1_rtx
;
2839 high
[2] = m68hc11_gen_highpart (mode
, operands
[2]);
2841 low
[3] = operands
[3];
2842 high
[3] = operands
[3];
2845 m68hc11_split_logical (HImode
, code
, low
);
2846 m68hc11_split_logical (HImode
, code
, high
);
2850 m68hc11_emit_logical (mode
, code
, low
);
2851 m68hc11_emit_logical (mode
, code
, high
);
2855 /* Code generation. */
2858 m68hc11_output_swap (insn
, operands
)
2859 rtx insn ATTRIBUTE_UNUSED
;
2862 /* We have to be careful with the cc_status. An address register swap
2863 is generated for some comparison. The comparison is made with D
2864 but the branch really uses the address register. See the split
2865 pattern for compare. The xgdx/xgdy preserve the flags but after
2866 the exchange, the flags will reflect to the value of X and not D.
2867 Tell this by setting the cc_status according to the cc_prev_status. */
2868 if (X_REG_P (operands
[1]) || X_REG_P (operands
[0]))
2870 if (cc_prev_status
.value1
!= 0
2871 && (D_REG_P (cc_prev_status
.value1
)
2872 || X_REG_P (cc_prev_status
.value1
)))
2874 cc_status
= cc_prev_status
;
2875 if (D_REG_P (cc_status
.value1
))
2876 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2879 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2885 output_asm_insn ("xgdx", operands
);
2889 if (cc_prev_status
.value1
!= 0
2890 && (D_REG_P (cc_prev_status
.value1
)
2891 || Y_REG_P (cc_prev_status
.value1
)))
2893 cc_status
= cc_prev_status
;
2894 if (D_REG_P (cc_status
.value1
))
2895 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2898 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2904 output_asm_insn ("xgdy", operands
);
2908 /* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
2909 This is used to decide whether a move that set flags should be used
2912 next_insn_test_reg (insn
, reg
)
2918 insn
= next_nonnote_insn (insn
);
2919 if (GET_CODE (insn
) != INSN
)
2922 body
= PATTERN (insn
);
2923 if (sets_cc0_p (body
) != 1)
2926 if (rtx_equal_p (XEXP (body
, 1), reg
) == 0)
2932 /* Generate the code to move a 16-bit operand into another one. */
2935 m68hc11_gen_movhi (insn
, operands
)
2941 /* Move a register or memory to the same location.
2942 This is possible because such insn can appear
2943 in a non-optimizing mode. */
2944 if (operands
[0] == operands
[1] || rtx_equal_p (operands
[0], operands
[1]))
2946 cc_status
= cc_prev_status
;
2952 if (IS_STACK_PUSH (operands
[0]) && H_REG_P (operands
[1]))
2954 cc_status
= cc_prev_status
;
2955 switch (REGNO (operands
[1]))
2960 output_asm_insn ("psh%1", operands
);
2967 if (IS_STACK_POP (operands
[1]) && H_REG_P (operands
[0]))
2969 cc_status
= cc_prev_status
;
2970 switch (REGNO (operands
[0]))
2975 output_asm_insn ("pul%0", operands
);
2982 if (H_REG_P (operands
[0]) && H_REG_P (operands
[1]))
2984 m68hc11_notice_keep_cc (operands
[0]);
2985 output_asm_insn ("tfr\t%1,%0", operands
);
2987 else if (H_REG_P (operands
[0]))
2989 if (SP_REG_P (operands
[0]))
2990 output_asm_insn ("lds\t%1", operands
);
2992 output_asm_insn ("ld%0\t%1", operands
);
2994 else if (H_REG_P (operands
[1]))
2996 if (SP_REG_P (operands
[1]))
2997 output_asm_insn ("sts\t%0", operands
);
2999 output_asm_insn ("st%1\t%0", operands
);
3003 rtx from
= operands
[1];
3004 rtx to
= operands
[0];
3006 if ((m68hc11_register_indirect_p (from
, GET_MODE (from
))
3007 && !m68hc11_small_indexed_indirect_p (from
, GET_MODE (from
)))
3008 || (m68hc11_register_indirect_p (to
, GET_MODE (to
))
3009 && !m68hc11_small_indexed_indirect_p (to
, GET_MODE (to
))))
3015 ops
[0] = operands
[2];
3018 m68hc11_gen_movhi (insn
, ops
);
3020 ops
[1] = operands
[2];
3021 m68hc11_gen_movhi (insn
, ops
);
3025 /* !!!! SCz wrong here. */
3026 fatal_insn ("Move insn not handled", insn
);
3031 if (GET_CODE (from
) == CONST_INT
&& INTVAL (from
) == 0)
3033 output_asm_insn ("clr\t%h0", operands
);
3034 output_asm_insn ("clr\t%b0", operands
);
3038 m68hc11_notice_keep_cc (operands
[0]);
3039 output_asm_insn ("movw\t%1,%0", operands
);
3046 if (IS_STACK_POP (operands
[1]) && H_REG_P (operands
[0]))
3048 cc_status
= cc_prev_status
;
3049 switch (REGNO (operands
[0]))
3053 output_asm_insn ("pul%0", operands
);
3056 output_asm_insn ("pula", operands
);
3057 output_asm_insn ("pulb", operands
);
3064 /* Some moves to a hard register are special. Not all of them
3065 are really supported and we have to use a temporary
3066 location to provide them (either the stack of a temp var). */
3067 if (H_REG_P (operands
[0]))
3069 switch (REGNO (operands
[0]))
3072 if (X_REG_P (operands
[1]))
3074 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_X_REGNUM
))
3076 m68hc11_output_swap (insn
, operands
);
3078 else if (next_insn_test_reg (insn
, operands
[0]))
3080 output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands
);
3084 m68hc11_notice_keep_cc (operands
[0]);
3085 output_asm_insn ("pshx\n\tpula\n\tpulb", operands
);
3088 else if (Y_REG_P (operands
[1]))
3090 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_Y_REGNUM
))
3092 m68hc11_output_swap (insn
, operands
);
3096 /* %t means *ZTMP scratch register. */
3097 output_asm_insn ("sty\t%t1", operands
);
3098 output_asm_insn ("ldd\t%t1", operands
);
3101 else if (SP_REG_P (operands
[1]))
3106 if (optimize
== 0 || dead_register_here (insn
, ix_reg
) == 0)
3107 output_asm_insn ("xgdx", operands
);
3108 output_asm_insn ("tsx", operands
);
3109 output_asm_insn ("xgdx", operands
);
3111 else if (IS_STACK_POP (operands
[1]))
3113 output_asm_insn ("pula\n\tpulb", operands
);
3115 else if (GET_CODE (operands
[1]) == CONST_INT
3116 && INTVAL (operands
[1]) == 0)
3118 output_asm_insn ("clra\n\tclrb", operands
);
3122 output_asm_insn ("ldd\t%1", operands
);
3127 if (D_REG_P (operands
[1]))
3129 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_D_REGNUM
))
3131 m68hc11_output_swap (insn
, operands
);
3133 else if (next_insn_test_reg (insn
, operands
[0]))
3135 output_asm_insn ("std\t%t0\n\tldx\t%t0", operands
);
3139 m68hc11_notice_keep_cc (operands
[0]);
3140 output_asm_insn ("pshb", operands
);
3141 output_asm_insn ("psha", operands
);
3142 output_asm_insn ("pulx", operands
);
3145 else if (Y_REG_P (operands
[1]))
3147 /* When both D and Y are dead, use the sequence xgdy, xgdx
3148 to move Y into X. The D and Y registers are modified. */
3149 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_Y_REGNUM
)
3150 && dead_register_here (insn
, d_reg
))
3152 output_asm_insn ("xgdy", operands
);
3153 output_asm_insn ("xgdx", operands
);
3158 output_asm_insn ("sty\t%t1", operands
);
3159 output_asm_insn ("ldx\t%t1", operands
);
3162 else if (SP_REG_P (operands
[1]))
3164 /* tsx, tsy preserve the flags */
3165 cc_status
= cc_prev_status
;
3166 output_asm_insn ("tsx", operands
);
3170 output_asm_insn ("ldx\t%1", operands
);
3175 if (D_REG_P (operands
[1]))
3177 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_D_REGNUM
))
3179 m68hc11_output_swap (insn
, operands
);
3183 output_asm_insn ("std\t%t1", operands
);
3184 output_asm_insn ("ldy\t%t1", operands
);
3187 else if (X_REG_P (operands
[1]))
3189 /* When both D and X are dead, use the sequence xgdx, xgdy
3190 to move X into Y. The D and X registers are modified. */
3191 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_X_REGNUM
)
3192 && dead_register_here (insn
, d_reg
))
3194 output_asm_insn ("xgdx", operands
);
3195 output_asm_insn ("xgdy", operands
);
3200 output_asm_insn ("stx\t%t1", operands
);
3201 output_asm_insn ("ldy\t%t1", operands
);
3204 else if (SP_REG_P (operands
[1]))
3206 /* tsx, tsy preserve the flags */
3207 cc_status
= cc_prev_status
;
3208 output_asm_insn ("tsy", operands
);
3212 output_asm_insn ("ldy\t%1", operands
);
3216 case HARD_SP_REGNUM
:
3217 if (D_REG_P (operands
[1]))
3219 m68hc11_notice_keep_cc (operands
[0]);
3220 output_asm_insn ("xgdx", operands
);
3221 output_asm_insn ("txs", operands
);
3222 output_asm_insn ("xgdx", operands
);
3224 else if (X_REG_P (operands
[1]))
3226 /* tys, txs preserve the flags */
3227 cc_status
= cc_prev_status
;
3228 output_asm_insn ("txs", operands
);
3230 else if (Y_REG_P (operands
[1]))
3232 /* tys, txs preserve the flags */
3233 cc_status
= cc_prev_status
;
3234 output_asm_insn ("tys", operands
);
3238 /* lds sets the flags but the des does not. */
3240 output_asm_insn ("lds\t%1", operands
);
3241 output_asm_insn ("des", operands
);
3246 fatal_insn ("Invalid register in the move instruction", insn
);
3251 if (SP_REG_P (operands
[1]) && REG_P (operands
[0])
3252 && REGNO (operands
[0]) == HARD_FRAME_POINTER_REGNUM
)
3254 output_asm_insn ("sts\t%0", operands
);
3258 if (IS_STACK_PUSH (operands
[0]) && H_REG_P (operands
[1]))
3260 cc_status
= cc_prev_status
;
3261 switch (REGNO (operands
[1]))
3265 output_asm_insn ("psh%1", operands
);
3268 output_asm_insn ("pshb", operands
);
3269 output_asm_insn ("psha", operands
);
3277 /* Operand 1 must be a hard register. */
3278 if (!H_REG_P (operands
[1]))
3280 fatal_insn ("Invalid operand in the instruction", insn
);
3283 reg
= REGNO (operands
[1]);
3287 output_asm_insn ("std\t%0", operands
);
3291 output_asm_insn ("stx\t%0", operands
);
3295 output_asm_insn ("sty\t%0", operands
);
3298 case HARD_SP_REGNUM
:
3302 if (reg_mentioned_p (ix_reg
, operands
[0]))
3304 output_asm_insn ("sty\t%t0", operands
);
3305 output_asm_insn ("tsy", operands
);
3306 output_asm_insn ("sty\t%0", operands
);
3307 output_asm_insn ("ldy\t%t0", operands
);
3311 output_asm_insn ("stx\t%t0", operands
);
3312 output_asm_insn ("tsx", operands
);
3313 output_asm_insn ("stx\t%0", operands
);
3314 output_asm_insn ("ldx\t%t0", operands
);
3320 fatal_insn ("Invalid register in the move instruction", insn
);
3326 m68hc11_gen_movqi (insn
, operands
)
3330 /* Move a register or memory to the same location.
3331 This is possible because such insn can appear
3332 in a non-optimizing mode. */
3333 if (operands
[0] == operands
[1] || rtx_equal_p (operands
[0], operands
[1]))
3335 cc_status
= cc_prev_status
;
3342 if (H_REG_P (operands
[0]) && H_REG_P (operands
[1]))
3344 m68hc11_notice_keep_cc (operands
[0]);
3345 output_asm_insn ("tfr\t%1,%0", operands
);
3347 else if (H_REG_P (operands
[0]))
3349 if (Q_REG_P (operands
[0]))
3350 output_asm_insn ("lda%0\t%b1", operands
);
3351 else if (D_REG_P (operands
[0]))
3352 output_asm_insn ("ldab\t%b1", operands
);
3356 else if (H_REG_P (operands
[1]))
3358 if (Q_REG_P (operands
[1]))
3359 output_asm_insn ("sta%1\t%b0", operands
);
3360 else if (D_REG_P (operands
[1]))
3361 output_asm_insn ("stab\t%b0", operands
);
3367 rtx from
= operands
[1];
3368 rtx to
= operands
[0];
3370 if ((m68hc11_register_indirect_p (from
, GET_MODE (from
))
3371 && !m68hc11_small_indexed_indirect_p (from
, GET_MODE (from
)))
3372 || (m68hc11_register_indirect_p (to
, GET_MODE (to
))
3373 && !m68hc11_small_indexed_indirect_p (to
, GET_MODE (to
))))
3379 ops
[0] = operands
[2];
3382 m68hc11_gen_movqi (insn
, ops
);
3384 ops
[1] = operands
[2];
3385 m68hc11_gen_movqi (insn
, ops
);
3389 /* !!!! SCz wrong here. */
3390 fatal_insn ("Move insn not handled", insn
);
3395 if (GET_CODE (from
) == CONST_INT
&& INTVAL (from
) == 0)
3397 output_asm_insn ("clr\t%b0", operands
);
3401 m68hc11_notice_keep_cc (operands
[0]);
3402 output_asm_insn ("movb\t%b1,%b0", operands
);
3410 if (H_REG_P (operands
[0]))
3412 switch (REGNO (operands
[0]))
3416 if (X_REG_P (operands
[1]))
3418 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_X_REGNUM
))
3420 m68hc11_output_swap (insn
, operands
);
3424 output_asm_insn ("stx\t%t1", operands
);
3425 output_asm_insn ("ldab\t%T0", operands
);
3428 else if (Y_REG_P (operands
[1]))
3430 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_Y_REGNUM
))
3432 m68hc11_output_swap (insn
, operands
);
3436 output_asm_insn ("sty\t%t1", operands
);
3437 output_asm_insn ("ldab\t%T0", operands
);
3440 else if (!DB_REG_P (operands
[1]) && !D_REG_P (operands
[1])
3441 && !DA_REG_P (operands
[1]))
3443 output_asm_insn ("ldab\t%b1", operands
);
3445 else if (DA_REG_P (operands
[1]))
3447 output_asm_insn ("tab", operands
);
3451 cc_status
= cc_prev_status
;
3457 if (X_REG_P (operands
[1]))
3459 output_asm_insn ("stx\t%t1", operands
);
3460 output_asm_insn ("ldaa\t%T0", operands
);
3462 else if (Y_REG_P (operands
[1]))
3464 output_asm_insn ("sty\t%t1", operands
);
3465 output_asm_insn ("ldaa\t%T0", operands
);
3467 else if (!DB_REG_P (operands
[1]) && !D_REG_P (operands
[1])
3468 && !DA_REG_P (operands
[1]))
3470 output_asm_insn ("ldaa\t%b1", operands
);
3472 else if (!DA_REG_P (operands
[1]))
3474 output_asm_insn ("tba", operands
);
3478 cc_status
= cc_prev_status
;
3483 if (D_REG_P (operands
[1]))
3485 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_D_REGNUM
))
3487 m68hc11_output_swap (insn
, operands
);
3491 output_asm_insn ("stab\t%T1", operands
);
3492 output_asm_insn ("ldx\t%t1", operands
);
3496 else if (Y_REG_P (operands
[1]))
3498 output_asm_insn ("sty\t%t0", operands
);
3499 output_asm_insn ("ldx\t%t0", operands
);
3501 else if (GET_CODE (operands
[1]) == CONST_INT
)
3503 output_asm_insn ("ldx\t%1", operands
);
3505 else if (dead_register_here (insn
, d_reg
))
3507 output_asm_insn ("ldab\t%b1", operands
);
3508 output_asm_insn ("xgdx", operands
);
3510 else if (!reg_mentioned_p (operands
[0], operands
[1]))
3512 output_asm_insn ("xgdx", operands
);
3513 output_asm_insn ("ldab\t%b1", operands
);
3514 output_asm_insn ("xgdx", operands
);
3518 output_asm_insn ("pshb", operands
);
3519 output_asm_insn ("ldab\t%b1", operands
);
3520 output_asm_insn ("stab\t%T1", operands
);
3521 output_asm_insn ("ldx\t%t1", operands
);
3522 output_asm_insn ("pulb", operands
);
3528 if (D_REG_P (operands
[1]))
3530 output_asm_insn ("stab\t%T1", operands
);
3531 output_asm_insn ("ldy\t%t1", operands
);
3534 else if (X_REG_P (operands
[1]))
3536 output_asm_insn ("stx\t%t1", operands
);
3537 output_asm_insn ("ldy\t%t1", operands
);
3540 else if (GET_CODE (operands
[1]) == CONST_INT
)
3542 output_asm_insn ("ldy\t%1", operands
);
3544 else if (dead_register_here (insn
, d_reg
))
3546 output_asm_insn ("ldab\t%b1", operands
);
3547 output_asm_insn ("xgdy", operands
);
3549 else if (!reg_mentioned_p (operands
[0], operands
[1]))
3551 output_asm_insn ("xgdy", operands
);
3552 output_asm_insn ("ldab\t%b1", operands
);
3553 output_asm_insn ("xgdy", operands
);
3557 output_asm_insn ("pshb", operands
);
3558 output_asm_insn ("ldab\t%b1", operands
);
3559 output_asm_insn ("stab\t%T1", operands
);
3560 output_asm_insn ("ldy\t%t1", operands
);
3561 output_asm_insn ("pulb", operands
);
3567 fatal_insn ("Invalid register in the instruction", insn
);
3571 else if (H_REG_P (operands
[1]))
3573 switch (REGNO (operands
[1]))
3577 output_asm_insn ("stab\t%b0", operands
);
3581 output_asm_insn ("staa\t%b0", operands
);
3585 output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands
);
3589 output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands
);
3593 fatal_insn ("Invalid register in the move instruction", insn
);
3600 fatal_insn ("Operand 1 must be a hard register", insn
);
3604 /* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
3605 The source and destination must be D or A and the shift must
3608 m68hc11_gen_rotate (code
, insn
, operands
)
3615 if (GET_CODE (operands
[2]) != CONST_INT
3616 || (!D_REG_P (operands
[0]) && !DA_REG_P (operands
[0])))
3617 fatal_insn ("Invalid rotate insn", insn
);
3619 val
= INTVAL (operands
[2]);
3620 if (code
== ROTATERT
)
3621 val
= GET_MODE_SIZE (GET_MODE (operands
[0])) * BITS_PER_UNIT
- val
;
3623 if (GET_MODE (operands
[0]) != QImode
)
3626 /* Rotate by 8-bits if the shift is within [5..11]. */
3627 if (val
>= 5 && val
<= 11)
3629 output_asm_insn ("psha", operands
);
3630 output_asm_insn ("tba", operands
);
3631 output_asm_insn ("pulb", operands
);
3635 /* If the shift is big, invert the rotation. */
3643 /* Set the carry to bit-15, but don't change D yet. */
3644 if (GET_MODE (operands
[0]) != QImode
)
3646 output_asm_insn ("asra", operands
);
3647 output_asm_insn ("rola", operands
);
3652 /* Rotate B first to move the carry to bit-0. */
3653 if (D_REG_P (operands
[0]))
3654 output_asm_insn ("rolb", operands
);
3656 if (GET_MODE (operands
[0]) != QImode
|| DA_REG_P (operands
[0]))
3657 output_asm_insn ("rola", operands
);
3662 /* Set the carry to bit-8 of D. */
3663 if (val
!= 0 && GET_MODE (operands
[0]) != QImode
)
3665 output_asm_insn ("tap", operands
);
3670 /* Rotate B first to move the carry to bit-7. */
3671 if (D_REG_P (operands
[0]))
3672 output_asm_insn ("rorb", operands
);
3674 if (GET_MODE (operands
[0]) != QImode
|| DA_REG_P (operands
[0]))
3675 output_asm_insn ("rora", operands
);
3682 /* Store in cc_status the expressions that the condition codes will
3683 describe after execution of an instruction whose pattern is EXP.
3684 Do not alter them if the instruction would not alter the cc's. */
3687 m68hc11_notice_update_cc (exp
, insn
)
3689 rtx insn ATTRIBUTE_UNUSED
;
3691 /* recognize SET insn's. */
3692 if (GET_CODE (exp
) == SET
)
3694 /* Jumps do not alter the cc's. */
3695 if (SET_DEST (exp
) == pc_rtx
)
3698 /* NOTE: most instructions don't affect the carry bit, but the
3699 bhi/bls/bhs/blo instructions use it. This isn't mentioned in
3700 the conditions.h header. */
3702 /* Function calls clobber the cc's. */
3703 else if (GET_CODE (SET_SRC (exp
)) == CALL
)
3708 /* Tests and compares set the cc's in predictable ways. */
3709 else if (SET_DEST (exp
) == cc0_rtx
)
3711 cc_status
.flags
= 0;
3712 cc_status
.value1
= XEXP (exp
, 0);
3713 cc_status
.value2
= XEXP (exp
, 1);
3717 /* All other instructions affect the condition codes. */
3718 cc_status
.flags
= 0;
3719 cc_status
.value1
= XEXP (exp
, 0);
3720 cc_status
.value2
= XEXP (exp
, 1);
3725 /* Default action if we haven't recognized something
3726 and returned earlier. */
3730 if (cc_status
.value2
!= 0)
3731 switch (GET_CODE (cc_status
.value2
))
3733 /* These logical operations can generate several insns.
3734 The flags are setup according to what is generated. */
3740 /* The (not ...) generates several 'com' instructions for
3741 non QImode. We have to invalidate the flags. */
3743 if (GET_MODE (cc_status
.value2
) != QImode
)
3755 if (GET_MODE (cc_status
.value2
) != VOIDmode
)
3756 cc_status
.flags
|= CC_NO_OVERFLOW
;
3759 /* The asl sets the overflow bit in such a way that this
3760 makes the flags unusable for a next compare insn. */
3764 if (GET_MODE (cc_status
.value2
) != VOIDmode
)
3765 cc_status
.flags
|= CC_NO_OVERFLOW
;
3768 /* A load/store instruction does not affect the carry. */
3773 cc_status
.flags
|= CC_NO_OVERFLOW
;
3779 if (cc_status
.value1
&& GET_CODE (cc_status
.value1
) == REG
3781 && reg_overlap_mentioned_p (cc_status
.value1
, cc_status
.value2
))
3782 cc_status
.value2
= 0;
3785 /* The current instruction does not affect the flags but changes
3786 the register 'reg'. See if the previous flags can be kept for the
3787 next instruction to avoid a comparison. */
3789 m68hc11_notice_keep_cc (reg
)
3793 || cc_prev_status
.value1
== 0
3794 || rtx_equal_p (reg
, cc_prev_status
.value1
)
3795 || (cc_prev_status
.value2
3796 && reg_mentioned_p (reg
, cc_prev_status
.value2
)))
3799 cc_status
= cc_prev_status
;
3804 /* Machine Specific Reorg. */
3806 /* Z register replacement:
3808 GCC treats the Z register as an index base address register like
3809 X or Y. In general, it uses it during reload to compute the address
3810 of some operand. This helps the reload pass to avoid to fall into the
3811 register spill failure.
3813 The Z register is in the A_REGS class. In the machine description,
3814 the 'A' constraint matches it. The 'x' or 'y' constraints do not.
3816 It can appear everywhere an X or Y register can appear, except for
3817 some templates in the clobber section (when a clobber of X or Y is asked).
3818 For a given instruction, the template must ensure that no more than
3819 2 'A' registers are used. Otherwise, the register replacement is not
3822 To replace the Z register, the algorithm is not terrific:
3823 1. Insns that do not use the Z register are not changed
3824 2. When a Z register is used, we scan forward the insns to see
3825 a potential register to use: either X or Y and sometimes D.
3826 We stop when a call, a label or a branch is seen, or when we
3827 detect that both X and Y are used (probably at different times, but it does
3829 3. The register that will be used for the replacement of Z is saved
3830 in a .page0 register or on the stack. If the first instruction that
3831 used Z, uses Z as an input, the value is loaded from another .page0
3832 register. The replacement register is pushed on the stack in the
3833 rare cases where a compare insn uses Z and we couldn't find if X/Y
3835 4. The Z register is replaced in all instructions until we reach
3836 the end of the Z-block, as detected by step 2.
3837 5. If we detect that Z is still alive, its value is saved.
3838 If the replacement register is alive, its old value is loaded.
3840 The Z register can be disabled with -ffixed-z.
3850 int must_restore_reg
;
3861 int save_before_last
;
3862 int z_loaded_with_sp
;
3865 static rtx z_reg_qi
;
3867 static int m68hc11_check_z_replacement
PARAMS ((rtx
, struct replace_info
*));
3868 static void m68hc11_find_z_replacement
PARAMS ((rtx
, struct replace_info
*));
3869 static void m68hc11_z_replacement
PARAMS ((rtx
));
3870 static void m68hc11_reassign_regs
PARAMS ((rtx
));
3872 int z_replacement_completed
= 0;
3874 /* Analyze the insn to find out which replacement register to use and
3875 the boundaries of the replacement.
3876 Returns 0 if we reached the last insn to be replaced, 1 if we can
3877 continue replacement in next insns. */
3880 m68hc11_check_z_replacement (insn
, info
)
3882 struct replace_info
*info
;
3884 int this_insn_uses_ix
;
3885 int this_insn_uses_iy
;
3886 int this_insn_uses_z
;
3887 int this_insn_uses_z_in_dst
;
3888 int this_insn_uses_d
;
3892 /* A call is said to clobber the Z register, we don't need
3893 to save the value of Z. We also don't need to restore
3894 the replacement register (unless it is used by the call). */
3895 if (GET_CODE (insn
) == CALL_INSN
)
3897 body
= PATTERN (insn
);
3899 info
->can_use_d
= 0;
3901 /* If the call is an indirect call with Z, we have to use the
3902 Y register because X can be used as an input (D+X).
3903 We also must not save Z nor restore Y. */
3904 if (reg_mentioned_p (z_reg
, body
))
3906 insn
= NEXT_INSN (insn
);
3909 info
->found_call
= 1;
3910 info
->must_restore_reg
= 0;
3911 info
->last
= NEXT_INSN (insn
);
3913 info
->need_save_z
= 0;
3916 if (GET_CODE (insn
) == CODE_LABEL
3917 || GET_CODE (insn
) == BARRIER
|| GET_CODE (insn
) == ASM_INPUT
)
3920 if (GET_CODE (insn
) == JUMP_INSN
)
3922 if (reg_mentioned_p (z_reg
, insn
) == 0)
3925 info
->can_use_d
= 0;
3926 info
->must_save_reg
= 0;
3927 info
->must_restore_reg
= 0;
3928 info
->need_save_z
= 0;
3929 info
->last
= NEXT_INSN (insn
);
3932 if (GET_CODE (insn
) != INSN
&& GET_CODE (insn
) != JUMP_INSN
)
3937 /* Z register dies here. */
3938 z_dies_here
= find_regno_note (insn
, REG_DEAD
, HARD_Z_REGNUM
) != NULL
;
3940 body
= PATTERN (insn
);
3941 if (GET_CODE (body
) == SET
)
3943 rtx src
= XEXP (body
, 1);
3944 rtx dst
= XEXP (body
, 0);
3946 /* Condition code is set here. We have to restore the X/Y and
3947 save into Z before any test/compare insn because once we save/restore
3948 we can change the condition codes. When the compare insn uses Z and
3949 we can't use X/Y, the comparison is made with the *ZREG soft register
3950 (this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
3953 if ((GET_CODE (src
) == REG
&& REGNO (src
) == HARD_Z_REGNUM
)
3954 || (GET_CODE (src
) == COMPARE
&&
3955 (rtx_equal_p (XEXP (src
, 0), z_reg
)
3956 || rtx_equal_p (XEXP (src
, 1), z_reg
))))
3958 if (insn
== info
->first
)
3960 info
->must_load_z
= 0;
3961 info
->must_save_reg
= 0;
3962 info
->must_restore_reg
= 0;
3963 info
->need_save_z
= 0;
3964 info
->found_call
= 1;
3965 info
->regno
= SOFT_Z_REGNUM
;
3970 if (reg_mentioned_p (z_reg
, src
) == 0)
3972 info
->can_use_d
= 0;
3976 if (insn
!= info
->first
)
3979 /* Compare insn which uses Z. We have to save/restore the X/Y
3980 register without modifying the condition codes. For this
3981 we have to use a push/pop insn. */
3982 info
->must_push_reg
= 1;
3986 /* Z reg is set to something new. We don't need to load it. */
3989 if (!reg_mentioned_p (z_reg
, src
))
3991 /* Z reg is used before being set. Treat this as
3992 a new sequence of Z register replacement. */
3993 if (insn
!= info
->first
)
3997 info
->must_load_z
= 0;
3999 info
->z_set_count
++;
4000 info
->z_value
= src
;
4002 info
->z_loaded_with_sp
= 1;
4004 else if (reg_mentioned_p (z_reg
, dst
))
4005 info
->can_use_d
= 0;
4007 this_insn_uses_d
= reg_mentioned_p (d_reg
, src
)
4008 | reg_mentioned_p (d_reg
, dst
);
4009 this_insn_uses_ix
= reg_mentioned_p (ix_reg
, src
)
4010 | reg_mentioned_p (ix_reg
, dst
);
4011 this_insn_uses_iy
= reg_mentioned_p (iy_reg
, src
)
4012 | reg_mentioned_p (iy_reg
, dst
);
4013 this_insn_uses_z
= reg_mentioned_p (z_reg
, src
);
4015 /* If z is used as an address operand (like (MEM (reg z))),
4016 we can't replace it with d. */
4017 if (this_insn_uses_z
&& !Z_REG_P (src
)
4018 && !(m68hc11_arith_operator (src
, GET_MODE (src
))
4019 && Z_REG_P (XEXP (src
, 0))
4020 && !reg_mentioned_p (z_reg
, XEXP (src
, 1))
4021 && insn
== info
->first
4022 && dead_register_here (insn
, d_reg
)))
4023 info
->can_use_d
= 0;
4025 this_insn_uses_z_in_dst
= reg_mentioned_p (z_reg
, dst
);
4026 if (TARGET_M6812
&& !z_dies_here
4027 && ((this_insn_uses_z
&& side_effects_p (src
))
4028 || (this_insn_uses_z_in_dst
&& side_effects_p (dst
))))
4030 info
->need_save_z
= 1;
4031 info
->z_set_count
++;
4033 this_insn_uses_z
|= this_insn_uses_z_in_dst
;
4035 if (this_insn_uses_z
&& this_insn_uses_ix
&& this_insn_uses_iy
)
4037 fatal_insn ("Registers IX, IY and Z used in the same INSN", insn
);
4040 if (this_insn_uses_d
)
4041 info
->can_use_d
= 0;
4043 /* IX and IY are used at the same time, we have to restore
4044 the value of the scratch register before this insn. */
4045 if (this_insn_uses_ix
&& this_insn_uses_iy
)
4050 if (this_insn_uses_ix
&& X_REG_P (dst
) && GET_MODE (dst
) == SImode
)
4051 info
->can_use_d
= 0;
4053 if (info
->x_used
== 0 && this_insn_uses_ix
)
4057 /* We have a (set (REG:HI X) (REG:HI Z)).
4058 Since we use Z as the replacement register, this insn
4059 is no longer necessary. We turn it into a note. We must
4060 not reload the old value of X. */
4061 if (X_REG_P (dst
) && rtx_equal_p (src
, z_reg
))
4065 info
->need_save_z
= 0;
4068 info
->must_save_reg
= 0;
4069 info
->must_restore_reg
= 0;
4070 info
->found_call
= 1;
4071 info
->can_use_d
= 0;
4072 PUT_CODE (insn
, NOTE
);
4073 NOTE_LINE_NUMBER (insn
) = NOTE_INSN_DELETED
;
4074 NOTE_SOURCE_FILE (insn
) = 0;
4075 info
->last
= NEXT_INSN (insn
);
4080 && (rtx_equal_p (src
, z_reg
)
4081 || (z_dies_here
&& !reg_mentioned_p (ix_reg
, src
))))
4085 info
->need_save_z
= 0;
4088 info
->last
= NEXT_INSN (insn
);
4089 info
->must_save_reg
= 0;
4090 info
->must_restore_reg
= 0;
4092 else if (X_REG_P (dst
) && reg_mentioned_p (z_reg
, src
)
4093 && !reg_mentioned_p (ix_reg
, src
))
4098 info
->need_save_z
= 0;
4102 info
->save_before_last
= 1;
4104 info
->must_restore_reg
= 0;
4105 info
->last
= NEXT_INSN (insn
);
4107 else if (info
->can_use_d
)
4109 info
->last
= NEXT_INSN (insn
);
4115 if (z_dies_here
&& !reg_mentioned_p (ix_reg
, src
)
4116 && GET_CODE (dst
) == REG
&& REGNO (dst
) == HARD_X_REGNUM
)
4118 info
->need_save_z
= 0;
4120 info
->last
= NEXT_INSN (insn
);
4121 info
->regno
= HARD_X_REGNUM
;
4122 info
->must_save_reg
= 0;
4123 info
->must_restore_reg
= 0;
4126 if (rtx_equal_p (src
, z_reg
) && rtx_equal_p (dst
, ix_reg
))
4128 info
->regno
= HARD_X_REGNUM
;
4129 info
->must_restore_reg
= 0;
4130 info
->must_save_reg
= 0;
4134 if (info
->y_used
== 0 && this_insn_uses_iy
)
4138 if (Y_REG_P (dst
) && rtx_equal_p (src
, z_reg
))
4142 info
->need_save_z
= 0;
4145 info
->must_save_reg
= 0;
4146 info
->must_restore_reg
= 0;
4147 info
->found_call
= 1;
4148 info
->can_use_d
= 0;
4149 PUT_CODE (insn
, NOTE
);
4150 NOTE_LINE_NUMBER (insn
) = NOTE_INSN_DELETED
;
4151 NOTE_SOURCE_FILE (insn
) = 0;
4152 info
->last
= NEXT_INSN (insn
);
4157 && (rtx_equal_p (src
, z_reg
)
4158 || (z_dies_here
&& !reg_mentioned_p (iy_reg
, src
))))
4163 info
->need_save_z
= 0;
4165 info
->last
= NEXT_INSN (insn
);
4166 info
->must_save_reg
= 0;
4167 info
->must_restore_reg
= 0;
4169 else if (Y_REG_P (dst
) && reg_mentioned_p (z_reg
, src
)
4170 && !reg_mentioned_p (iy_reg
, src
))
4175 info
->need_save_z
= 0;
4179 info
->save_before_last
= 1;
4181 info
->must_restore_reg
= 0;
4182 info
->last
= NEXT_INSN (insn
);
4184 else if (info
->can_use_d
)
4186 info
->last
= NEXT_INSN (insn
);
4193 if (z_dies_here
&& !reg_mentioned_p (iy_reg
, src
)
4194 && GET_CODE (dst
) == REG
&& REGNO (dst
) == HARD_Y_REGNUM
)
4196 info
->need_save_z
= 0;
4198 info
->last
= NEXT_INSN (insn
);
4199 info
->regno
= HARD_Y_REGNUM
;
4200 info
->must_save_reg
= 0;
4201 info
->must_restore_reg
= 0;
4204 if (rtx_equal_p (src
, z_reg
) && rtx_equal_p (dst
, iy_reg
))
4206 info
->regno
= HARD_Y_REGNUM
;
4207 info
->must_restore_reg
= 0;
4208 info
->must_save_reg
= 0;
4214 info
->need_save_z
= 0;
4216 if (info
->last
== 0)
4217 info
->last
= NEXT_INSN (insn
);
4220 return info
->last
!= NULL_RTX
? 0 : 1;
4222 if (GET_CODE (body
) == PARALLEL
)
4225 char ix_clobber
= 0;
4226 char iy_clobber
= 0;
4228 this_insn_uses_iy
= 0;
4229 this_insn_uses_ix
= 0;
4230 this_insn_uses_z
= 0;
4232 for (i
= XVECLEN (body
, 0) - 1; i
>= 0; i
--)
4235 int uses_ix
, uses_iy
, uses_z
;
4237 x
= XVECEXP (body
, 0, i
);
4239 if (info
->can_use_d
&& reg_mentioned_p (d_reg
, x
))
4240 info
->can_use_d
= 0;
4242 uses_ix
= reg_mentioned_p (ix_reg
, x
);
4243 uses_iy
= reg_mentioned_p (iy_reg
, x
);
4244 uses_z
= reg_mentioned_p (z_reg
, x
);
4245 if (GET_CODE (x
) == CLOBBER
)
4247 ix_clobber
|= uses_ix
;
4248 iy_clobber
|= uses_iy
;
4249 z_clobber
|= uses_z
;
4253 this_insn_uses_ix
|= uses_ix
;
4254 this_insn_uses_iy
|= uses_iy
;
4255 this_insn_uses_z
|= uses_z
;
4257 if (uses_z
&& GET_CODE (x
) == SET
)
4259 rtx dst
= XEXP (x
, 0);
4262 info
->z_set_count
++;
4264 if (TARGET_M6812
&& uses_z
&& side_effects_p (x
))
4265 info
->need_save_z
= 1;
4268 info
->need_save_z
= 0;
4272 printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
4273 this_insn_uses_ix
, this_insn_uses_iy
,
4274 this_insn_uses_z
, ix_clobber
, iy_clobber
, z_clobber
);
4277 if (this_insn_uses_z
)
4278 info
->can_use_d
= 0;
4280 if (z_clobber
&& info
->first
!= insn
)
4282 info
->need_save_z
= 0;
4286 if (z_clobber
&& info
->x_used
== 0 && info
->y_used
== 0)
4288 if (this_insn_uses_z
== 0 && insn
== info
->first
)
4290 info
->must_load_z
= 0;
4292 if (dead_register_here (insn
, d_reg
))
4294 info
->regno
= HARD_D_REGNUM
;
4295 info
->must_save_reg
= 0;
4296 info
->must_restore_reg
= 0;
4298 else if (dead_register_here (insn
, ix_reg
))
4300 info
->regno
= HARD_X_REGNUM
;
4301 info
->must_save_reg
= 0;
4302 info
->must_restore_reg
= 0;
4304 else if (dead_register_here (insn
, iy_reg
))
4306 info
->regno
= HARD_Y_REGNUM
;
4307 info
->must_save_reg
= 0;
4308 info
->must_restore_reg
= 0;
4310 if (info
->regno
>= 0)
4312 info
->last
= NEXT_INSN (insn
);
4315 if (this_insn_uses_ix
== 0)
4317 info
->regno
= HARD_X_REGNUM
;
4318 info
->must_save_reg
= 1;
4319 info
->must_restore_reg
= 1;
4321 else if (this_insn_uses_iy
== 0)
4323 info
->regno
= HARD_Y_REGNUM
;
4324 info
->must_save_reg
= 1;
4325 info
->must_restore_reg
= 1;
4329 info
->regno
= HARD_D_REGNUM
;
4330 info
->must_save_reg
= 1;
4331 info
->must_restore_reg
= 1;
4333 info
->last
= NEXT_INSN (insn
);
4337 if (((info
->x_used
|| this_insn_uses_ix
) && iy_clobber
)
4338 || ((info
->y_used
|| this_insn_uses_iy
) && ix_clobber
))
4340 if (this_insn_uses_z
)
4342 if (info
->y_used
== 0 && iy_clobber
)
4344 info
->regno
= HARD_Y_REGNUM
;
4345 info
->must_save_reg
= 0;
4346 info
->must_restore_reg
= 0;
4348 info
->last
= NEXT_INSN (insn
);
4349 info
->save_before_last
= 1;
4353 if (this_insn_uses_ix
&& this_insn_uses_iy
)
4355 if (this_insn_uses_z
)
4357 fatal_insn ("Cannot do z-register replacement", insn
);
4361 if (info
->x_used
== 0 && (this_insn_uses_ix
|| ix_clobber
))
4368 if (iy_clobber
|| z_clobber
)
4370 info
->last
= NEXT_INSN (insn
);
4371 info
->save_before_last
= 1;
4376 if (info
->y_used
== 0 && (this_insn_uses_iy
|| iy_clobber
))
4383 if (ix_clobber
|| z_clobber
)
4385 info
->last
= NEXT_INSN (insn
);
4386 info
->save_before_last
= 1;
4393 info
->need_save_z
= 0;
4397 if (GET_CODE (body
) == CLOBBER
)
4400 /* IX and IY are used at the same time, we have to restore
4401 the value of the scratch register before this insn. */
4402 if (this_insn_uses_ix
&& this_insn_uses_iy
)
4406 if (info
->x_used
== 0 && this_insn_uses_ix
)
4414 if (info
->y_used
== 0 && this_insn_uses_iy
)
4428 m68hc11_find_z_replacement (insn
, info
)
4430 struct replace_info
*info
;
4434 info
->replace_reg
= NULL_RTX
;
4435 info
->must_load_z
= 1;
4436 info
->need_save_z
= 1;
4437 info
->must_save_reg
= 1;
4438 info
->must_restore_reg
= 1;
4442 info
->can_use_d
= TARGET_M6811
? 1 : 0;
4443 info
->found_call
= 0;
4447 info
->z_set_count
= 0;
4448 info
->z_value
= NULL_RTX
;
4449 info
->must_push_reg
= 0;
4450 info
->save_before_last
= 0;
4451 info
->z_loaded_with_sp
= 0;
4453 /* Scan the insn forward to find an address register that is not used.
4455 - the flow of the program changes,
4456 - when we detect that both X and Y are necessary,
4457 - when the Z register dies,
4458 - when the condition codes are set. */
4460 for (; insn
&& info
->z_died
== 0; insn
= NEXT_INSN (insn
))
4462 if (m68hc11_check_z_replacement (insn
, info
) == 0)
4466 /* May be we can use Y or X if they contain the same value as Z.
4467 This happens very often after the reload. */
4468 if (info
->z_set_count
== 1)
4470 rtx p
= info
->first
;
4475 v
= find_last_value (iy_reg
, &p
, insn
, 1);
4477 else if (info
->y_used
)
4479 v
= find_last_value (ix_reg
, &p
, insn
, 1);
4481 if (v
&& (v
!= iy_reg
&& v
!= ix_reg
) && rtx_equal_p (v
, info
->z_value
))
4484 info
->regno
= HARD_Y_REGNUM
;
4486 info
->regno
= HARD_X_REGNUM
;
4487 info
->must_load_z
= 0;
4488 info
->must_save_reg
= 0;
4489 info
->must_restore_reg
= 0;
4490 info
->found_call
= 1;
4493 if (info
->z_set_count
== 0)
4494 info
->need_save_z
= 0;
4497 info
->need_save_z
= 0;
4499 if (info
->last
== 0)
4502 if (info
->regno
>= 0)
4505 info
->replace_reg
= gen_rtx (REG
, HImode
, reg
);
4507 else if (info
->can_use_d
)
4509 reg
= HARD_D_REGNUM
;
4510 info
->replace_reg
= d_reg
;
4512 else if (info
->x_used
)
4514 reg
= HARD_Y_REGNUM
;
4515 info
->replace_reg
= iy_reg
;
4519 reg
= HARD_X_REGNUM
;
4520 info
->replace_reg
= ix_reg
;
4524 if (info
->must_save_reg
&& info
->must_restore_reg
)
4526 if (insn
&& dead_register_here (insn
, info
->replace_reg
))
4528 info
->must_save_reg
= 0;
4529 info
->must_restore_reg
= 0;
4534 /* The insn uses the Z register. Find a replacement register for it
4535 (either X or Y) and replace it in the insn and the next ones until
4536 the flow changes or the replacement register is used. Instructions
4537 are emited before and after the Z-block to preserve the value of
4538 Z and of the replacement register. */
4541 m68hc11_z_replacement (insn
)
4546 struct replace_info info
;
4548 /* Find trivial case where we only need to replace z with the
4549 equivalent soft register. */
4550 if (GET_CODE (insn
) == INSN
&& GET_CODE (PATTERN (insn
)) == SET
)
4552 rtx body
= PATTERN (insn
);
4553 rtx src
= XEXP (body
, 1);
4554 rtx dst
= XEXP (body
, 0);
4556 if (Z_REG_P (dst
) && (H_REG_P (src
) && !SP_REG_P (src
)))
4558 XEXP (body
, 0) = gen_rtx (REG
, GET_MODE (dst
), SOFT_Z_REGNUM
);
4561 else if (Z_REG_P (src
)
4562 && ((H_REG_P (dst
) && !SP_REG_P (src
)) || dst
== cc0_rtx
))
4564 XEXP (body
, 1) = gen_rtx (REG
, GET_MODE (src
), SOFT_Z_REGNUM
);
4567 else if (D_REG_P (dst
)
4568 && m68hc11_arith_operator (src
, GET_MODE (src
))
4569 && D_REG_P (XEXP (src
, 0)) && Z_REG_P (XEXP (src
, 1)))
4571 XEXP (src
, 1) = gen_rtx (REG
, GET_MODE (src
), SOFT_Z_REGNUM
);
4574 else if (Z_REG_P (dst
) && GET_CODE (src
) == CONST_INT
4575 && INTVAL (src
) == 0)
4577 XEXP (body
, 0) = gen_rtx (REG
, GET_MODE (dst
), SOFT_Z_REGNUM
);
4578 /* Force it to be re-recognized. */
4579 INSN_CODE (insn
) = -1;
4584 m68hc11_find_z_replacement (insn
, &info
);
4586 replace_reg
= info
.replace_reg
;
4587 replace_reg_qi
= NULL_RTX
;
4589 /* Save the X register in a .page0 location. */
4590 if (info
.must_save_reg
&& !info
.must_push_reg
)
4594 if (info
.must_push_reg
&& 0)
4595 dst
= gen_rtx (MEM
, HImode
,
4596 gen_rtx (PRE_DEC
, HImode
,
4597 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
4599 dst
= gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
);
4601 emit_insn_before (gen_movhi (dst
,
4602 gen_rtx (REG
, HImode
, info
.regno
)), insn
);
4604 if (info
.must_load_z
&& !info
.must_push_reg
)
4606 emit_insn_before (gen_movhi (gen_rtx (REG
, HImode
, info
.regno
),
4607 gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
)),
4612 /* Replace all occurence of Z by replace_reg.
4613 Stop when the last instruction to replace is reached.
4614 Also stop when we detect a change in the flow (but it's not
4615 necessary; just safeguard). */
4617 for (; insn
&& insn
!= info
.last
; insn
= NEXT_INSN (insn
))
4621 if (GET_CODE (insn
) == CODE_LABEL
|| GET_CODE (insn
) == BARRIER
)
4624 if (GET_CODE (insn
) != INSN
4625 && GET_CODE (insn
) != CALL_INSN
&& GET_CODE (insn
) != JUMP_INSN
)
4628 body
= PATTERN (insn
);
4629 if (GET_CODE (body
) == SET
|| GET_CODE (body
) == PARALLEL
4630 || GET_CODE (insn
) == CALL_INSN
|| GET_CODE (insn
) == JUMP_INSN
)
4632 if (debug_m6811
&& reg_mentioned_p (replace_reg
, body
))
4634 printf ("Reg mentioned here...:\n");
4639 /* Stack pointer was decremented by 2 due to the push.
4640 Correct that by adding 2 to the destination. */
4641 if (info
.must_push_reg
4642 && info
.z_loaded_with_sp
&& GET_CODE (body
) == SET
)
4646 src
= SET_SRC (body
);
4647 dst
= SET_DEST (body
);
4648 if (SP_REG_P (src
) && Z_REG_P (dst
))
4650 emit_insn_after (gen_addhi3 (dst
,
4653 VOIDmode
, 2)), insn
);
4657 /* Replace any (REG:HI Z) occurrence by either X or Y. */
4658 if (!validate_replace_rtx (z_reg
, replace_reg
, insn
))
4660 INSN_CODE (insn
) = -1;
4661 if (!validate_replace_rtx (z_reg
, replace_reg
, insn
))
4662 fatal_insn ("Cannot do z-register replacement", insn
);
4665 /* Likewise for (REG:QI Z). */
4666 if (reg_mentioned_p (z_reg
, insn
))
4668 if (replace_reg_qi
== NULL_RTX
)
4669 replace_reg_qi
= gen_rtx (REG
, QImode
, REGNO (replace_reg
));
4670 validate_replace_rtx (z_reg_qi
, replace_reg_qi
, insn
);
4673 if (GET_CODE (insn
) == CALL_INSN
|| GET_CODE (insn
) == JUMP_INSN
)
4677 /* Save Z before restoring the old value. */
4678 if (insn
&& info
.need_save_z
&& !info
.must_push_reg
)
4680 rtx save_pos_insn
= insn
;
4682 /* If Z is clobber by the last insn, we have to save its value
4683 before the last instruction. */
4684 if (info
.save_before_last
)
4685 save_pos_insn
= PREV_INSN (save_pos_insn
);
4687 emit_insn_before (gen_movhi (gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
),
4688 gen_rtx (REG
, HImode
, info
.regno
)),
4692 if (info
.must_push_reg
&& info
.last
)
4696 body
= PATTERN (info
.last
);
4697 new_body
= gen_rtx (PARALLEL
, VOIDmode
,
4699 gen_rtx (USE
, VOIDmode
,
4701 gen_rtx (USE
, VOIDmode
,
4702 gen_rtx (REG
, HImode
,
4704 PATTERN (info
.last
) = new_body
;
4706 /* Force recognition on insn since we changed it. */
4707 INSN_CODE (insn
) = -1;
4709 if (!validate_replace_rtx (z_reg
, replace_reg
, info
.last
))
4711 fatal_insn ("Invalid Z register replacement for insn", insn
);
4713 insn
= NEXT_INSN (info
.last
);
4716 /* Restore replacement register unless it was died. */
4717 if (insn
&& info
.must_restore_reg
&& !info
.must_push_reg
)
4721 if (info
.must_push_reg
&& 0)
4722 dst
= gen_rtx (MEM
, HImode
,
4723 gen_rtx (POST_INC
, HImode
,
4724 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
4726 dst
= gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
);
4728 emit_insn_before (gen_movhi (gen_rtx (REG
, HImode
, info
.regno
),
4735 /* Scan all the insn and re-affects some registers
4736 - The Z register (if it was used), is affected to X or Y depending
4737 on the instruction. */
4740 m68hc11_reassign_regs (first
)
4745 ix_reg
= gen_rtx (REG
, HImode
, HARD_X_REGNUM
);
4746 iy_reg
= gen_rtx (REG
, HImode
, HARD_Y_REGNUM
);
4747 z_reg
= gen_rtx (REG
, HImode
, HARD_Z_REGNUM
);
4748 z_reg_qi
= gen_rtx (REG
, QImode
, HARD_Z_REGNUM
);
4750 /* Scan all insns to replace Z by X or Y preserving the old value
4751 of X/Y and restoring it afterward. */
4753 for (insn
= first
; insn
; insn
= NEXT_INSN (insn
))
4757 if (GET_CODE (insn
) == CODE_LABEL
4758 || GET_CODE (insn
) == NOTE
|| GET_CODE (insn
) == BARRIER
)
4761 if (GET_RTX_CLASS (GET_CODE (insn
)) != 'i')
4764 body
= PATTERN (insn
);
4765 if (GET_CODE (body
) == CLOBBER
|| GET_CODE (body
) == USE
)
4768 if (GET_CODE (body
) == CONST_INT
|| GET_CODE (body
) == ASM_INPUT
4769 || GET_CODE (body
) == ASM_OPERANDS
4770 || GET_CODE (body
) == UNSPEC
|| GET_CODE (body
) == UNSPEC_VOLATILE
)
4773 if (GET_CODE (body
) == SET
|| GET_CODE (body
) == PARALLEL
4774 || GET_CODE (insn
) == CALL_INSN
|| GET_CODE (insn
) == JUMP_INSN
)
4777 /* If Z appears in this insn, replace it in the current insn
4778 and the next ones until the flow changes or we have to
4779 restore back the replacement register. */
4781 if (reg_mentioned_p (z_reg
, body
))
4783 m68hc11_z_replacement (insn
);
4788 printf ("Insn not handled by Z replacement:\n");
4797 m68hc11_reorg (first
)
4803 z_replacement_completed
= 0;
4804 z_reg
= gen_rtx (REG
, HImode
, HARD_Z_REGNUM
);
4806 /* Some RTX are shared at this point. This breaks the Z register
4807 replacement, unshare everything. */
4808 unshare_all_rtl_again (first
);
4810 /* Force a split of all splitable insn. This is necessary for the
4811 Z register replacement mechanism because we end up with basic insns. */
4812 split_all_insns_noflow ();
4815 z_replacement_completed
= 1;
4816 m68hc11_reassign_regs (first
);
4818 /* After some splitting, there are some oportunities for CSE pass.
4819 This happens quite often when 32-bit or above patterns are split. */
4820 if (optimize
> 0 && split_done
)
4821 reload_cse_regs (first
);
4823 /* Re-create the REG_DEAD notes. These notes are used in the machine
4824 description to use the best assembly directives. */
4827 /* Before recomputing the REG_DEAD notes, remove all of them.
4828 This is necessary because the reload_cse_regs() pass can
4829 have replaced some (MEM) with a register. In that case,
4830 the REG_DEAD that could exist for that register may become
4832 for (insn
= first
; insn
; insn
= NEXT_INSN (insn
))
4838 pnote
= ®_NOTES (insn
);
4841 if (REG_NOTE_KIND (*pnote
) == REG_DEAD
)
4842 *pnote
= XEXP (*pnote
, 1);
4844 pnote
= &XEXP (*pnote
, 1);
4849 find_basic_blocks (first
, max_reg_num (), 0);
4850 life_analysis (first
, 0, PROP_REG_INFO
| PROP_DEATH_NOTES
);
4853 z_replacement_completed
= 2;
4855 /* If optimizing, then go ahead and split insns that must be
4856 split after Z register replacement. This gives more opportunities
4857 for peephole (in particular for consecutives xgdx/xgdy). */
4859 split_all_insns_noflow ();
4861 /* Once insns are split after the z_replacement_completed == 2,
4862 we must not re-run the life_analysis. The xgdx/xgdy patterns
4863 are not recognized and the life_analysis pass removes some
4864 insns because it thinks some (SETs) are noops or made to dead
4865 stores (which is false due to the swap).
4867 Do a simple pass to eliminate the noop set that the final
4868 split could generate (because it was easier for split definition). */
4872 for (insn
= first
; insn
; insn
= NEXT_INSN (insn
))
4876 if (INSN_DELETED_P (insn
))
4878 if (GET_RTX_CLASS (GET_CODE (insn
)) != 'i')
4881 /* Remove the (set (R) (R)) insns generated by some splits. */
4882 body
= PATTERN (insn
);
4883 if (GET_CODE (body
) == SET
4884 && rtx_equal_p (SET_SRC (body
), SET_DEST (body
)))
4886 PUT_CODE (insn
, NOTE
);
4887 NOTE_LINE_NUMBER (insn
) = NOTE_INSN_DELETED
;
4888 NOTE_SOURCE_FILE (insn
) = 0;
4896 /* Cost functions. */
4898 /* Cost of moving memory. */
4900 m68hc11_memory_move_cost (mode
, class, in
)
4901 enum machine_mode mode
;
4902 enum reg_class
class;
4903 int in ATTRIBUTE_UNUSED
;
4905 if (class <= H_REGS
)
4907 if (GET_MODE_SIZE (mode
) <= 2)
4908 return COSTS_N_INSNS (1) + (reload_completed
| reload_in_progress
);
4910 return COSTS_N_INSNS (2) + (reload_completed
| reload_in_progress
);
4914 if (GET_MODE_SIZE (mode
) <= 2)
4915 return COSTS_N_INSNS (2);
4917 return COSTS_N_INSNS (4);
4922 /* Cost of moving data from a register of class 'from' to on in class 'to'.
4923 Reload does not check the constraint of set insns when the two registers
4924 have a move cost of 2. Setting a higher cost will force reload to check
4927 m68hc11_register_move_cost (from
, to
)
4928 enum reg_class from
;
4931 if (from
>= S_REGS
&& to
>= S_REGS
)
4933 return COSTS_N_INSNS (3);
4935 if (from
<= S_REGS
&& to
<= S_REGS
)
4937 return COSTS_N_INSNS (1) + (reload_completed
| reload_in_progress
);
4939 return COSTS_N_INSNS (2);
4943 /* Provide the costs of an addressing mode that contains ADDR.
4944 If ADDR is not a valid address, its cost is irrelevant. */
4947 m68hc11_address_cost (addr
)
4952 switch (GET_CODE (addr
))
4955 /* Make the cost of hard registers and specially SP, FP small. */
4956 if (REGNO (addr
) < FIRST_PSEUDO_REGISTER
)
4973 register rtx plus0
= XEXP (addr
, 0);
4974 register rtx plus1
= XEXP (addr
, 1);
4976 if (GET_CODE (plus0
) != REG
)
4979 switch (GET_CODE (plus1
))
4982 if (INTVAL (plus1
) >= 2 * m68hc11_max_offset
4983 || INTVAL (plus1
) < m68hc11_min_offset
)
4985 else if (INTVAL (plus1
) >= m68hc11_max_offset
)
4989 if (REGNO (plus0
) < FIRST_PSEUDO_REGISTER
)
5011 if (SP_REG_P (XEXP (addr
, 0)))
5020 printf ("Address cost: %d for :", cost
);
5029 m68hc11_shift_cost (mode
, x
, shift
)
5030 enum machine_mode mode
;
5036 total
= rtx_cost (x
, SET
);
5038 total
+= m68hc11_cost
->shiftQI_const
[shift
% 8];
5039 else if (mode
== HImode
)
5040 total
+= m68hc11_cost
->shiftHI_const
[shift
% 16];
5041 else if (shift
== 8 || shift
== 16 || shift
== 32)
5042 total
+= m68hc11_cost
->shiftHI_const
[8];
5043 else if (shift
!= 0 && shift
!= 16 && shift
!= 32)
5045 total
+= m68hc11_cost
->shiftHI_const
[1] * shift
;
5048 /* For SI and others, the cost is higher. */
5049 if (GET_MODE_SIZE (mode
) > 2 && (shift
% 16) != 0)
5050 total
*= GET_MODE_SIZE (mode
) / 2;
5052 /* When optimizing for size, make shift more costly so that
5053 multiplications are prefered. */
5054 if (optimize_size
&& (shift
% 8) != 0)
5061 m68hc11_rtx_costs (x
, code
, outer_code
)
5064 enum rtx_code outer_code ATTRIBUTE_UNUSED
;
5066 enum machine_mode mode
= GET_MODE (x
);
5077 if (GET_CODE (XEXP (x
, 1)) == CONST_INT
)
5079 return m68hc11_shift_cost (mode
, XEXP (x
, 0), INTVAL (XEXP (x
, 1)));
5082 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5083 total
+= m68hc11_cost
->shift_var
;
5089 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5090 total
+= m68hc11_cost
->logical
;
5092 /* Logical instructions are byte instructions only. */
5093 total
*= GET_MODE_SIZE (mode
);
5098 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5099 total
+= m68hc11_cost
->add
;
5100 if (GET_MODE_SIZE (mode
) > 2)
5102 total
*= GET_MODE_SIZE (mode
) / 2;
5109 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5113 total
+= m68hc11_cost
->divQI
;
5117 total
+= m68hc11_cost
->divHI
;
5122 total
+= m68hc11_cost
->divSI
;
5128 /* mul instruction produces 16-bit result. */
5129 if (mode
== HImode
&& GET_CODE (XEXP (x
, 0)) == ZERO_EXTEND
5130 && GET_CODE (XEXP (x
, 1)) == ZERO_EXTEND
)
5131 return m68hc11_cost
->multQI
5132 + rtx_cost (XEXP (XEXP (x
, 0), 0), code
)
5133 + rtx_cost (XEXP (XEXP (x
, 1), 0), code
);
5135 /* emul instruction produces 32-bit result for 68HC12. */
5136 if (TARGET_M6812
&& mode
== SImode
5137 && GET_CODE (XEXP (x
, 0)) == ZERO_EXTEND
5138 && GET_CODE (XEXP (x
, 1)) == ZERO_EXTEND
)
5139 return m68hc11_cost
->multHI
5140 + rtx_cost (XEXP (XEXP (x
, 0), 0), code
)
5141 + rtx_cost (XEXP (XEXP (x
, 1), 0), code
);
5143 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5147 total
+= m68hc11_cost
->multQI
;
5151 total
+= m68hc11_cost
->multHI
;
5156 total
+= m68hc11_cost
->multSI
;
5163 extra_cost
= COSTS_N_INSNS (2);
5170 total
= extra_cost
+ rtx_cost (XEXP (x
, 0), code
);
5173 return total
+ COSTS_N_INSNS (1);
5177 return total
+ COSTS_N_INSNS (2);
5181 return total
+ COSTS_N_INSNS (4);
5183 return total
+ COSTS_N_INSNS (8);
5186 if (GET_CODE (XEXP (x
, 1)) == PC
|| GET_CODE (XEXP (x
, 2)) == PC
)
5187 return COSTS_N_INSNS (1);
5189 return COSTS_N_INSNS (1);
5192 return COSTS_N_INSNS (4);
5197 /* print_options - called at the start of the code generation for a
5200 extern char *asm_file_name
;
5203 #include <sys/types.h>
5212 extern int save_argc
;
5213 extern char **save_argv
;
5215 fprintf (out
, ";;; Command:\t");
5216 for (i
= 0; i
< save_argc
; i
++)
5218 fprintf (out
, "%s", save_argv
[i
]);
5219 if (i
+ 1 < save_argc
)
5222 fprintf (out
, "\n");
5224 a_time
= ctime (&c_time
);
5225 fprintf (out
, ";;; Compiled:\t%s", a_time
);
5228 #define __VERSION__ "[unknown]"
5230 fprintf (out
, ";;; (META)compiled by GNU C version %s.\n", __VERSION__
);
5232 fprintf (out
, ";;; (META)compiled by CC.\n");
5237 m68hc11_asm_file_start (out
, main_file
)
5241 fprintf (out
, ";;;-----------------------------------------\n");
5242 fprintf (out
, ";;; Start MC68HC11 gcc assembly output\n");
5243 fprintf (out
, ";;; gcc compiler %s\n", version_string
);
5244 print_options (out
);
5245 fprintf (out
, ";;;-----------------------------------------\n");
5246 output_file_directive (out
, main_file
);
5251 m68hc11_add_gc_roots ()
5253 ggc_add_rtx_root (&m68hc11_soft_tmp_reg
, 1);
5254 ggc_add_rtx_root (&ix_reg
, 1);
5255 ggc_add_rtx_root (&iy_reg
, 1);
5256 ggc_add_rtx_root (&d_reg
, 1);
5257 ggc_add_rtx_root (&da_reg
, 1);
5258 ggc_add_rtx_root (&z_reg
, 1);
5259 ggc_add_rtx_root (&z_reg_qi
, 1);
5260 ggc_add_rtx_root (&stack_push_word
, 1);
5261 ggc_add_rtx_root (&stack_pop_word
, 1);
5265 m68hc11_asm_out_constructor (symbol
, priority
)
5269 default_ctor_section_asm_out_constructor (symbol
, priority
);
5270 fprintf (asm_out_file
, "\t.globl\t__do_global_ctors\n");
5274 m68hc11_asm_out_destructor (symbol
, priority
)
5278 default_dtor_section_asm_out_destructor (symbol
, priority
);
5279 fprintf (asm_out_file
, "\t.globl\t__do_global_dtors\n");