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"
57 #include "target-def.h"
59 static void print_options
PARAMS ((FILE *));
60 static void emit_move_after_reload
PARAMS ((rtx
, rtx
, rtx
));
61 static rtx simplify_logical
PARAMS ((enum machine_mode
, int, rtx
, rtx
*));
62 static void m68hc11_emit_logical
PARAMS ((enum machine_mode
, int, rtx
*));
63 static int go_if_legitimate_address_internal
PARAMS((rtx
, enum machine_mode
,
65 static int register_indirect_p
PARAMS((rtx
, enum machine_mode
, int));
66 static rtx m68hc11_expand_compare
PARAMS((enum rtx_code
, rtx
, rtx
));
67 static int must_parenthesize
PARAMS ((rtx
));
68 static int m68hc11_shift_cost
PARAMS ((enum machine_mode
, rtx
, int));
69 static int m68hc11_auto_inc_p
PARAMS ((rtx
));
70 static tree m68hc11_handle_fntype_attribute
PARAMS ((tree
*, tree
, tree
, int, bool *));
71 const struct attribute_spec m68hc11_attribute_table
[];
73 void create_regs_rtx
PARAMS ((void));
74 static void m68hc11_add_gc_roots
PARAMS ((void));
76 static void asm_print_register
PARAMS ((FILE *, int));
77 static void m68hc11_output_function_epilogue
PARAMS ((FILE *, HOST_WIDE_INT
));
78 static void m68hc11_asm_out_constructor
PARAMS ((rtx
, int));
79 static void m68hc11_asm_out_destructor
PARAMS ((rtx
, int));
81 rtx m68hc11_soft_tmp_reg
;
83 /* Must be set to 1 to produce debug messages. */
86 extern FILE *asm_out_file
;
94 static int regs_inited
= 0;
97 /* Set to 1 by expand_prologue() when the function is an interrupt handler. */
98 int current_function_interrupt
;
100 /* Set to 1 by expand_prologue() when the function is a trap handler. */
101 int current_function_trap
;
103 /* Min offset that is valid for the indirect addressing mode. */
104 HOST_WIDE_INT m68hc11_min_offset
= 0;
106 /* Max offset that is valid for the indirect addressing mode. */
107 HOST_WIDE_INT m68hc11_max_offset
= 256;
109 /* The class value for base registers. */
110 enum reg_class m68hc11_base_reg_class
= A_REGS
;
112 /* The class value for index registers. This is NO_REGS for 68HC11. */
113 enum reg_class m68hc11_index_reg_class
= NO_REGS
;
115 enum reg_class m68hc11_tmp_regs_class
= NO_REGS
;
117 /* Tables that tell whether a given hard register is valid for
118 a base or an index register. It is filled at init time depending
119 on the target processor. */
120 unsigned char m68hc11_reg_valid_for_base
[FIRST_PSEUDO_REGISTER
];
121 unsigned char m68hc11_reg_valid_for_index
[FIRST_PSEUDO_REGISTER
];
123 /* A correction offset which is applied to the stack pointer.
124 This is 1 for 68HC11 and 0 for 68HC12. */
125 int m68hc11_sp_correction
;
127 /* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns. */
128 rtx m68hc11_compare_op0
;
129 rtx m68hc11_compare_op1
;
132 const struct processor_costs
*m68hc11_cost
;
134 /* Costs for a 68HC11. */
135 static const struct processor_costs m6811_cost
= {
140 /* non-constant shift */
143 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
144 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
145 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
148 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
149 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
150 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
151 COSTS_N_INSNS (2), COSTS_N_INSNS (4),
152 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
153 COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
158 COSTS_N_INSNS (20 * 4),
160 COSTS_N_INSNS (20 * 16),
169 /* Costs for a 68HC12. */
170 static const struct processor_costs m6812_cost
= {
175 /* non-constant shift */
178 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
179 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
180 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
183 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
184 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
185 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
186 COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
187 COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
188 COSTS_N_INSNS (6), COSTS_N_INSNS (4)
195 COSTS_N_INSNS (3 * 4),
204 /* Machine specific options */
206 const char *m68hc11_regparm_string
;
207 const char *m68hc11_reg_alloc_order
;
208 const char *m68hc11_soft_reg_count
;
210 static int nb_soft_regs
;
212 /* Initialize the GCC target structure. */
213 #undef TARGET_ATTRIBUTE_TABLE
214 #define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
216 #undef TARGET_ASM_ALIGNED_HI_OP
217 #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
219 #undef TARGET_ASM_FUNCTION_EPILOGUE
220 #define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
222 struct gcc_target targetm
= TARGET_INITIALIZER
;
225 m68hc11_override_options ()
227 m68hc11_add_gc_roots ();
229 memset (m68hc11_reg_valid_for_index
, 0,
230 sizeof (m68hc11_reg_valid_for_index
));
231 memset (m68hc11_reg_valid_for_base
, 0, sizeof (m68hc11_reg_valid_for_base
));
233 /* Compilation with -fpic generates a wrong code. */
236 warning ("-f%s ignored for 68HC11/68HC12 (not supported)",
237 (flag_pic
> 1) ? "PIC" : "pic");
241 /* Configure for a 68hc11 processor. */
244 /* If gcc was built for a 68hc12, invalidate that because
245 a -m68hc11 option was specified on the command line. */
246 if (TARGET_DEFAULT
!= MASK_M6811
)
247 target_flags
&= ~TARGET_DEFAULT
;
249 m68hc11_cost
= &m6811_cost
;
250 m68hc11_min_offset
= 0;
251 m68hc11_max_offset
= 256;
252 m68hc11_index_reg_class
= NO_REGS
;
253 m68hc11_base_reg_class
= A_REGS
;
254 m68hc11_reg_valid_for_base
[HARD_X_REGNUM
] = 1;
255 m68hc11_reg_valid_for_base
[HARD_Y_REGNUM
] = 1;
256 m68hc11_reg_valid_for_base
[HARD_Z_REGNUM
] = 1;
257 m68hc11_sp_correction
= 1;
258 m68hc11_tmp_regs_class
= D_REGS
;
259 if (m68hc11_soft_reg_count
== 0 && !TARGET_M6812
)
260 m68hc11_soft_reg_count
= "4";
263 /* Configure for a 68hc12 processor. */
266 m68hc11_cost
= &m6812_cost
;
267 m68hc11_min_offset
= -65536;
268 m68hc11_max_offset
= 65536;
269 m68hc11_index_reg_class
= D_REGS
;
270 m68hc11_base_reg_class
= A_OR_SP_REGS
;
271 m68hc11_reg_valid_for_base
[HARD_X_REGNUM
] = 1;
272 m68hc11_reg_valid_for_base
[HARD_Y_REGNUM
] = 1;
273 m68hc11_reg_valid_for_base
[HARD_Z_REGNUM
] = 1;
274 m68hc11_reg_valid_for_base
[HARD_SP_REGNUM
] = 1;
275 m68hc11_reg_valid_for_index
[HARD_D_REGNUM
] = 1;
276 m68hc11_sp_correction
= 0;
277 m68hc11_tmp_regs_class
= TMP_REGS
;
278 target_flags
&= ~MASK_M6811
;
279 if (m68hc11_soft_reg_count
== 0)
280 m68hc11_soft_reg_count
= "2";
287 m68hc11_conditional_register_usage ()
290 int cnt
= atoi (m68hc11_soft_reg_count
);
294 if (cnt
> SOFT_REG_LAST
- SOFT_REG_FIRST
)
295 cnt
= SOFT_REG_LAST
- SOFT_REG_FIRST
;
298 for (i
= SOFT_REG_FIRST
+ cnt
; i
< SOFT_REG_LAST
; i
++)
301 call_used_regs
[i
] = 1;
306 /* Reload and register operations. */
308 static const char *const reg_class_names
[] = REG_CLASS_NAMES
;
314 /* regs_inited = 1; */
315 ix_reg
= gen_rtx (REG
, HImode
, HARD_X_REGNUM
);
316 iy_reg
= gen_rtx (REG
, HImode
, HARD_Y_REGNUM
);
317 d_reg
= gen_rtx (REG
, HImode
, HARD_D_REGNUM
);
318 da_reg
= gen_rtx (REG
, QImode
, HARD_A_REGNUM
);
319 m68hc11_soft_tmp_reg
= gen_rtx (REG
, HImode
, SOFT_TMP_REGNUM
);
321 stack_push_word
= gen_rtx (MEM
, HImode
,
322 gen_rtx (PRE_DEC
, HImode
,
323 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
324 stack_pop_word
= gen_rtx (MEM
, HImode
,
325 gen_rtx (POST_INC
, HImode
,
326 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
330 /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
331 - 8 bit values are stored anywhere (except the SP register).
332 - 16 bit values can be stored in any register whose mode is 16
333 - 32 bit values can be stored in D, X registers or in a soft register
334 (except the last one because we need 2 soft registers)
335 - Values whose size is > 32 bit are not stored in real hard
336 registers. They may be stored in soft registers if there are
339 hard_regno_mode_ok (regno
, mode
)
341 enum machine_mode mode
;
343 switch (GET_MODE_SIZE (mode
))
346 return S_REGNO_P (regno
) && nb_soft_regs
>= 4;
349 return X_REGNO_P (regno
) || (S_REGNO_P (regno
) && nb_soft_regs
>= 2);
352 return G_REGNO_P (regno
);
355 /* We have to accept a QImode in X or Y registers. Otherwise, the
356 reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
357 in the insns. Reload fails if the insn rejects the register class 'a'
358 as well as if it accepts it. Patterns that failed were
359 zero_extend_qihi2 and iorqi3. */
361 return G_REGNO_P (regno
) && !SP_REGNO_P (regno
);
369 preferred_reload_class (operand
, class)
371 enum reg_class
class;
373 enum machine_mode mode
;
375 mode
= GET_MODE (operand
);
379 printf ("Preferred reload: (class=%s): ", reg_class_names
[class]);
382 if (class == D_OR_A_OR_S_REGS
&& SP_REG_P (operand
))
383 return m68hc11_base_reg_class
;
385 if (class >= S_REGS
&& (GET_CODE (operand
) == MEM
386 || GET_CODE (operand
) == CONST_INT
))
388 /* S_REGS class must not be used. The movhi template does not
389 work to move a memory to a soft register.
390 Restrict to a hard reg. */
395 case D_OR_A_OR_S_REGS
:
401 case D_OR_SP_OR_S_REGS
:
402 class = D_OR_SP_REGS
;
404 case D_OR_Y_OR_S_REGS
:
407 case D_OR_X_OR_S_REGS
:
423 else if (class == Y_REGS
&& GET_CODE (operand
) == MEM
)
427 else if (class == A_OR_D_REGS
&& GET_MODE_SIZE (mode
) == 4)
431 else if (class >= S_REGS
&& S_REG_P (operand
))
437 case D_OR_A_OR_S_REGS
:
443 case D_OR_SP_OR_S_REGS
:
444 class = D_OR_SP_REGS
;
446 case D_OR_Y_OR_S_REGS
:
449 case D_OR_X_OR_S_REGS
:
465 else if (class >= S_REGS
)
469 printf ("Class = %s for: ", reg_class_names
[class]);
477 printf (" => class=%s\n", reg_class_names
[class]);
485 /* Return 1 if the operand is a valid indexed addressing mode.
486 For 68hc11: n,r with n in [0..255] and r in A_REGS class
487 For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
489 register_indirect_p (operand
, mode
, strict
)
491 enum machine_mode mode
;
496 switch (GET_CODE (operand
))
502 if (TARGET_M6812
&& TARGET_AUTO_INC_DEC
)
503 return register_indirect_p (XEXP (operand
, 0), mode
, strict
);
507 base
= XEXP (operand
, 0);
508 if (GET_CODE (base
) == MEM
)
511 offset
= XEXP (operand
, 1);
512 if (GET_CODE (offset
) == MEM
)
515 if (GET_CODE (base
) == REG
)
517 if (!VALID_CONSTANT_OFFSET_P (offset
, mode
))
523 return REGNO_OK_FOR_BASE_P2 (REGNO (base
), strict
);
525 if (GET_CODE (offset
) == REG
)
527 if (!VALID_CONSTANT_OFFSET_P (base
, mode
))
533 return REGNO_OK_FOR_BASE_P2 (REGNO (offset
), strict
);
538 return REGNO_OK_FOR_BASE_P2 (REGNO (operand
), strict
);
545 /* Returns 1 if the operand fits in a 68HC11 indirect mode or in
546 a 68HC12 1-byte index addressing mode. */
548 m68hc11_small_indexed_indirect_p (operand
, mode
)
550 enum machine_mode mode
;
554 if (GET_CODE (operand
) != MEM
)
557 operand
= XEXP (operand
, 0);
558 if (CONSTANT_ADDRESS_P (operand
))
561 if (PUSH_POP_ADDRESS_P (operand
))
564 if (!register_indirect_p (operand
, mode
,
565 (reload_completed
| reload_in_progress
)))
568 if (TARGET_M6812
&& GET_CODE (operand
) == PLUS
569 && (reload_completed
| reload_in_progress
))
571 base
= XEXP (operand
, 0);
572 offset
= XEXP (operand
, 1);
573 if (GET_CODE (base
) == CONST_INT
)
576 switch (GET_MODE_SIZE (mode
))
579 if (INTVAL (offset
) < -16 + 6 || INTVAL (offset
) > 15 - 6)
584 if (INTVAL (offset
) < -16 + 2 || INTVAL (offset
) > 15 - 2)
589 if (INTVAL (offset
) < -16 || INTVAL (offset
) > 15)
598 m68hc11_register_indirect_p (operand
, mode
)
600 enum machine_mode mode
;
602 if (GET_CODE (operand
) != MEM
)
605 operand
= XEXP (operand
, 0);
606 return register_indirect_p (operand
, mode
,
607 (reload_completed
| reload_in_progress
));
611 go_if_legitimate_address_internal (operand
, mode
, strict
)
613 enum machine_mode mode
;
616 if (CONSTANT_ADDRESS_P (operand
))
618 /* Reject the global variables if they are too wide. This forces
619 a load of their address in a register and generates smaller code. */
620 if (GET_MODE_SIZE (mode
) == 8)
625 if (register_indirect_p (operand
, mode
, strict
))
629 if (PUSH_POP_ADDRESS_P (operand
))
633 if (symbolic_memory_operand (operand
, mode
))
641 m68hc11_go_if_legitimate_address (operand
, mode
, strict
)
643 enum machine_mode mode
;
650 printf ("Checking: ");
655 result
= go_if_legitimate_address_internal (operand
, mode
, strict
);
659 printf (" -> %s\n", result
== 0 ? "NO" : "YES");
666 printf ("go_if_legitimate%s, ret 0: %d:",
667 (strict
? "_strict" : ""), mode
);
676 m68hc11_legitimize_address (operand
, old_operand
, mode
)
677 rtx
*operand ATTRIBUTE_UNUSED
;
678 rtx old_operand ATTRIBUTE_UNUSED
;
679 enum machine_mode mode ATTRIBUTE_UNUSED
;
686 m68hc11_reload_operands (operands
)
689 enum machine_mode mode
;
691 if (regs_inited
== 0)
694 mode
= GET_MODE (operands
[1]);
696 /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
697 if (A_REG_P (operands
[0]) && memory_reload_operand (operands
[1], mode
))
699 rtx big_offset
= XEXP (XEXP (operands
[1], 0), 1);
700 rtx base
= XEXP (XEXP (operands
[1], 0), 0);
702 if (GET_CODE (base
) != REG
)
709 /* If the offset is out of range, we have to compute the address
710 with a separate add instruction. We try to do with with an 8-bit
711 add on the A register. This is possible only if the lowest part
712 of the offset (ie, big_offset % 256) is a valid constant offset
713 with respect to the mode. If it's not, we have to generate a
714 16-bit add on the D register. From:
716 (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
720 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
721 (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
722 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
723 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
725 (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
726 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
729 if (!VALID_CONSTANT_OFFSET_P (big_offset
, mode
))
732 rtx reg
= operands
[0];
734 int val
= INTVAL (big_offset
);
737 /* We use the 'operands[0]' as a scratch register to compute the
738 address. Make sure 'base' is in that register. */
739 if (!rtx_equal_p (base
, operands
[0]))
741 emit_move_insn (reg
, base
);
751 vh
= (val
>> 8) & 0x0FF;
755 /* Create the lowest part offset that still remains to be added.
756 If it's not a valid offset, do a 16-bit add. */
757 offset
= gen_rtx (CONST_INT
, VOIDmode
, vl
);
758 if (!VALID_CONSTANT_OFFSET_P (offset
, mode
))
760 emit_insn (gen_rtx (SET
, VOIDmode
, reg
,
761 gen_rtx (PLUS
, HImode
, reg
, big_offset
)));
766 emit_insn (gen_rtx (SET
, VOIDmode
, reg
,
767 gen_rtx (PLUS
, HImode
, reg
,
769 VOIDmode
, vh
<< 8))));
771 emit_move_insn (operands
[0],
772 gen_rtx (MEM
, GET_MODE (operands
[1]),
773 gen_rtx (PLUS
, Pmode
, reg
, offset
)));
778 /* Use the normal gen_movhi pattern. */
783 m68hc11_emit_libcall (name
, code
, dmode
, smode
, noperands
, operands
)
786 enum machine_mode dmode
;
787 enum machine_mode smode
;
797 libcall
= gen_rtx_SYMBOL_REF (Pmode
, name
);
801 ret
= emit_library_call_value (libcall
, NULL_RTX
, LCT_CONST
,
802 dmode
, 1, operands
[1], smode
);
803 equiv
= gen_rtx (code
, dmode
, operands
[1]);
807 ret
= emit_library_call_value (libcall
, NULL_RTX
,
809 operands
[1], smode
, operands
[2],
811 equiv
= gen_rtx (code
, dmode
, operands
[1], operands
[2]);
818 insns
= get_insns ();
820 emit_libcall_block (insns
, operands
[0], ret
, equiv
);
823 /* Returns true if X is a PRE/POST increment decrement
824 (same as auto_inc_p() in rtlanal.c but do not take into
825 account the stack). */
827 m68hc11_auto_inc_p (x
)
830 return GET_CODE (x
) == PRE_DEC
831 || GET_CODE (x
) == POST_INC
832 || GET_CODE (x
) == POST_DEC
|| GET_CODE (x
) == PRE_INC
;
836 /* Predicates for machine description. */
839 memory_reload_operand (operand
, mode
)
841 enum machine_mode mode ATTRIBUTE_UNUSED
;
843 return GET_CODE (operand
) == MEM
844 && GET_CODE (XEXP (operand
, 0)) == PLUS
845 && ((GET_CODE (XEXP (XEXP (operand
, 0), 0)) == REG
846 && GET_CODE (XEXP (XEXP (operand
, 0), 1)) == CONST_INT
)
847 || (GET_CODE (XEXP (XEXP (operand
, 0), 1)) == REG
848 && GET_CODE (XEXP (XEXP (operand
, 0), 0)) == CONST_INT
));
852 tst_operand (operand
, mode
)
854 enum machine_mode mode
;
856 if (GET_CODE (operand
) == MEM
)
858 rtx addr
= XEXP (operand
, 0);
859 if (m68hc11_auto_inc_p (addr
))
862 return nonimmediate_operand (operand
, mode
);
866 cmp_operand (operand
, mode
)
868 enum machine_mode mode
;
870 if (GET_CODE (operand
) == MEM
)
872 rtx addr
= XEXP (operand
, 0);
873 if (m68hc11_auto_inc_p (addr
))
876 return general_operand (operand
, mode
);
880 non_push_operand (operand
, mode
)
882 enum machine_mode mode
;
884 if (general_operand (operand
, mode
) == 0)
887 if (push_operand (operand
, mode
) == 1)
893 reg_or_some_mem_operand (operand
, mode
)
895 enum machine_mode mode
;
897 if (GET_CODE (operand
) == MEM
)
899 rtx op
= XEXP (operand
, 0);
901 if (symbolic_memory_operand (op
, mode
))
904 if (IS_STACK_PUSH (operand
))
907 if (m68hc11_register_indirect_p (operand
, mode
))
913 return register_operand (operand
, mode
);
917 stack_register_operand (operand
, mode
)
919 enum machine_mode mode ATTRIBUTE_UNUSED
;
921 return SP_REG_P (operand
);
925 d_register_operand (operand
, mode
)
927 enum machine_mode mode ATTRIBUTE_UNUSED
;
929 if (GET_CODE (operand
) == SUBREG
)
930 operand
= XEXP (operand
, 0);
932 return GET_CODE (operand
) == REG
933 && (REGNO (operand
) >= FIRST_PSEUDO_REGISTER
934 || REGNO (operand
) == HARD_D_REGNUM
935 || (mode
== QImode
&& REGNO (operand
) == HARD_B_REGNUM
));
939 hard_addr_reg_operand (operand
, mode
)
941 enum machine_mode mode ATTRIBUTE_UNUSED
;
943 if (GET_CODE (operand
) == SUBREG
)
944 operand
= XEXP (operand
, 0);
946 return GET_CODE (operand
) == REG
947 && (REGNO (operand
) == HARD_X_REGNUM
948 || REGNO (operand
) == HARD_Y_REGNUM
949 || REGNO (operand
) == HARD_Z_REGNUM
);
953 hard_reg_operand (operand
, mode
)
955 enum machine_mode mode ATTRIBUTE_UNUSED
;
957 if (GET_CODE (operand
) == SUBREG
)
958 operand
= XEXP (operand
, 0);
960 return GET_CODE (operand
) == REG
961 && (REGNO (operand
) >= FIRST_PSEUDO_REGISTER
962 || H_REGNO_P (REGNO (operand
)));
966 memory_indexed_operand (operand
, mode
)
968 enum machine_mode mode ATTRIBUTE_UNUSED
;
970 if (GET_CODE (operand
) != MEM
)
973 operand
= XEXP (operand
, 0);
974 if (GET_CODE (operand
) == PLUS
)
976 if (GET_CODE (XEXP (operand
, 0)) == REG
)
977 operand
= XEXP (operand
, 0);
978 else if (GET_CODE (XEXP (operand
, 1)) == REG
)
979 operand
= XEXP (operand
, 1);
981 return GET_CODE (operand
) == REG
982 && (REGNO (operand
) >= FIRST_PSEUDO_REGISTER
983 || A_REGNO_P (REGNO (operand
)));
987 push_pop_operand_p (operand
)
990 if (GET_CODE (operand
) != MEM
)
994 operand
= XEXP (operand
, 0);
995 return PUSH_POP_ADDRESS_P (operand
);
998 /* Returns 1 if OP is either a symbol reference or a sum of a symbol
999 reference and a constant. */
1002 symbolic_memory_operand (op
, mode
)
1004 enum machine_mode mode
;
1006 switch (GET_CODE (op
))
1013 return ((GET_CODE (XEXP (op
, 0)) == SYMBOL_REF
1014 || GET_CODE (XEXP (op
, 0)) == LABEL_REF
)
1015 && GET_CODE (XEXP (op
, 1)) == CONST_INT
);
1017 /* ??? This clause seems to be irrelevant. */
1019 return GET_MODE (op
) == mode
;
1022 return symbolic_memory_operand (XEXP (op
, 0), mode
)
1023 && symbolic_memory_operand (XEXP (op
, 1), mode
);
1031 m68hc11_logical_operator (op
, mode
)
1033 enum machine_mode mode ATTRIBUTE_UNUSED
;
1035 return GET_CODE (op
) == AND
|| GET_CODE (op
) == IOR
|| GET_CODE (op
) == XOR
;
1039 m68hc11_arith_operator (op
, mode
)
1041 enum machine_mode mode ATTRIBUTE_UNUSED
;
1043 return GET_CODE (op
) == AND
|| GET_CODE (op
) == IOR
|| GET_CODE (op
) == XOR
1044 || GET_CODE (op
) == PLUS
|| GET_CODE (op
) == MINUS
1045 || GET_CODE (op
) == ASHIFT
|| GET_CODE (op
) == ASHIFTRT
1046 || GET_CODE (op
) == LSHIFTRT
|| GET_CODE (op
) == ROTATE
1047 || GET_CODE (op
) == ROTATERT
;
1051 m68hc11_non_shift_operator (op
, mode
)
1053 enum machine_mode mode ATTRIBUTE_UNUSED
;
1055 return GET_CODE (op
) == AND
|| GET_CODE (op
) == IOR
|| GET_CODE (op
) == XOR
1056 || GET_CODE (op
) == PLUS
|| GET_CODE (op
) == MINUS
;
1061 m68hc11_unary_operator (op
, mode
)
1063 enum machine_mode mode ATTRIBUTE_UNUSED
;
1065 return GET_CODE (op
) == NEG
|| GET_CODE (op
) == NOT
1066 || GET_CODE (op
) == SIGN_EXTEND
|| GET_CODE (op
) == ZERO_EXTEND
;
1073 m68hc11_block_profiler (out
, blockno
)
1074 FILE *out ATTRIBUTE_UNUSED
;
1075 int blockno ATTRIBUTE_UNUSED
;
1081 m68hc11_function_block_profiler (out
, block_or_label
)
1082 FILE *out ATTRIBUTE_UNUSED
;
1083 int block_or_label ATTRIBUTE_UNUSED
;
1088 /* Emit the code to build the trampoline used to call a nested function.
1092 ldy #&CXT movw #&CXT,*_.d1
1093 sty *_.d1 jmp FNADDR
1098 m68hc11_initialize_trampoline (tramp
, fnaddr
, cxt
)
1103 const char *static_chain_reg
= reg_names
[STATIC_CHAIN_REGNUM
];
1106 if (*static_chain_reg
== '*')
1110 emit_move_insn (gen_rtx_MEM (HImode
, tramp
), GEN_INT (0x18ce));
1111 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 2)), cxt
);
1112 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 4)),
1114 emit_move_insn (gen_rtx_MEM (QImode
, plus_constant (tramp
, 6)),
1115 gen_rtx_CONST (QImode
,
1116 gen_rtx_SYMBOL_REF (Pmode
,
1117 static_chain_reg
)));
1118 emit_move_insn (gen_rtx_MEM (QImode
, plus_constant (tramp
, 7)),
1120 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 8)), fnaddr
);
1124 emit_move_insn (gen_rtx_MEM (HImode
, tramp
), GEN_INT (0x1803));
1125 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 2)), cxt
);
1126 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 4)),
1127 gen_rtx_CONST (HImode
,
1128 gen_rtx_SYMBOL_REF (Pmode
,
1129 static_chain_reg
)));
1130 emit_move_insn (gen_rtx_MEM (QImode
, plus_constant (tramp
, 6)),
1132 emit_move_insn (gen_rtx_MEM (HImode
, plus_constant (tramp
, 7)), fnaddr
);
1136 /* Declaration of types. */
1138 const struct attribute_spec m68hc11_attribute_table
[] =
1140 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
1141 { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute
},
1142 { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute
},
1143 { NULL
, 0, 0, false, false, false, NULL
}
1146 /* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
1147 arguments as in struct attribute_spec.handler. */
1149 m68hc11_handle_fntype_attribute (node
, name
, args
, flags
, no_add_attrs
)
1152 tree args ATTRIBUTE_UNUSED
;
1153 int flags ATTRIBUTE_UNUSED
;
1156 if (TREE_CODE (*node
) != FUNCTION_TYPE
1157 && TREE_CODE (*node
) != FIELD_DECL
1158 && TREE_CODE (*node
) != TYPE_DECL
)
1160 warning ("`%s' attribute only applies to functions",
1161 IDENTIFIER_POINTER (name
));
1162 *no_add_attrs
= true;
1168 /* Define this macro if references to a symbol must be treated
1169 differently depending on something about the variable or function
1170 named by the symbol (such as what section it is in).
1172 For the 68HC11, we want to recognize trap handlers so that we
1173 handle calls to traps in a special manner (by issuing the trap).
1174 This information is stored in SYMBOL_REF_FLAG. */
1176 m68hc11_encode_section_info (decl
)
1183 if (TREE_CODE (decl
) != FUNCTION_DECL
)
1186 rtl
= DECL_RTL (decl
);
1188 func_attr
= TYPE_ATTRIBUTES (TREE_TYPE (decl
));
1189 trap_handler
= lookup_attribute ("trap", func_attr
) != NULL_TREE
;
1190 SYMBOL_REF_FLAG (XEXP (rtl
, 0)) = trap_handler
;
1194 /* Argument support functions. */
1196 /* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
1197 Arrays are passed by references and other types by value.
1199 SCz: I tried to pass DImode by reference but it seems that this
1200 does not work very well. */
1202 m68hc11_function_arg_pass_by_reference (cum
, mode
, type
, named
)
1203 const CUMULATIVE_ARGS
*cum ATTRIBUTE_UNUSED
;
1204 enum machine_mode mode ATTRIBUTE_UNUSED
;
1206 int named ATTRIBUTE_UNUSED
;
1208 return ((type
&& TREE_CODE (type
) == ARRAY_TYPE
)
1209 /* Consider complex values as aggregates, so care for TCmode. */
1210 /*|| GET_MODE_SIZE (mode) > 4 SCz, temporary */
1211 /*|| (type && AGGREGATE_TYPE_P (type))) */ );
1215 /* Define the offset between two registers, one to be eliminated, and the
1216 other its replacement, at the start of a routine. */
1218 m68hc11_initial_elimination_offset (from
, to
)
1227 /* For a trap handler, we must take into account the registers which
1228 are pushed on the stack during the trap (except the PC). */
1229 func_attr
= TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl
));
1230 trap_handler
= lookup_attribute ("trap", func_attr
) != NULL_TREE
;
1231 if (trap_handler
&& from
== ARG_POINTER_REGNUM
)
1236 if (from
== ARG_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
1238 /* 2 is for the saved frame.
1239 1 is for the 'sts' correction when creating the frame. */
1240 return get_frame_size () + 2 + m68hc11_sp_correction
+ size
;
1243 if (from
== FRAME_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
1245 return m68hc11_sp_correction
;
1248 /* Push any 2 byte pseudo hard registers that we need to save. */
1249 for (regno
= SOFT_REG_FIRST
; regno
< SOFT_REG_LAST
; regno
++)
1251 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1257 if (from
== ARG_POINTER_REGNUM
&& to
== HARD_SP_REGNUM
)
1259 return get_frame_size () + size
;
1262 if (from
== FRAME_POINTER_REGNUM
&& to
== HARD_SP_REGNUM
)
1269 /* Initialize a variable CUM of type CUMULATIVE_ARGS
1270 for a call to a function whose data type is FNTYPE.
1271 For a library call, FNTYPE is 0. */
1274 m68hc11_init_cumulative_args (cum
, fntype
, libname
)
1275 CUMULATIVE_ARGS
*cum
;
1281 z_replacement_completed
= 0;
1285 /* For a library call, we must find out the type of the return value.
1286 When the return value is bigger than 4 bytes, it is returned in
1287 memory. In that case, the first argument of the library call is a
1288 pointer to the memory location. Because the first argument is passed in
1289 register D, we have to identify this, so that the first function
1290 parameter is not passed in D either. */
1296 if (libname
== 0 || GET_CODE (libname
) != SYMBOL_REF
)
1299 /* If the library ends in 'di' or in 'df', we assume it's
1300 returning some DImode or some DFmode which are 64-bit wide. */
1301 name
= XSTR (libname
, 0);
1302 len
= strlen (name
);
1304 && ((name
[len
- 2] == 'd'
1305 && (name
[len
- 1] == 'f' || name
[len
- 1] == 'i'))
1306 || (name
[len
- 3] == 'd'
1307 && (name
[len
- 2] == 'i' || name
[len
- 2] == 'f'))))
1309 /* We are in. Mark the first parameter register as already used. */
1316 ret_type
= TREE_TYPE (fntype
);
1318 if (ret_type
&& aggregate_value_p (ret_type
))
1325 /* Update the data in CUM to advance over an argument
1326 of mode MODE and data type TYPE.
1327 (TYPE is null for libcalls where that information may not be available.) */
1330 m68hc11_function_arg_advance (cum
, mode
, type
, named
)
1331 CUMULATIVE_ARGS
*cum
;
1332 enum machine_mode mode
;
1334 int named ATTRIBUTE_UNUSED
;
1336 if (mode
!= BLKmode
)
1338 if (cum
->words
== 0 && GET_MODE_SIZE (mode
) == 4)
1341 cum
->words
= GET_MODE_SIZE (mode
);
1345 cum
->words
+= GET_MODE_SIZE (mode
);
1346 if (cum
->words
<= HARD_REG_SIZE
)
1352 cum
->words
+= int_size_in_bytes (type
);
1357 /* Define where to put the arguments to a function.
1358 Value is zero to push the argument on the stack,
1359 or a hard register in which to store the argument.
1361 MODE is the argument's machine mode.
1362 TYPE is the data type of the argument (as a tree).
1363 This is null for libcalls where that information may
1365 CUM is a variable of type CUMULATIVE_ARGS which gives info about
1366 the preceding args and about the function being called.
1367 NAMED is nonzero if this argument is a named parameter
1368 (otherwise it is an extra parameter matching an ellipsis). */
1371 m68hc11_function_arg (cum
, mode
, type
, named
)
1372 const CUMULATIVE_ARGS
*cum
;
1373 enum machine_mode mode
;
1374 tree type ATTRIBUTE_UNUSED
;
1375 int named ATTRIBUTE_UNUSED
;
1377 if (cum
->words
!= 0)
1382 if (mode
!= BLKmode
)
1384 if (GET_MODE_SIZE (mode
) == 2 * HARD_REG_SIZE
)
1385 return gen_rtx (REG
, mode
, HARD_X_REGNUM
);
1387 if (GET_MODE_SIZE (mode
) > HARD_REG_SIZE
)
1391 return gen_rtx (REG
, mode
, HARD_D_REGNUM
);
1396 /* The "standard" implementation of va_start: just assign `nextarg' to
1399 m68hc11_expand_builtin_va_start (stdarg_p
, valist
, nextarg
)
1400 int stdarg_p ATTRIBUTE_UNUSED
;
1406 /* SCz: the default implementation in builtins.c adjust the
1407 nextarg using UNITS_PER_WORD. This works only with -mshort
1408 and fails when integers are 32-bit. Here is the correct way. */
1410 nextarg
= plus_constant (nextarg
, -INT_TYPE_SIZE
/ 8);
1412 t
= build (MODIFY_EXPR
, TREE_TYPE (valist
), valist
,
1413 make_tree (ptr_type_node
, nextarg
));
1414 TREE_SIDE_EFFECTS (t
) = 1;
1416 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1420 m68hc11_va_arg (valist
, type
)
1425 HOST_WIDE_INT align
;
1426 HOST_WIDE_INT rounded_size
;
1430 /* Compute the rounded size of the type. */
1431 align
= PARM_BOUNDARY
/ BITS_PER_UNIT
;
1432 rounded_size
= (((int_size_in_bytes (type
) + align
- 1) / align
) * align
);
1436 pad_direction
= m68hc11_function_arg_padding (TYPE_MODE (type
), type
);
1438 if (pad_direction
== downward
)
1440 /* Small args are padded downward. */
1443 adj
= TREE_INT_CST_LOW (TYPE_SIZE (type
)) / BITS_PER_UNIT
;
1444 if (rounded_size
> align
)
1447 addr_tree
= build (PLUS_EXPR
, TREE_TYPE (addr_tree
), addr_tree
,
1448 build_int_2 (rounded_size
- adj
, 0));
1451 addr
= expand_expr (addr_tree
, NULL_RTX
, Pmode
, EXPAND_NORMAL
);
1452 addr
= copy_to_reg (addr
);
1454 /* Compute new value for AP. */
1455 t
= build (MODIFY_EXPR
, TREE_TYPE (valist
), valist
,
1456 build (PLUS_EXPR
, TREE_TYPE (valist
), valist
,
1457 build_int_2 (rounded_size
, 0)));
1458 TREE_SIDE_EFFECTS (t
) = 1;
1459 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1464 /* If defined, a C expression which determines whether, and in which direction,
1465 to pad out an argument with extra space. The value should be of type
1466 `enum direction': either `upward' to pad above the argument,
1467 `downward' to pad below, or `none' to inhibit padding.
1469 Structures are stored left shifted in their argument slot. */
1471 m68hc11_function_arg_padding (mode
, type
)
1472 enum machine_mode mode
;
1475 if (type
!= 0 && AGGREGATE_TYPE_P (type
))
1478 /* This is the default definition. */
1479 return (!BYTES_BIG_ENDIAN
1482 ? (type
&& TREE_CODE (TYPE_SIZE (type
)) == INTEGER_CST
1483 && int_size_in_bytes (type
) <
1484 (PARM_BOUNDARY
/ BITS_PER_UNIT
)) : GET_MODE_BITSIZE (mode
) <
1485 PARM_BOUNDARY
) ? downward
: upward
));
1489 /* Function prologue and epilogue. */
1491 /* Emit a move after the reload pass has completed. This is used to
1492 emit the prologue and epilogue. */
1494 emit_move_after_reload (to
, from
, scratch
)
1495 rtx to
, from
, scratch
;
1499 if (TARGET_M6812
|| H_REG_P (to
) || H_REG_P (from
))
1501 insn
= emit_move_insn (to
, from
);
1505 emit_move_insn (scratch
, from
);
1506 insn
= emit_move_insn (to
, scratch
);
1509 /* Put a REG_INC note to tell the flow analysis that the instruction
1511 if (IS_STACK_PUSH (to
))
1513 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_INC
,
1514 XEXP (XEXP (to
, 0), 0),
1517 else if (IS_STACK_POP (from
))
1519 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_INC
,
1520 XEXP (XEXP (from
, 0), 0),
1526 m68hc11_total_frame_size ()
1531 size
= get_frame_size ();
1532 if (current_function_interrupt
)
1534 size
+= 3 * HARD_REG_SIZE
;
1536 if (frame_pointer_needed
)
1537 size
+= HARD_REG_SIZE
;
1539 for (regno
= SOFT_REG_FIRST
; regno
<= SOFT_REG_LAST
; regno
++)
1540 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1541 size
+= HARD_REG_SIZE
;
1547 m68hc11_output_function_epilogue (out
, size
)
1548 FILE *out ATTRIBUTE_UNUSED
;
1549 HOST_WIDE_INT size ATTRIBUTE_UNUSED
;
1551 /* We catch the function epilogue generation to have a chance
1552 to clear the z_replacement_completed flag. */
1553 z_replacement_completed
= 0;
1564 if (reload_completed
!= 1)
1567 size
= get_frame_size ();
1571 /* Generate specific prologue for interrupt handlers. */
1572 func_attr
= TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl
));
1573 current_function_interrupt
= lookup_attribute ("interrupt",
1574 func_attr
) != NULL_TREE
;
1575 current_function_trap
= lookup_attribute ("trap", func_attr
) != NULL_TREE
;
1577 /* Get the scratch register to build the frame and push registers.
1578 If the first argument is a 32-bit quantity, the D+X registers
1579 are used. Use Y to compute the frame. Otherwise, X is cheaper.
1580 For 68HC12, this scratch register is not used. */
1581 if (current_function_args_info
.nregs
== 2)
1586 /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
1587 Other soft registers in page0 need not to be saved because they
1588 will be restored by C functions. For a trap handler, we don't
1589 need to preserve these registers because this is a synchronous call. */
1590 if (current_function_interrupt
)
1592 emit_move_after_reload (stack_push_word
, m68hc11_soft_tmp_reg
, scratch
);
1593 emit_move_after_reload (stack_push_word
,
1594 gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
), scratch
);
1595 emit_move_after_reload (stack_push_word
,
1596 gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
),
1600 /* Save current stack frame. */
1601 if (frame_pointer_needed
)
1602 emit_move_after_reload (stack_push_word
, hard_frame_pointer_rtx
, scratch
);
1604 /* Allocate local variables. */
1605 if (TARGET_M6812
&& size
>= 2)
1607 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1608 stack_pointer_rtx
, GEN_INT (-size
)));
1614 insn
= gen_rtx_PARALLEL
1617 gen_rtx_SET (VOIDmode
,
1619 gen_rtx_PLUS (HImode
,
1622 gen_rtx_CLOBBER (VOIDmode
, scratch
)));
1629 /* Allocate by pushing scratch values. */
1630 for (i
= 2; i
<= size
; i
+= 2)
1631 emit_move_after_reload (stack_push_word
, ix_reg
, 0);
1634 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1635 stack_pointer_rtx
, GEN_INT (-1)));
1638 /* Create the frame pointer. */
1639 if (frame_pointer_needed
)
1640 emit_move_after_reload (hard_frame_pointer_rtx
,
1641 stack_pointer_rtx
, scratch
);
1643 /* Push any 2 byte pseudo hard registers that we need to save. */
1644 for (regno
= SOFT_REG_FIRST
; regno
<= SOFT_REG_LAST
; regno
++)
1646 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1648 emit_move_after_reload (stack_push_word
,
1649 gen_rtx (REG
, HImode
, regno
), scratch
);
1662 if (reload_completed
!= 1)
1665 size
= get_frame_size ();
1667 /* If we are returning a value in two registers, we have to preserve the
1668 X register and use the Y register to restore the stack and the saved
1669 registers. Otherwise, use X because it's faster (and smaller). */
1670 if (current_function_return_rtx
== 0)
1672 else if (GET_CODE (current_function_return_rtx
) == MEM
)
1673 return_size
= HARD_REG_SIZE
;
1675 return_size
= GET_MODE_SIZE (GET_MODE (current_function_return_rtx
));
1677 if (return_size
> HARD_REG_SIZE
)
1682 /* Pop any 2 byte pseudo hard registers that we saved. */
1683 for (regno
= SOFT_REG_LAST
; regno
>= SOFT_REG_FIRST
; regno
--)
1685 if (regs_ever_live
[regno
] && !call_used_regs
[regno
])
1687 emit_move_after_reload (gen_rtx (REG
, HImode
, regno
),
1688 stack_pop_word
, scratch
);
1692 /* de-allocate auto variables */
1693 if (TARGET_M6812
&& size
>= 2)
1695 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1696 stack_pointer_rtx
, GEN_INT (size
)));
1702 insn
= gen_rtx_PARALLEL
1705 gen_rtx_SET (VOIDmode
,
1707 gen_rtx_PLUS (HImode
,
1710 gen_rtx_CLOBBER (VOIDmode
, scratch
)));
1717 for (i
= 2; i
<= size
; i
+= 2)
1718 emit_move_after_reload (scratch
, stack_pop_word
, scratch
);
1720 emit_insn (gen_addhi3 (stack_pointer_rtx
,
1721 stack_pointer_rtx
, GEN_INT (1)));
1724 /* Restore previous frame pointer. */
1725 if (frame_pointer_needed
)
1726 emit_move_after_reload (hard_frame_pointer_rtx
, stack_pop_word
, scratch
);
1728 /* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
1729 if (current_function_interrupt
)
1731 emit_move_after_reload (gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
),
1732 stack_pop_word
, scratch
);
1733 emit_move_after_reload (gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
),
1734 stack_pop_word
, scratch
);
1735 emit_move_after_reload (m68hc11_soft_tmp_reg
, stack_pop_word
, scratch
);
1738 /* If the trap handler returns some value, copy the value
1739 in D, X onto the stack so that the rti will pop the return value
1741 else if (current_function_trap
&& return_size
!= 0)
1743 rtx addr_reg
= stack_pointer_rtx
;
1747 emit_move_after_reload (scratch
, stack_pointer_rtx
, 0);
1750 emit_move_after_reload (gen_rtx (MEM
, HImode
,
1751 gen_rtx (PLUS
, HImode
, addr_reg
,
1752 GEN_INT (1))), d_reg
, 0);
1753 if (return_size
> HARD_REG_SIZE
)
1754 emit_move_after_reload (gen_rtx (MEM
, HImode
,
1755 gen_rtx (PLUS
, HImode
, addr_reg
,
1756 GEN_INT (3))), ix_reg
, 0);
1759 emit_jump_insn (gen_return ());
1763 /* Low and High part extraction for 68HC11. These routines are
1764 similar to gen_lowpart and gen_highpart but they have been
1765 fixed to work for constants and 68HC11 specific registers. */
1768 m68hc11_gen_lowpart (mode
, x
)
1769 enum machine_mode mode
;
1772 /* We assume that the low part of an auto-inc mode is the same with
1773 the mode changed and that the caller split the larger mode in the
1775 if (GET_CODE (x
) == MEM
&& m68hc11_auto_inc_p (XEXP (x
, 0)))
1777 return gen_rtx (MEM
, mode
, XEXP (x
, 0));
1780 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1781 floating-point constant. A CONST_DOUBLE is used whenever the
1782 constant requires more than one word in order to be adequately
1784 if (GET_CODE (x
) == CONST_DOUBLE
)
1788 if (GET_MODE_CLASS (GET_MODE (x
)) == MODE_FLOAT
)
1792 if (GET_MODE (x
) == SFmode
)
1794 REAL_VALUE_FROM_CONST_DOUBLE (r
, x
);
1795 REAL_VALUE_TO_TARGET_SINGLE (r
, l
[0]);
1801 split_double (x
, &first
, &second
);
1805 return gen_rtx (CONST_INT
, VOIDmode
, l
[0]);
1807 return gen_rtx (CONST_INT
, VOIDmode
,
1808 trunc_int_for_mode (l
[0], HImode
));
1812 l
[0] = CONST_DOUBLE_LOW (x
);
1815 return gen_rtx (CONST_INT
, VOIDmode
, l
[0]);
1816 else if (mode
== HImode
&& GET_MODE (x
) == SFmode
)
1817 return gen_rtx (CONST_INT
, VOIDmode
,
1818 trunc_int_for_mode (l
[0], HImode
));
1823 if (mode
== QImode
&& D_REG_P (x
))
1824 return gen_rtx (REG
, mode
, HARD_B_REGNUM
);
1826 /* gen_lowpart crashes when it is called with a SUBREG. */
1827 if (GET_CODE (x
) == SUBREG
&& SUBREG_BYTE (x
) != 0)
1830 return gen_rtx_SUBREG (mode
, SUBREG_REG (x
), SUBREG_BYTE (x
) + 4);
1831 else if (mode
== HImode
)
1832 return gen_rtx_SUBREG (mode
, SUBREG_REG (x
), SUBREG_BYTE (x
) + 2);
1836 x
= gen_lowpart (mode
, x
);
1838 /* Return a different rtx to avoid to share it in several insns
1839 (when used by a split pattern). Sharing addresses within
1840 a MEM breaks the Z register replacement (and reloading). */
1841 if (GET_CODE (x
) == MEM
)
1847 m68hc11_gen_highpart (mode
, x
)
1848 enum machine_mode mode
;
1851 /* We assume that the high part of an auto-inc mode is the same with
1852 the mode changed and that the caller split the larger mode in the
1854 if (GET_CODE (x
) == MEM
&& m68hc11_auto_inc_p (XEXP (x
, 0)))
1856 return gen_rtx (MEM
, mode
, XEXP (x
, 0));
1859 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1860 floating-point constant. A CONST_DOUBLE is used whenever the
1861 constant requires more than one word in order to be adequately
1863 if (GET_CODE (x
) == CONST_DOUBLE
)
1867 if (GET_MODE_CLASS (GET_MODE (x
)) == MODE_FLOAT
)
1871 if (GET_MODE (x
) == SFmode
)
1873 REAL_VALUE_FROM_CONST_DOUBLE (r
, x
);
1874 REAL_VALUE_TO_TARGET_SINGLE (r
, l
[1]);
1880 split_double (x
, &first
, &second
);
1884 return gen_rtx (CONST_INT
, VOIDmode
, l
[1]);
1886 return gen_rtx (CONST_INT
, VOIDmode
,
1887 trunc_int_for_mode ((l
[1] >> 16), HImode
));
1891 l
[1] = CONST_DOUBLE_HIGH (x
);
1895 return gen_rtx (CONST_INT
, VOIDmode
, l
[1]);
1896 else if (mode
== HImode
&& GET_MODE_CLASS (GET_MODE (x
)) == MODE_FLOAT
)
1897 return gen_rtx (CONST_INT
, VOIDmode
,
1898 trunc_int_for_mode ((l
[0] >> 16), HImode
));
1902 if (GET_CODE (x
) == CONST_INT
)
1904 HOST_WIDE_INT val
= INTVAL (x
);
1908 return gen_rtx (CONST_INT
, VOIDmode
,
1909 trunc_int_for_mode (val
>> 8, QImode
));
1911 else if (mode
== HImode
)
1913 return gen_rtx (CONST_INT
, VOIDmode
,
1914 trunc_int_for_mode (val
>> 16, HImode
));
1917 if (mode
== QImode
&& D_REG_P (x
))
1918 return gen_rtx (REG
, mode
, HARD_A_REGNUM
);
1920 /* There is no way in GCC to represent the upper part of a word register.
1921 To obtain the 8-bit upper part of a soft register, we change the
1922 reg into a mem rtx. This is possible because they are physically
1923 located in memory. There is no offset because we are big-endian. */
1924 if (mode
== QImode
&& S_REG_P (x
))
1928 /* For 68HC12, avoid the '*' for direct addressing mode. */
1929 pos
= TARGET_M6812
? 1 : 0;
1930 return gen_rtx (MEM
, QImode
,
1931 gen_rtx (SYMBOL_REF
, Pmode
,
1932 ®_names
[REGNO (x
)][pos
]));
1935 /* gen_highpart crashes when it is called with a SUBREG. */
1936 if (GET_CODE (x
) == SUBREG
)
1938 return gen_rtx (SUBREG
, mode
, XEXP (x
, 0), XEXP (x
, 1));
1940 if (GET_CODE (x
) == REG
)
1942 if (REGNO (x
) < FIRST_PSEUDO_REGISTER
)
1943 return gen_rtx (REG
, mode
, REGNO (x
));
1945 return gen_rtx_SUBREG (mode
, x
, 0);
1948 if (GET_CODE (x
) == MEM
)
1950 x
= change_address (x
, mode
, 0);
1952 /* Return a different rtx to avoid to share it in several insns
1953 (when used by a split pattern). Sharing addresses within
1954 a MEM breaks the Z register replacement (and reloading). */
1955 if (GET_CODE (x
) == MEM
)
1963 /* Obscure register manipulation. */
1965 /* Finds backward in the instructions to see if register 'reg' is
1966 dead. This is used when generating code to see if we can use 'reg'
1967 as a scratch register. This allows us to choose a better generation
1968 of code when we know that some register dies or can be clobbered. */
1971 dead_register_here (x
, reg
)
1979 x_reg
= gen_rtx (REG
, SImode
, HARD_X_REGNUM
);
1983 for (p
= PREV_INSN (x
); p
&& GET_CODE (p
) != CODE_LABEL
; p
= PREV_INSN (p
))
1984 if (GET_RTX_CLASS (GET_CODE (p
)) == 'i')
1990 if (GET_CODE (body
) == CALL_INSN
)
1992 if (GET_CODE (body
) == JUMP_INSN
)
1995 if (GET_CODE (body
) == SET
)
1997 rtx dst
= XEXP (body
, 0);
1999 if (GET_CODE (dst
) == REG
&& REGNO (dst
) == REGNO (reg
))
2001 if (x_reg
&& rtx_equal_p (dst
, x_reg
))
2004 if (find_regno_note (p
, REG_DEAD
, REGNO (reg
)))
2007 else if (reg_mentioned_p (reg
, p
)
2008 || (x_reg
&& reg_mentioned_p (x_reg
, p
)))
2012 /* Scan forward to see if the register is set in some insns and never
2014 for (p
= x
/*NEXT_INSN (x) */ ; p
; p
= NEXT_INSN (p
))
2018 if (GET_CODE (p
) == CODE_LABEL
2019 || GET_CODE (p
) == JUMP_INSN
2020 || GET_CODE (p
) == CALL_INSN
|| GET_CODE (p
) == BARRIER
)
2023 if (GET_CODE (p
) != INSN
)
2027 if (GET_CODE (body
) == SET
)
2029 rtx src
= XEXP (body
, 1);
2030 rtx dst
= XEXP (body
, 0);
2032 if (GET_CODE (dst
) == REG
2033 && REGNO (dst
) == REGNO (reg
) && !reg_mentioned_p (reg
, src
))
2037 /* Register is used (may be in source or in dest). */
2038 if (reg_mentioned_p (reg
, p
)
2039 || (x_reg
!= 0 && GET_MODE (p
) == SImode
2040 && reg_mentioned_p (x_reg
, p
)))
2043 return p
== 0 ? 1 : 0;
2047 /* Code generation operations called from machine description file. */
2049 /* Print the name of register 'regno' in the assembly file. */
2051 asm_print_register (file
, regno
)
2055 const char *name
= reg_names
[regno
];
2057 if (TARGET_M6812
&& name
[0] == '*')
2060 asm_fprintf (file
, "%s", name
);
2063 /* A C compound statement to output to stdio stream STREAM the
2064 assembler syntax for an instruction operand X. X is an RTL
2067 CODE is a value that can be used to specify one of several ways
2068 of printing the operand. It is used when identical operands
2069 must be printed differently depending on the context. CODE
2070 comes from the `%' specification that was used to request
2071 printing of the operand. If the specification was just `%DIGIT'
2072 then CODE is 0; if the specification was `%LTR DIGIT' then CODE
2073 is the ASCII code for LTR.
2075 If X is a register, this macro should print the register's name.
2076 The names can be found in an array `reg_names' whose type is
2077 `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
2079 When the machine description has a specification `%PUNCT' (a `%'
2080 followed by a punctuation character), this macro is called with
2081 a null pointer for X and the punctuation character for CODE.
2083 The M68HC11 specific codes are:
2085 'b' for the low part of the operand.
2086 'h' for the high part of the operand
2087 The 'b' or 'h' modifiers have no effect if the operand has
2088 the QImode and is not a S_REG_P (soft register). If the
2089 operand is a hard register, these two modifiers have no effect.
2090 't' generate the temporary scratch register. The operand is
2092 'T' generate the low-part temporary scratch register. The operand is
2096 print_operand (file
, op
, letter
)
2103 asm_print_register (file
, SOFT_TMP_REGNUM
);
2106 else if (letter
== 'T')
2108 asm_print_register (file
, SOFT_TMP_REGNUM
);
2109 asm_fprintf (file
, "+1");
2112 else if (letter
== '#')
2114 asm_fprintf (file
, "%0I");
2117 if (GET_CODE (op
) == REG
)
2119 if (letter
== 'b' && S_REG_P (op
))
2121 asm_print_register (file
, REGNO (op
));
2122 asm_fprintf (file
, "+1");
2126 asm_print_register (file
, REGNO (op
));
2131 if (GET_CODE (op
) == SYMBOL_REF
&& (letter
== 'b' || letter
== 'h'))
2134 asm_fprintf (file
, "%0I%%lo(");
2136 asm_fprintf (file
, "%0I%%hi(");
2138 output_addr_const (file
, op
);
2139 asm_fprintf (file
, ")");
2143 /* Get the low or high part of the operand when 'b' or 'h' modifiers
2144 are specified. If we already have a QImode, there is nothing to do. */
2145 if (GET_MODE (op
) == HImode
|| GET_MODE (op
) == VOIDmode
)
2149 op
= m68hc11_gen_lowpart (QImode
, op
);
2151 else if (letter
== 'h')
2153 op
= m68hc11_gen_highpart (QImode
, op
);
2157 if (GET_CODE (op
) == MEM
)
2159 rtx base
= XEXP (op
, 0);
2160 switch (GET_CODE (base
))
2165 asm_fprintf (file
, "%u,-", GET_MODE_SIZE (GET_MODE (op
)));
2166 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2175 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (op
)));
2176 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2177 asm_fprintf (file
, "-");
2186 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (op
)));
2187 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2188 asm_fprintf (file
, "+");
2197 asm_fprintf (file
, "%u,+", GET_MODE_SIZE (GET_MODE (op
)));
2198 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2205 output_address (base
);
2209 else if (GET_CODE (op
) == CONST_DOUBLE
&& GET_MODE (op
) == SFmode
)
2214 REAL_VALUE_FROM_CONST_DOUBLE (r
, op
);
2215 REAL_VALUE_TO_TARGET_SINGLE (r
, l
);
2216 asm_fprintf (file
, "%I0x%lx", l
);
2218 else if (GET_CODE (op
) == CONST_DOUBLE
2219 && (GET_MODE (op
) == DFmode
|| GET_MODE (op
) == XFmode
))
2224 REAL_VALUE_FROM_CONST_DOUBLE (r
, op
);
2225 REAL_VALUE_TO_DECIMAL (r
, "%.20g", dstr
);
2226 asm_fprintf (file
, "%I0r%s", dstr
);
2230 int need_parenthesize
= 0;
2233 asm_fprintf (file
, "%0I");
2235 need_parenthesize
= must_parenthesize (op
);
2237 if (need_parenthesize
)
2238 asm_fprintf (file
, "(");
2240 output_addr_const (file
, op
);
2241 if (need_parenthesize
)
2242 asm_fprintf (file
, ")");
2246 /* Returns true if the operand 'op' must be printed with parenthesis
2247 arround it. This must be done only if there is a symbol whose name
2248 is a processor register. */
2250 must_parenthesize (op
)
2255 switch (GET_CODE (op
))
2258 name
= XSTR (op
, 0);
2259 /* Avoid a conflict between symbol name and a possible
2261 return (strcasecmp (name
, "a") == 0
2262 || strcasecmp (name
, "b") == 0
2263 || strcasecmp (name
, "d") == 0
2264 || strcasecmp (name
, "x") == 0
2265 || strcasecmp (name
, "y") == 0
2266 || strcasecmp (name
, "pc") == 0
2267 || strcasecmp (name
, "sp") == 0
2268 || strcasecmp (name
, "ccr") == 0) ? 1 : 0;
2272 return must_parenthesize (XEXP (op
, 0))
2273 || must_parenthesize (XEXP (op
, 1));
2279 return must_parenthesize (XEXP (op
, 0));
2290 /* A C compound statement to output to stdio stream STREAM the
2291 assembler syntax for an instruction operand that is a memory
2292 reference whose address is ADDR. ADDR is an RTL expression. */
2295 print_operand_address (file
, addr
)
2301 int need_parenthesis
= 0;
2303 switch (GET_CODE (addr
))
2306 if (!REG_P (addr
) || !REG_OK_FOR_BASE_STRICT_P (addr
))
2309 asm_fprintf (file
, "0,");
2310 asm_print_register (file
, REGNO (addr
));
2314 base
= XEXP (addr
, 0);
2315 switch (GET_CODE (base
))
2320 asm_fprintf (file
, "%u,-", GET_MODE_SIZE (GET_MODE (addr
)));
2321 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2330 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (addr
)));
2331 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2332 asm_fprintf (file
, "-");
2341 asm_fprintf (file
, "%u,", GET_MODE_SIZE (GET_MODE (addr
)));
2342 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2343 asm_fprintf (file
, "+");
2352 asm_fprintf (file
, "%u,+", GET_MODE_SIZE (GET_MODE (addr
)));
2353 asm_print_register (file
, REGNO (XEXP (base
, 0)));
2360 need_parenthesis
= must_parenthesize (base
);
2361 if (need_parenthesis
)
2362 asm_fprintf (file
, "(");
2364 output_addr_const (file
, base
);
2365 if (need_parenthesis
)
2366 asm_fprintf (file
, ")");
2372 base
= XEXP (addr
, 0);
2373 offset
= XEXP (addr
, 1);
2374 if (!G_REG_P (base
) && G_REG_P (offset
))
2376 base
= XEXP (addr
, 1);
2377 offset
= XEXP (addr
, 0);
2379 if ((CONSTANT_ADDRESS_P (base
)) && (CONSTANT_ADDRESS_P (offset
)))
2381 need_parenthesis
= must_parenthesize (addr
);
2383 if (need_parenthesis
)
2384 asm_fprintf (file
, "(");
2386 output_addr_const (file
, base
);
2387 asm_fprintf (file
, "+");
2388 output_addr_const (file
, offset
);
2389 if (need_parenthesis
)
2390 asm_fprintf (file
, ")");
2392 else if (REG_P (base
) && REG_OK_FOR_BASE_STRICT_P (base
))
2398 asm_print_register (file
, REGNO (offset
));
2399 asm_fprintf (file
, ",");
2400 asm_print_register (file
, REGNO (base
));
2407 output_addr_const (file
, offset
);
2408 asm_fprintf (file
, ",");
2409 asm_print_register (file
, REGNO (base
));
2419 if (GET_CODE (addr
) == CONST_INT
2420 && INTVAL (addr
) < 0x8000 && INTVAL (addr
) >= -0x8000)
2422 asm_fprintf (file
, "%d", INTVAL (addr
));
2426 need_parenthesis
= must_parenthesize (addr
);
2427 if (need_parenthesis
)
2428 asm_fprintf (file
, "(");
2430 output_addr_const (file
, addr
);
2431 if (need_parenthesis
)
2432 asm_fprintf (file
, ")");
2439 /* Splitting of some instructions. */
2442 m68hc11_expand_compare (code
, op0
, op1
)
2448 if (GET_MODE_CLASS (GET_MODE (op0
)) == MODE_FLOAT
)
2452 emit_insn (gen_rtx_SET (VOIDmode
, cc0_rtx
,
2453 gen_rtx_COMPARE (VOIDmode
, op0
, op1
)));
2454 ret
= gen_rtx (code
, VOIDmode
, cc0_rtx
, const0_rtx
);
2461 m68hc11_expand_compare_and_branch (code
, op0
, op1
, label
)
2463 rtx op0
, op1
, label
;
2467 switch (GET_MODE (op0
))
2471 tmp
= m68hc11_expand_compare (code
, op0
, op1
);
2472 tmp
= gen_rtx_IF_THEN_ELSE (VOIDmode
, tmp
,
2473 gen_rtx_LABEL_REF (VOIDmode
, label
),
2475 emit_jump_insn (gen_rtx_SET (VOIDmode
, pc_rtx
, tmp
));
2479 /* SCz: from i386.c */
2482 /* Don't expand the comparison early, so that we get better code
2483 when jump or whoever decides to reverse the comparison. */
2488 code
= m68hc11_prepare_fp_compare_args (code
, &m68hc11_compare_op0
,
2489 &m68hc11_compare_op1
);
2491 tmp
= gen_rtx_fmt_ee (code
, m68hc11_fp_compare_mode (code
),
2492 m68hc11_compare_op0
, m68hc11_compare_op1
);
2493 tmp
= gen_rtx_IF_THEN_ELSE (VOIDmode
, tmp
,
2494 gen_rtx_LABEL_REF (VOIDmode
, label
),
2496 tmp
= gen_rtx_SET (VOIDmode
, pc_rtx
, tmp
);
2498 use_fcomi
= ix86_use_fcomi_compare (code
);
2499 vec
= rtvec_alloc (3 + !use_fcomi
);
2500 RTVEC_ELT (vec
, 0) = tmp
;
2502 = gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (CCFPmode
, 18));
2504 = gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (CCFPmode
, 17));
2507 = gen_rtx_CLOBBER (VOIDmode
, gen_rtx_SCRATCH (HImode
));
2509 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode
, vec
));
2515 /* Expand SImode branch into multiple compare+branch. */
2517 rtx lo
[2], hi
[2], label2
;
2518 enum rtx_code code1
, code2
, code3
;
2520 if (CONSTANT_P (op0
) && !CONSTANT_P (op1
))
2525 code
= swap_condition (code
);
2527 lo
[0] = m68hc11_gen_lowpart (HImode
, op0
);
2528 lo
[1] = m68hc11_gen_lowpart (HImode
, op1
);
2529 hi
[0] = m68hc11_gen_highpart (HImode
, op0
);
2530 hi
[1] = m68hc11_gen_highpart (HImode
, op1
);
2532 /* Otherwise, if we are doing less-than, op1 is a constant and the
2533 low word is zero, then we can just examine the high word. */
2535 if (GET_CODE (hi
[1]) == CONST_INT
&& lo
[1] == const0_rtx
2536 && (code
== LT
|| code
== LTU
))
2538 return m68hc11_expand_compare_and_branch (code
, hi
[0], hi
[1],
2542 /* Otherwise, we need two or three jumps. */
2544 label2
= gen_label_rtx ();
2547 code2
= swap_condition (code
);
2548 code3
= unsigned_condition (code
);
2589 * if (hi(a) < hi(b)) goto true;
2590 * if (hi(a) > hi(b)) goto false;
2591 * if (lo(a) < lo(b)) goto true;
2595 m68hc11_expand_compare_and_branch (code1
, hi
[0], hi
[1], label
);
2597 m68hc11_expand_compare_and_branch (code2
, hi
[0], hi
[1], label2
);
2599 m68hc11_expand_compare_and_branch (code3
, lo
[0], lo
[1], label
);
2602 emit_label (label2
);
2613 /* Split a DI, SI or HI move into several smaller move operations.
2614 The scratch register 'scratch' is used as a temporary to load
2615 store intermediate values. It must be a hard register. */
2617 m68hc11_split_move (to
, from
, scratch
)
2618 rtx to
, from
, scratch
;
2620 rtx low_to
, low_from
;
2621 rtx high_to
, high_from
;
2622 enum machine_mode mode
;
2625 mode
= GET_MODE (to
);
2626 if (GET_MODE_SIZE (mode
) == 8)
2628 else if (GET_MODE_SIZE (mode
) == 4)
2634 && IS_STACK_PUSH (to
)
2635 && reg_mentioned_p (gen_rtx (REG
, HImode
, HARD_SP_REGNUM
), from
))
2641 else if (mode
== HImode
)
2649 low_to
= m68hc11_gen_lowpart (mode
, to
);
2650 high_to
= m68hc11_gen_highpart (mode
, to
);
2652 low_from
= m68hc11_gen_lowpart (mode
, from
);
2653 if (mode
== SImode
&& GET_CODE (from
) == CONST_INT
)
2655 if (INTVAL (from
) >= 0)
2656 high_from
= const0_rtx
;
2658 high_from
= constm1_rtx
;
2661 high_from
= m68hc11_gen_highpart (mode
, from
);
2665 high_from
= adjust_address (high_from
, mode
, offset
);
2666 low_from
= high_from
;
2670 m68hc11_split_move (low_to
, low_from
, scratch
);
2671 m68hc11_split_move (high_to
, high_from
, scratch
);
2673 else if (H_REG_P (to
) || H_REG_P (from
)
2675 && (!m68hc11_register_indirect_p (from
, GET_MODE (from
))
2676 || m68hc11_small_indexed_indirect_p (from
,
2678 && (!m68hc11_register_indirect_p (to
, GET_MODE (to
))
2679 || m68hc11_small_indexed_indirect_p (to
, GET_MODE (to
)))))
2681 emit_move_insn (low_to
, low_from
);
2682 emit_move_insn (high_to
, high_from
);
2688 emit_move_insn (scratch
, low_from
);
2689 insn
= emit_move_insn (low_to
, scratch
);
2691 emit_move_insn (scratch
, high_from
);
2692 insn
= emit_move_insn (high_to
, scratch
);
2697 simplify_logical (mode
, code
, operand
, result
)
2698 enum machine_mode mode
;
2707 if (GET_CODE (operand
) != CONST_INT
)
2715 val
= INTVAL (operand
);
2719 if ((val
& mask
) == 0)
2721 if ((val
& mask
) == mask
)
2722 *result
= constm1_rtx
;
2726 if ((val
& mask
) == 0)
2727 *result
= const0_rtx
;
2728 if ((val
& mask
) == mask
)
2733 if ((val
& mask
) == 0)
2741 m68hc11_emit_logical (mode
, code
, operands
)
2742 enum machine_mode mode
;
2749 need_copy
= (rtx_equal_p (operands
[0], operands
[1])
2750 || rtx_equal_p (operands
[0], operands
[2])) ? 0 : 1;
2752 operands
[1] = simplify_logical (mode
, code
, operands
[1], &result
);
2753 operands
[2] = simplify_logical (mode
, code
, operands
[2], &result
);
2755 if (result
&& GET_CODE (result
) == CONST_INT
)
2757 if (!H_REG_P (operands
[0]) && operands
[3]
2758 && (INTVAL (result
) != 0 || IS_STACK_PUSH (operands
[0])))
2760 emit_move_insn (operands
[3], result
);
2761 emit_move_insn (operands
[0], operands
[3]);
2765 emit_move_insn (operands
[0], result
);
2768 else if (operands
[1] != 0 && operands
[2] != 0)
2772 if (!H_REG_P (operands
[0]) && operands
[3])
2774 emit_move_insn (operands
[3], operands
[1]);
2775 emit_insn (gen_rtx (SET
, mode
,
2777 gen_rtx (code
, mode
,
2778 operands
[3], operands
[2])));
2779 insn
= emit_move_insn (operands
[0], operands
[3]);
2783 insn
= emit_insn (gen_rtx (SET
, mode
,
2785 gen_rtx (code
, mode
,
2786 operands
[0], operands
[2])));
2790 /* The logical operation is similar to a copy. */
2795 if (GET_CODE (operands
[1]) == CONST_INT
)
2800 if (!H_REG_P (operands
[0]) && !H_REG_P (src
))
2802 emit_move_insn (operands
[3], src
);
2803 emit_move_insn (operands
[0], operands
[3]);
2807 emit_move_insn (operands
[0], src
);
2813 m68hc11_split_logical (mode
, code
, operands
)
2814 enum machine_mode mode
;
2821 low
[0] = m68hc11_gen_lowpart (mode
, operands
[0]);
2822 low
[1] = m68hc11_gen_lowpart (mode
, operands
[1]);
2823 low
[2] = m68hc11_gen_lowpart (mode
, operands
[2]);
2825 high
[0] = m68hc11_gen_highpart (mode
, operands
[0]);
2827 if (mode
== SImode
&& GET_CODE (operands
[1]) == CONST_INT
)
2829 if (INTVAL (operands
[1]) >= 0)
2830 high
[1] = const0_rtx
;
2832 high
[1] = constm1_rtx
;
2835 high
[1] = m68hc11_gen_highpart (mode
, operands
[1]);
2837 if (mode
== SImode
&& GET_CODE (operands
[2]) == CONST_INT
)
2839 if (INTVAL (operands
[2]) >= 0)
2840 high
[2] = const0_rtx
;
2842 high
[2] = constm1_rtx
;
2845 high
[2] = m68hc11_gen_highpart (mode
, operands
[2]);
2847 low
[3] = operands
[3];
2848 high
[3] = operands
[3];
2851 m68hc11_split_logical (HImode
, code
, low
);
2852 m68hc11_split_logical (HImode
, code
, high
);
2856 m68hc11_emit_logical (mode
, code
, low
);
2857 m68hc11_emit_logical (mode
, code
, high
);
2861 /* Code generation. */
2864 m68hc11_output_swap (insn
, operands
)
2865 rtx insn ATTRIBUTE_UNUSED
;
2868 /* We have to be careful with the cc_status. An address register swap
2869 is generated for some comparison. The comparison is made with D
2870 but the branch really uses the address register. See the split
2871 pattern for compare. The xgdx/xgdy preserve the flags but after
2872 the exchange, the flags will reflect to the value of X and not D.
2873 Tell this by setting the cc_status according to the cc_prev_status. */
2874 if (X_REG_P (operands
[1]) || X_REG_P (operands
[0]))
2876 if (cc_prev_status
.value1
!= 0
2877 && (D_REG_P (cc_prev_status
.value1
)
2878 || X_REG_P (cc_prev_status
.value1
)))
2880 cc_status
= cc_prev_status
;
2881 if (D_REG_P (cc_status
.value1
))
2882 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2885 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2891 output_asm_insn ("xgdx", operands
);
2895 if (cc_prev_status
.value1
!= 0
2896 && (D_REG_P (cc_prev_status
.value1
)
2897 || Y_REG_P (cc_prev_status
.value1
)))
2899 cc_status
= cc_prev_status
;
2900 if (D_REG_P (cc_status
.value1
))
2901 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2904 cc_status
.value1
= gen_rtx (REG
, GET_MODE (cc_status
.value1
),
2910 output_asm_insn ("xgdy", operands
);
2914 /* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
2915 This is used to decide whether a move that set flags should be used
2918 next_insn_test_reg (insn
, reg
)
2924 insn
= next_nonnote_insn (insn
);
2925 if (GET_CODE (insn
) != INSN
)
2928 body
= PATTERN (insn
);
2929 if (sets_cc0_p (body
) != 1)
2932 if (rtx_equal_p (XEXP (body
, 1), reg
) == 0)
2938 /* Generate the code to move a 16-bit operand into another one. */
2941 m68hc11_gen_movhi (insn
, operands
)
2947 /* Move a register or memory to the same location.
2948 This is possible because such insn can appear
2949 in a non-optimizing mode. */
2950 if (operands
[0] == operands
[1] || rtx_equal_p (operands
[0], operands
[1]))
2952 cc_status
= cc_prev_status
;
2958 if (IS_STACK_PUSH (operands
[0]) && H_REG_P (operands
[1]))
2960 cc_status
= cc_prev_status
;
2961 switch (REGNO (operands
[1]))
2966 output_asm_insn ("psh%1", operands
);
2973 if (IS_STACK_POP (operands
[1]) && H_REG_P (operands
[0]))
2975 cc_status
= cc_prev_status
;
2976 switch (REGNO (operands
[0]))
2981 output_asm_insn ("pul%0", operands
);
2988 if (H_REG_P (operands
[0]) && H_REG_P (operands
[1]))
2990 m68hc11_notice_keep_cc (operands
[0]);
2991 output_asm_insn ("tfr\t%1,%0", operands
);
2993 else if (H_REG_P (operands
[0]))
2995 if (SP_REG_P (operands
[0]))
2996 output_asm_insn ("lds\t%1", operands
);
2998 output_asm_insn ("ld%0\t%1", operands
);
3000 else if (H_REG_P (operands
[1]))
3002 if (SP_REG_P (operands
[1]))
3003 output_asm_insn ("sts\t%0", operands
);
3005 output_asm_insn ("st%1\t%0", operands
);
3009 rtx from
= operands
[1];
3010 rtx to
= operands
[0];
3012 if ((m68hc11_register_indirect_p (from
, GET_MODE (from
))
3013 && !m68hc11_small_indexed_indirect_p (from
, GET_MODE (from
)))
3014 || (m68hc11_register_indirect_p (to
, GET_MODE (to
))
3015 && !m68hc11_small_indexed_indirect_p (to
, GET_MODE (to
))))
3021 ops
[0] = operands
[2];
3024 m68hc11_gen_movhi (insn
, ops
);
3026 ops
[1] = operands
[2];
3027 m68hc11_gen_movhi (insn
, ops
);
3031 /* !!!! SCz wrong here. */
3032 fatal_insn ("move insn not handled", insn
);
3037 if (GET_CODE (from
) == CONST_INT
&& INTVAL (from
) == 0)
3039 output_asm_insn ("clr\t%h0", operands
);
3040 output_asm_insn ("clr\t%b0", operands
);
3044 m68hc11_notice_keep_cc (operands
[0]);
3045 output_asm_insn ("movw\t%1,%0", operands
);
3052 if (IS_STACK_POP (operands
[1]) && H_REG_P (operands
[0]))
3054 cc_status
= cc_prev_status
;
3055 switch (REGNO (operands
[0]))
3059 output_asm_insn ("pul%0", operands
);
3062 output_asm_insn ("pula", operands
);
3063 output_asm_insn ("pulb", operands
);
3070 /* Some moves to a hard register are special. Not all of them
3071 are really supported and we have to use a temporary
3072 location to provide them (either the stack of a temp var). */
3073 if (H_REG_P (operands
[0]))
3075 switch (REGNO (operands
[0]))
3078 if (X_REG_P (operands
[1]))
3080 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_X_REGNUM
))
3082 m68hc11_output_swap (insn
, operands
);
3084 else if (next_insn_test_reg (insn
, operands
[0]))
3086 output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands
);
3090 m68hc11_notice_keep_cc (operands
[0]);
3091 output_asm_insn ("pshx\n\tpula\n\tpulb", operands
);
3094 else if (Y_REG_P (operands
[1]))
3096 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_Y_REGNUM
))
3098 m68hc11_output_swap (insn
, operands
);
3102 /* %t means *ZTMP scratch register. */
3103 output_asm_insn ("sty\t%t1", operands
);
3104 output_asm_insn ("ldd\t%t1", operands
);
3107 else if (SP_REG_P (operands
[1]))
3112 if (optimize
== 0 || dead_register_here (insn
, ix_reg
) == 0)
3113 output_asm_insn ("xgdx", operands
);
3114 output_asm_insn ("tsx", operands
);
3115 output_asm_insn ("xgdx", operands
);
3117 else if (IS_STACK_POP (operands
[1]))
3119 output_asm_insn ("pula\n\tpulb", operands
);
3121 else if (GET_CODE (operands
[1]) == CONST_INT
3122 && INTVAL (operands
[1]) == 0)
3124 output_asm_insn ("clra\n\tclrb", operands
);
3128 output_asm_insn ("ldd\t%1", operands
);
3133 if (D_REG_P (operands
[1]))
3135 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_D_REGNUM
))
3137 m68hc11_output_swap (insn
, operands
);
3139 else if (next_insn_test_reg (insn
, operands
[0]))
3141 output_asm_insn ("std\t%t0\n\tldx\t%t0", operands
);
3145 m68hc11_notice_keep_cc (operands
[0]);
3146 output_asm_insn ("pshb", operands
);
3147 output_asm_insn ("psha", operands
);
3148 output_asm_insn ("pulx", operands
);
3151 else if (Y_REG_P (operands
[1]))
3153 /* When both D and Y are dead, use the sequence xgdy, xgdx
3154 to move Y into X. The D and Y registers are modified. */
3155 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_Y_REGNUM
)
3156 && dead_register_here (insn
, d_reg
))
3158 output_asm_insn ("xgdy", operands
);
3159 output_asm_insn ("xgdx", operands
);
3164 output_asm_insn ("sty\t%t1", operands
);
3165 output_asm_insn ("ldx\t%t1", operands
);
3168 else if (SP_REG_P (operands
[1]))
3170 /* tsx, tsy preserve the flags */
3171 cc_status
= cc_prev_status
;
3172 output_asm_insn ("tsx", operands
);
3176 output_asm_insn ("ldx\t%1", operands
);
3181 if (D_REG_P (operands
[1]))
3183 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_D_REGNUM
))
3185 m68hc11_output_swap (insn
, operands
);
3189 output_asm_insn ("std\t%t1", operands
);
3190 output_asm_insn ("ldy\t%t1", operands
);
3193 else if (X_REG_P (operands
[1]))
3195 /* When both D and X are dead, use the sequence xgdx, xgdy
3196 to move X into Y. The D and X registers are modified. */
3197 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_X_REGNUM
)
3198 && dead_register_here (insn
, d_reg
))
3200 output_asm_insn ("xgdx", operands
);
3201 output_asm_insn ("xgdy", operands
);
3206 output_asm_insn ("stx\t%t1", operands
);
3207 output_asm_insn ("ldy\t%t1", operands
);
3210 else if (SP_REG_P (operands
[1]))
3212 /* tsx, tsy preserve the flags */
3213 cc_status
= cc_prev_status
;
3214 output_asm_insn ("tsy", operands
);
3218 output_asm_insn ("ldy\t%1", operands
);
3222 case HARD_SP_REGNUM
:
3223 if (D_REG_P (operands
[1]))
3225 m68hc11_notice_keep_cc (operands
[0]);
3226 output_asm_insn ("xgdx", operands
);
3227 output_asm_insn ("txs", operands
);
3228 output_asm_insn ("xgdx", operands
);
3230 else if (X_REG_P (operands
[1]))
3232 /* tys, txs preserve the flags */
3233 cc_status
= cc_prev_status
;
3234 output_asm_insn ("txs", operands
);
3236 else if (Y_REG_P (operands
[1]))
3238 /* tys, txs preserve the flags */
3239 cc_status
= cc_prev_status
;
3240 output_asm_insn ("tys", operands
);
3244 /* lds sets the flags but the des does not. */
3246 output_asm_insn ("lds\t%1", operands
);
3247 output_asm_insn ("des", operands
);
3252 fatal_insn ("invalid register in the move instruction", insn
);
3257 if (SP_REG_P (operands
[1]) && REG_P (operands
[0])
3258 && REGNO (operands
[0]) == HARD_FRAME_POINTER_REGNUM
)
3260 output_asm_insn ("sts\t%0", operands
);
3264 if (IS_STACK_PUSH (operands
[0]) && H_REG_P (operands
[1]))
3266 cc_status
= cc_prev_status
;
3267 switch (REGNO (operands
[1]))
3271 output_asm_insn ("psh%1", operands
);
3274 output_asm_insn ("pshb", operands
);
3275 output_asm_insn ("psha", operands
);
3283 /* Operand 1 must be a hard register. */
3284 if (!H_REG_P (operands
[1]))
3286 fatal_insn ("invalid operand in the instruction", insn
);
3289 reg
= REGNO (operands
[1]);
3293 output_asm_insn ("std\t%0", operands
);
3297 output_asm_insn ("stx\t%0", operands
);
3301 output_asm_insn ("sty\t%0", operands
);
3304 case HARD_SP_REGNUM
:
3308 if (REG_P (operands
[0]) && REGNO (operands
[0]) == SOFT_TMP_REGNUM
)
3310 output_asm_insn ("pshx", operands
);
3311 output_asm_insn ("tsx", operands
);
3312 output_asm_insn ("inx", operands
);
3313 output_asm_insn ("inx", operands
);
3314 output_asm_insn ("stx\t%0", operands
);
3315 output_asm_insn ("pulx", operands
);
3318 else if (reg_mentioned_p (ix_reg
, operands
[0]))
3320 output_asm_insn ("sty\t%t0", operands
);
3321 output_asm_insn ("tsy", operands
);
3322 output_asm_insn ("sty\t%0", operands
);
3323 output_asm_insn ("ldy\t%t0", operands
);
3327 output_asm_insn ("stx\t%t0", operands
);
3328 output_asm_insn ("tsx", operands
);
3329 output_asm_insn ("stx\t%0", operands
);
3330 output_asm_insn ("ldx\t%t0", operands
);
3336 fatal_insn ("invalid register in the move instruction", insn
);
3342 m68hc11_gen_movqi (insn
, operands
)
3346 /* Move a register or memory to the same location.
3347 This is possible because such insn can appear
3348 in a non-optimizing mode. */
3349 if (operands
[0] == operands
[1] || rtx_equal_p (operands
[0], operands
[1]))
3351 cc_status
= cc_prev_status
;
3358 if (H_REG_P (operands
[0]) && H_REG_P (operands
[1]))
3360 m68hc11_notice_keep_cc (operands
[0]);
3361 output_asm_insn ("tfr\t%1,%0", operands
);
3363 else if (H_REG_P (operands
[0]))
3365 if (Q_REG_P (operands
[0]))
3366 output_asm_insn ("lda%0\t%b1", operands
);
3367 else if (D_REG_P (operands
[0]))
3368 output_asm_insn ("ldab\t%b1", operands
);
3372 else if (H_REG_P (operands
[1]))
3374 if (Q_REG_P (operands
[1]))
3375 output_asm_insn ("sta%1\t%b0", operands
);
3376 else if (D_REG_P (operands
[1]))
3377 output_asm_insn ("stab\t%b0", operands
);
3383 rtx from
= operands
[1];
3384 rtx to
= operands
[0];
3386 if ((m68hc11_register_indirect_p (from
, GET_MODE (from
))
3387 && !m68hc11_small_indexed_indirect_p (from
, GET_MODE (from
)))
3388 || (m68hc11_register_indirect_p (to
, GET_MODE (to
))
3389 && !m68hc11_small_indexed_indirect_p (to
, GET_MODE (to
))))
3395 ops
[0] = operands
[2];
3398 m68hc11_gen_movqi (insn
, ops
);
3400 ops
[1] = operands
[2];
3401 m68hc11_gen_movqi (insn
, ops
);
3405 /* !!!! SCz wrong here. */
3406 fatal_insn ("move insn not handled", insn
);
3411 if (GET_CODE (from
) == CONST_INT
&& INTVAL (from
) == 0)
3413 output_asm_insn ("clr\t%b0", operands
);
3417 m68hc11_notice_keep_cc (operands
[0]);
3418 output_asm_insn ("movb\t%b1,%b0", operands
);
3426 if (H_REG_P (operands
[0]))
3428 switch (REGNO (operands
[0]))
3432 if (X_REG_P (operands
[1]))
3434 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_X_REGNUM
))
3436 m68hc11_output_swap (insn
, operands
);
3440 output_asm_insn ("stx\t%t1", operands
);
3441 output_asm_insn ("ldab\t%T0", operands
);
3444 else if (Y_REG_P (operands
[1]))
3446 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_Y_REGNUM
))
3448 m68hc11_output_swap (insn
, operands
);
3452 output_asm_insn ("sty\t%t1", operands
);
3453 output_asm_insn ("ldab\t%T0", operands
);
3456 else if (!DB_REG_P (operands
[1]) && !D_REG_P (operands
[1])
3457 && !DA_REG_P (operands
[1]))
3459 output_asm_insn ("ldab\t%b1", operands
);
3461 else if (DA_REG_P (operands
[1]))
3463 output_asm_insn ("tab", operands
);
3467 cc_status
= cc_prev_status
;
3473 if (X_REG_P (operands
[1]))
3475 output_asm_insn ("stx\t%t1", operands
);
3476 output_asm_insn ("ldaa\t%T0", operands
);
3478 else if (Y_REG_P (operands
[1]))
3480 output_asm_insn ("sty\t%t1", operands
);
3481 output_asm_insn ("ldaa\t%T0", operands
);
3483 else if (!DB_REG_P (operands
[1]) && !D_REG_P (operands
[1])
3484 && !DA_REG_P (operands
[1]))
3486 output_asm_insn ("ldaa\t%b1", operands
);
3488 else if (!DA_REG_P (operands
[1]))
3490 output_asm_insn ("tba", operands
);
3494 cc_status
= cc_prev_status
;
3499 if (D_REG_P (operands
[1]))
3501 if (optimize
&& find_regno_note (insn
, REG_DEAD
, HARD_D_REGNUM
))
3503 m68hc11_output_swap (insn
, operands
);
3507 output_asm_insn ("stab\t%T1", operands
);
3508 output_asm_insn ("ldx\t%t1", operands
);
3512 else if (Y_REG_P (operands
[1]))
3514 output_asm_insn ("sty\t%t0", operands
);
3515 output_asm_insn ("ldx\t%t0", operands
);
3517 else if (GET_CODE (operands
[1]) == CONST_INT
)
3519 output_asm_insn ("ldx\t%1", operands
);
3521 else if (dead_register_here (insn
, d_reg
))
3523 output_asm_insn ("ldab\t%b1", operands
);
3524 output_asm_insn ("xgdx", operands
);
3526 else if (!reg_mentioned_p (operands
[0], operands
[1]))
3528 output_asm_insn ("xgdx", operands
);
3529 output_asm_insn ("ldab\t%b1", operands
);
3530 output_asm_insn ("xgdx", operands
);
3534 output_asm_insn ("pshb", operands
);
3535 output_asm_insn ("ldab\t%b1", operands
);
3536 output_asm_insn ("stab\t%T1", operands
);
3537 output_asm_insn ("ldx\t%t1", operands
);
3538 output_asm_insn ("pulb", operands
);
3544 if (D_REG_P (operands
[1]))
3546 output_asm_insn ("stab\t%T1", operands
);
3547 output_asm_insn ("ldy\t%t1", operands
);
3550 else if (X_REG_P (operands
[1]))
3552 output_asm_insn ("stx\t%t1", operands
);
3553 output_asm_insn ("ldy\t%t1", operands
);
3556 else if (GET_CODE (operands
[1]) == CONST_INT
)
3558 output_asm_insn ("ldy\t%1", operands
);
3560 else if (dead_register_here (insn
, d_reg
))
3562 output_asm_insn ("ldab\t%b1", operands
);
3563 output_asm_insn ("xgdy", operands
);
3565 else if (!reg_mentioned_p (operands
[0], operands
[1]))
3567 output_asm_insn ("xgdy", operands
);
3568 output_asm_insn ("ldab\t%b1", operands
);
3569 output_asm_insn ("xgdy", operands
);
3573 output_asm_insn ("pshb", operands
);
3574 output_asm_insn ("ldab\t%b1", operands
);
3575 output_asm_insn ("stab\t%T1", operands
);
3576 output_asm_insn ("ldy\t%t1", operands
);
3577 output_asm_insn ("pulb", operands
);
3583 fatal_insn ("invalid register in the instruction", insn
);
3587 else if (H_REG_P (operands
[1]))
3589 switch (REGNO (operands
[1]))
3593 output_asm_insn ("stab\t%b0", operands
);
3597 output_asm_insn ("staa\t%b0", operands
);
3601 output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands
);
3605 output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands
);
3609 fatal_insn ("invalid register in the move instruction", insn
);
3616 fatal_insn ("operand 1 must be a hard register", insn
);
3620 /* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
3621 The source and destination must be D or A and the shift must
3624 m68hc11_gen_rotate (code
, insn
, operands
)
3631 if (GET_CODE (operands
[2]) != CONST_INT
3632 || (!D_REG_P (operands
[0]) && !DA_REG_P (operands
[0])))
3633 fatal_insn ("invalid rotate insn", insn
);
3635 val
= INTVAL (operands
[2]);
3636 if (code
== ROTATERT
)
3637 val
= GET_MODE_SIZE (GET_MODE (operands
[0])) * BITS_PER_UNIT
- val
;
3639 if (GET_MODE (operands
[0]) != QImode
)
3642 /* Rotate by 8-bits if the shift is within [5..11]. */
3643 if (val
>= 5 && val
<= 11)
3645 output_asm_insn ("psha", operands
);
3646 output_asm_insn ("tba", operands
);
3647 output_asm_insn ("pulb", operands
);
3651 /* If the shift is big, invert the rotation. */
3659 /* Set the carry to bit-15, but don't change D yet. */
3660 if (GET_MODE (operands
[0]) != QImode
)
3662 output_asm_insn ("asra", operands
);
3663 output_asm_insn ("rola", operands
);
3668 /* Rotate B first to move the carry to bit-0. */
3669 if (D_REG_P (operands
[0]))
3670 output_asm_insn ("rolb", operands
);
3672 if (GET_MODE (operands
[0]) != QImode
|| DA_REG_P (operands
[0]))
3673 output_asm_insn ("rola", operands
);
3678 /* Set the carry to bit-8 of D. */
3679 if (val
!= 0 && GET_MODE (operands
[0]) != QImode
)
3681 output_asm_insn ("tap", operands
);
3686 /* Rotate B first to move the carry to bit-7. */
3687 if (D_REG_P (operands
[0]))
3688 output_asm_insn ("rorb", operands
);
3690 if (GET_MODE (operands
[0]) != QImode
|| DA_REG_P (operands
[0]))
3691 output_asm_insn ("rora", operands
);
3698 /* Store in cc_status the expressions that the condition codes will
3699 describe after execution of an instruction whose pattern is EXP.
3700 Do not alter them if the instruction would not alter the cc's. */
3703 m68hc11_notice_update_cc (exp
, insn
)
3705 rtx insn ATTRIBUTE_UNUSED
;
3707 /* recognize SET insn's. */
3708 if (GET_CODE (exp
) == SET
)
3710 /* Jumps do not alter the cc's. */
3711 if (SET_DEST (exp
) == pc_rtx
)
3714 /* NOTE: most instructions don't affect the carry bit, but the
3715 bhi/bls/bhs/blo instructions use it. This isn't mentioned in
3716 the conditions.h header. */
3718 /* Function calls clobber the cc's. */
3719 else if (GET_CODE (SET_SRC (exp
)) == CALL
)
3724 /* Tests and compares set the cc's in predictable ways. */
3725 else if (SET_DEST (exp
) == cc0_rtx
)
3727 cc_status
.flags
= 0;
3728 cc_status
.value1
= XEXP (exp
, 0);
3729 cc_status
.value2
= XEXP (exp
, 1);
3733 /* All other instructions affect the condition codes. */
3734 cc_status
.flags
= 0;
3735 cc_status
.value1
= XEXP (exp
, 0);
3736 cc_status
.value2
= XEXP (exp
, 1);
3741 /* Default action if we haven't recognized something
3742 and returned earlier. */
3746 if (cc_status
.value2
!= 0)
3747 switch (GET_CODE (cc_status
.value2
))
3749 /* These logical operations can generate several insns.
3750 The flags are setup according to what is generated. */
3756 /* The (not ...) generates several 'com' instructions for
3757 non QImode. We have to invalidate the flags. */
3759 if (GET_MODE (cc_status
.value2
) != QImode
)
3771 if (GET_MODE (cc_status
.value2
) != VOIDmode
)
3772 cc_status
.flags
|= CC_NO_OVERFLOW
;
3775 /* The asl sets the overflow bit in such a way that this
3776 makes the flags unusable for a next compare insn. */
3780 if (GET_MODE (cc_status
.value2
) != VOIDmode
)
3781 cc_status
.flags
|= CC_NO_OVERFLOW
;
3784 /* A load/store instruction does not affect the carry. */
3789 cc_status
.flags
|= CC_NO_OVERFLOW
;
3795 if (cc_status
.value1
&& GET_CODE (cc_status
.value1
) == REG
3797 && reg_overlap_mentioned_p (cc_status
.value1
, cc_status
.value2
))
3798 cc_status
.value2
= 0;
3801 /* The current instruction does not affect the flags but changes
3802 the register 'reg'. See if the previous flags can be kept for the
3803 next instruction to avoid a comparison. */
3805 m68hc11_notice_keep_cc (reg
)
3809 || cc_prev_status
.value1
== 0
3810 || rtx_equal_p (reg
, cc_prev_status
.value1
)
3811 || (cc_prev_status
.value2
3812 && reg_mentioned_p (reg
, cc_prev_status
.value2
)))
3815 cc_status
= cc_prev_status
;
3820 /* Machine Specific Reorg. */
3822 /* Z register replacement:
3824 GCC treats the Z register as an index base address register like
3825 X or Y. In general, it uses it during reload to compute the address
3826 of some operand. This helps the reload pass to avoid to fall into the
3827 register spill failure.
3829 The Z register is in the A_REGS class. In the machine description,
3830 the 'A' constraint matches it. The 'x' or 'y' constraints do not.
3832 It can appear everywhere an X or Y register can appear, except for
3833 some templates in the clobber section (when a clobber of X or Y is asked).
3834 For a given instruction, the template must ensure that no more than
3835 2 'A' registers are used. Otherwise, the register replacement is not
3838 To replace the Z register, the algorithm is not terrific:
3839 1. Insns that do not use the Z register are not changed
3840 2. When a Z register is used, we scan forward the insns to see
3841 a potential register to use: either X or Y and sometimes D.
3842 We stop when a call, a label or a branch is seen, or when we
3843 detect that both X and Y are used (probably at different times, but it does
3845 3. The register that will be used for the replacement of Z is saved
3846 in a .page0 register or on the stack. If the first instruction that
3847 used Z, uses Z as an input, the value is loaded from another .page0
3848 register. The replacement register is pushed on the stack in the
3849 rare cases where a compare insn uses Z and we couldn't find if X/Y
3851 4. The Z register is replaced in all instructions until we reach
3852 the end of the Z-block, as detected by step 2.
3853 5. If we detect that Z is still alive, its value is saved.
3854 If the replacement register is alive, its old value is loaded.
3856 The Z register can be disabled with -ffixed-z.
3866 int must_restore_reg
;
3877 int save_before_last
;
3878 int z_loaded_with_sp
;
3881 static rtx z_reg_qi
;
3883 static int m68hc11_check_z_replacement
PARAMS ((rtx
, struct replace_info
*));
3884 static void m68hc11_find_z_replacement
PARAMS ((rtx
, struct replace_info
*));
3885 static void m68hc11_z_replacement
PARAMS ((rtx
));
3886 static void m68hc11_reassign_regs
PARAMS ((rtx
));
3888 int z_replacement_completed
= 0;
3890 /* Analyze the insn to find out which replacement register to use and
3891 the boundaries of the replacement.
3892 Returns 0 if we reached the last insn to be replaced, 1 if we can
3893 continue replacement in next insns. */
3896 m68hc11_check_z_replacement (insn
, info
)
3898 struct replace_info
*info
;
3900 int this_insn_uses_ix
;
3901 int this_insn_uses_iy
;
3902 int this_insn_uses_z
;
3903 int this_insn_uses_z_in_dst
;
3904 int this_insn_uses_d
;
3908 /* A call is said to clobber the Z register, we don't need
3909 to save the value of Z. We also don't need to restore
3910 the replacement register (unless it is used by the call). */
3911 if (GET_CODE (insn
) == CALL_INSN
)
3913 body
= PATTERN (insn
);
3915 info
->can_use_d
= 0;
3917 /* If the call is an indirect call with Z, we have to use the
3918 Y register because X can be used as an input (D+X).
3919 We also must not save Z nor restore Y. */
3920 if (reg_mentioned_p (z_reg
, body
))
3922 insn
= NEXT_INSN (insn
);
3925 info
->found_call
= 1;
3926 info
->must_restore_reg
= 0;
3927 info
->last
= NEXT_INSN (insn
);
3929 info
->need_save_z
= 0;
3932 if (GET_CODE (insn
) == CODE_LABEL
3933 || GET_CODE (insn
) == BARRIER
|| GET_CODE (insn
) == ASM_INPUT
)
3936 if (GET_CODE (insn
) == JUMP_INSN
)
3938 if (reg_mentioned_p (z_reg
, insn
) == 0)
3941 info
->can_use_d
= 0;
3942 info
->must_save_reg
= 0;
3943 info
->must_restore_reg
= 0;
3944 info
->need_save_z
= 0;
3945 info
->last
= NEXT_INSN (insn
);
3948 if (GET_CODE (insn
) != INSN
&& GET_CODE (insn
) != JUMP_INSN
)
3953 /* Z register dies here. */
3954 z_dies_here
= find_regno_note (insn
, REG_DEAD
, HARD_Z_REGNUM
) != NULL
;
3956 body
= PATTERN (insn
);
3957 if (GET_CODE (body
) == SET
)
3959 rtx src
= XEXP (body
, 1);
3960 rtx dst
= XEXP (body
, 0);
3962 /* Condition code is set here. We have to restore the X/Y and
3963 save into Z before any test/compare insn because once we save/restore
3964 we can change the condition codes. When the compare insn uses Z and
3965 we can't use X/Y, the comparison is made with the *ZREG soft register
3966 (this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
3969 if ((GET_CODE (src
) == REG
&& REGNO (src
) == HARD_Z_REGNUM
)
3970 || (GET_CODE (src
) == COMPARE
&&
3971 (rtx_equal_p (XEXP (src
, 0), z_reg
)
3972 || rtx_equal_p (XEXP (src
, 1), z_reg
))))
3974 if (insn
== info
->first
)
3976 info
->must_load_z
= 0;
3977 info
->must_save_reg
= 0;
3978 info
->must_restore_reg
= 0;
3979 info
->need_save_z
= 0;
3980 info
->found_call
= 1;
3981 info
->regno
= SOFT_Z_REGNUM
;
3986 if (reg_mentioned_p (z_reg
, src
) == 0)
3988 info
->can_use_d
= 0;
3992 if (insn
!= info
->first
)
3995 /* Compare insn which uses Z. We have to save/restore the X/Y
3996 register without modifying the condition codes. For this
3997 we have to use a push/pop insn. */
3998 info
->must_push_reg
= 1;
4002 /* Z reg is set to something new. We don't need to load it. */
4005 if (!reg_mentioned_p (z_reg
, src
))
4007 /* Z reg is used before being set. Treat this as
4008 a new sequence of Z register replacement. */
4009 if (insn
!= info
->first
)
4013 info
->must_load_z
= 0;
4015 info
->z_set_count
++;
4016 info
->z_value
= src
;
4018 info
->z_loaded_with_sp
= 1;
4020 else if (reg_mentioned_p (z_reg
, dst
))
4021 info
->can_use_d
= 0;
4023 this_insn_uses_d
= reg_mentioned_p (d_reg
, src
)
4024 | reg_mentioned_p (d_reg
, dst
);
4025 this_insn_uses_ix
= reg_mentioned_p (ix_reg
, src
)
4026 | reg_mentioned_p (ix_reg
, dst
);
4027 this_insn_uses_iy
= reg_mentioned_p (iy_reg
, src
)
4028 | reg_mentioned_p (iy_reg
, dst
);
4029 this_insn_uses_z
= reg_mentioned_p (z_reg
, src
);
4031 /* If z is used as an address operand (like (MEM (reg z))),
4032 we can't replace it with d. */
4033 if (this_insn_uses_z
&& !Z_REG_P (src
)
4034 && !(m68hc11_arith_operator (src
, GET_MODE (src
))
4035 && Z_REG_P (XEXP (src
, 0))
4036 && !reg_mentioned_p (z_reg
, XEXP (src
, 1))
4037 && insn
== info
->first
4038 && dead_register_here (insn
, d_reg
)))
4039 info
->can_use_d
= 0;
4041 this_insn_uses_z_in_dst
= reg_mentioned_p (z_reg
, dst
);
4042 if (TARGET_M6812
&& !z_dies_here
4043 && ((this_insn_uses_z
&& side_effects_p (src
))
4044 || (this_insn_uses_z_in_dst
&& side_effects_p (dst
))))
4046 info
->need_save_z
= 1;
4047 info
->z_set_count
++;
4049 this_insn_uses_z
|= this_insn_uses_z_in_dst
;
4051 if (this_insn_uses_z
&& this_insn_uses_ix
&& this_insn_uses_iy
)
4053 fatal_insn ("registers IX, IY and Z used in the same INSN", insn
);
4056 if (this_insn_uses_d
)
4057 info
->can_use_d
= 0;
4059 /* IX and IY are used at the same time, we have to restore
4060 the value of the scratch register before this insn. */
4061 if (this_insn_uses_ix
&& this_insn_uses_iy
)
4066 if (this_insn_uses_ix
&& X_REG_P (dst
) && GET_MODE (dst
) == SImode
)
4067 info
->can_use_d
= 0;
4069 if (info
->x_used
== 0 && this_insn_uses_ix
)
4073 /* We have a (set (REG:HI X) (REG:HI Z)).
4074 Since we use Z as the replacement register, this insn
4075 is no longer necessary. We turn it into a note. We must
4076 not reload the old value of X. */
4077 if (X_REG_P (dst
) && rtx_equal_p (src
, z_reg
))
4081 info
->need_save_z
= 0;
4084 info
->must_save_reg
= 0;
4085 info
->must_restore_reg
= 0;
4086 info
->found_call
= 1;
4087 info
->can_use_d
= 0;
4088 PUT_CODE (insn
, NOTE
);
4089 NOTE_LINE_NUMBER (insn
) = NOTE_INSN_DELETED
;
4090 NOTE_SOURCE_FILE (insn
) = 0;
4091 info
->last
= NEXT_INSN (insn
);
4096 && (rtx_equal_p (src
, z_reg
)
4097 || (z_dies_here
&& !reg_mentioned_p (ix_reg
, src
))))
4101 info
->need_save_z
= 0;
4104 info
->last
= NEXT_INSN (insn
);
4105 info
->must_save_reg
= 0;
4106 info
->must_restore_reg
= 0;
4108 else if (X_REG_P (dst
) && reg_mentioned_p (z_reg
, src
)
4109 && !reg_mentioned_p (ix_reg
, src
))
4114 info
->need_save_z
= 0;
4118 info
->save_before_last
= 1;
4120 info
->must_restore_reg
= 0;
4121 info
->last
= NEXT_INSN (insn
);
4123 else if (info
->can_use_d
)
4125 info
->last
= NEXT_INSN (insn
);
4131 if (z_dies_here
&& !reg_mentioned_p (ix_reg
, src
)
4132 && GET_CODE (dst
) == REG
&& REGNO (dst
) == HARD_X_REGNUM
)
4134 info
->need_save_z
= 0;
4136 info
->last
= NEXT_INSN (insn
);
4137 info
->regno
= HARD_X_REGNUM
;
4138 info
->must_save_reg
= 0;
4139 info
->must_restore_reg
= 0;
4142 if (rtx_equal_p (src
, z_reg
) && rtx_equal_p (dst
, ix_reg
))
4144 info
->regno
= HARD_X_REGNUM
;
4145 info
->must_restore_reg
= 0;
4146 info
->must_save_reg
= 0;
4150 if (info
->y_used
== 0 && this_insn_uses_iy
)
4154 if (Y_REG_P (dst
) && rtx_equal_p (src
, z_reg
))
4158 info
->need_save_z
= 0;
4161 info
->must_save_reg
= 0;
4162 info
->must_restore_reg
= 0;
4163 info
->found_call
= 1;
4164 info
->can_use_d
= 0;
4165 PUT_CODE (insn
, NOTE
);
4166 NOTE_LINE_NUMBER (insn
) = NOTE_INSN_DELETED
;
4167 NOTE_SOURCE_FILE (insn
) = 0;
4168 info
->last
= NEXT_INSN (insn
);
4173 && (rtx_equal_p (src
, z_reg
)
4174 || (z_dies_here
&& !reg_mentioned_p (iy_reg
, src
))))
4179 info
->need_save_z
= 0;
4181 info
->last
= NEXT_INSN (insn
);
4182 info
->must_save_reg
= 0;
4183 info
->must_restore_reg
= 0;
4185 else if (Y_REG_P (dst
) && reg_mentioned_p (z_reg
, src
)
4186 && !reg_mentioned_p (iy_reg
, src
))
4191 info
->need_save_z
= 0;
4195 info
->save_before_last
= 1;
4197 info
->must_restore_reg
= 0;
4198 info
->last
= NEXT_INSN (insn
);
4200 else if (info
->can_use_d
)
4202 info
->last
= NEXT_INSN (insn
);
4209 if (z_dies_here
&& !reg_mentioned_p (iy_reg
, src
)
4210 && GET_CODE (dst
) == REG
&& REGNO (dst
) == HARD_Y_REGNUM
)
4212 info
->need_save_z
= 0;
4214 info
->last
= NEXT_INSN (insn
);
4215 info
->regno
= HARD_Y_REGNUM
;
4216 info
->must_save_reg
= 0;
4217 info
->must_restore_reg
= 0;
4220 if (rtx_equal_p (src
, z_reg
) && rtx_equal_p (dst
, iy_reg
))
4222 info
->regno
= HARD_Y_REGNUM
;
4223 info
->must_restore_reg
= 0;
4224 info
->must_save_reg
= 0;
4230 info
->need_save_z
= 0;
4232 if (info
->last
== 0)
4233 info
->last
= NEXT_INSN (insn
);
4236 return info
->last
!= NULL_RTX
? 0 : 1;
4238 if (GET_CODE (body
) == PARALLEL
)
4241 char ix_clobber
= 0;
4242 char iy_clobber
= 0;
4244 this_insn_uses_iy
= 0;
4245 this_insn_uses_ix
= 0;
4246 this_insn_uses_z
= 0;
4248 for (i
= XVECLEN (body
, 0) - 1; i
>= 0; i
--)
4251 int uses_ix
, uses_iy
, uses_z
;
4253 x
= XVECEXP (body
, 0, i
);
4255 if (info
->can_use_d
&& reg_mentioned_p (d_reg
, x
))
4256 info
->can_use_d
= 0;
4258 uses_ix
= reg_mentioned_p (ix_reg
, x
);
4259 uses_iy
= reg_mentioned_p (iy_reg
, x
);
4260 uses_z
= reg_mentioned_p (z_reg
, x
);
4261 if (GET_CODE (x
) == CLOBBER
)
4263 ix_clobber
|= uses_ix
;
4264 iy_clobber
|= uses_iy
;
4265 z_clobber
|= uses_z
;
4269 this_insn_uses_ix
|= uses_ix
;
4270 this_insn_uses_iy
|= uses_iy
;
4271 this_insn_uses_z
|= uses_z
;
4273 if (uses_z
&& GET_CODE (x
) == SET
)
4275 rtx dst
= XEXP (x
, 0);
4278 info
->z_set_count
++;
4280 if (TARGET_M6812
&& uses_z
&& side_effects_p (x
))
4281 info
->need_save_z
= 1;
4284 info
->need_save_z
= 0;
4288 printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
4289 this_insn_uses_ix
, this_insn_uses_iy
,
4290 this_insn_uses_z
, ix_clobber
, iy_clobber
, z_clobber
);
4293 if (this_insn_uses_z
)
4294 info
->can_use_d
= 0;
4296 if (z_clobber
&& info
->first
!= insn
)
4298 info
->need_save_z
= 0;
4302 if (z_clobber
&& info
->x_used
== 0 && info
->y_used
== 0)
4304 if (this_insn_uses_z
== 0 && insn
== info
->first
)
4306 info
->must_load_z
= 0;
4308 if (dead_register_here (insn
, d_reg
))
4310 info
->regno
= HARD_D_REGNUM
;
4311 info
->must_save_reg
= 0;
4312 info
->must_restore_reg
= 0;
4314 else if (dead_register_here (insn
, ix_reg
))
4316 info
->regno
= HARD_X_REGNUM
;
4317 info
->must_save_reg
= 0;
4318 info
->must_restore_reg
= 0;
4320 else if (dead_register_here (insn
, iy_reg
))
4322 info
->regno
= HARD_Y_REGNUM
;
4323 info
->must_save_reg
= 0;
4324 info
->must_restore_reg
= 0;
4326 if (info
->regno
>= 0)
4328 info
->last
= NEXT_INSN (insn
);
4331 if (this_insn_uses_ix
== 0)
4333 info
->regno
= HARD_X_REGNUM
;
4334 info
->must_save_reg
= 1;
4335 info
->must_restore_reg
= 1;
4337 else if (this_insn_uses_iy
== 0)
4339 info
->regno
= HARD_Y_REGNUM
;
4340 info
->must_save_reg
= 1;
4341 info
->must_restore_reg
= 1;
4345 info
->regno
= HARD_D_REGNUM
;
4346 info
->must_save_reg
= 1;
4347 info
->must_restore_reg
= 1;
4349 info
->last
= NEXT_INSN (insn
);
4353 if (((info
->x_used
|| this_insn_uses_ix
) && iy_clobber
)
4354 || ((info
->y_used
|| this_insn_uses_iy
) && ix_clobber
))
4356 if (this_insn_uses_z
)
4358 if (info
->y_used
== 0 && iy_clobber
)
4360 info
->regno
= HARD_Y_REGNUM
;
4361 info
->must_save_reg
= 0;
4362 info
->must_restore_reg
= 0;
4364 info
->last
= NEXT_INSN (insn
);
4365 info
->save_before_last
= 1;
4369 if (this_insn_uses_ix
&& this_insn_uses_iy
)
4371 if (this_insn_uses_z
)
4373 fatal_insn ("cannot do z-register replacement", insn
);
4377 if (info
->x_used
== 0 && (this_insn_uses_ix
|| ix_clobber
))
4384 if (iy_clobber
|| z_clobber
)
4386 info
->last
= NEXT_INSN (insn
);
4387 info
->save_before_last
= 1;
4392 if (info
->y_used
== 0 && (this_insn_uses_iy
|| iy_clobber
))
4399 if (ix_clobber
|| z_clobber
)
4401 info
->last
= NEXT_INSN (insn
);
4402 info
->save_before_last
= 1;
4409 info
->need_save_z
= 0;
4413 if (GET_CODE (body
) == CLOBBER
)
4416 /* IX and IY are used at the same time, we have to restore
4417 the value of the scratch register before this insn. */
4418 if (this_insn_uses_ix
&& this_insn_uses_iy
)
4422 if (info
->x_used
== 0 && this_insn_uses_ix
)
4430 if (info
->y_used
== 0 && this_insn_uses_iy
)
4444 m68hc11_find_z_replacement (insn
, info
)
4446 struct replace_info
*info
;
4450 info
->replace_reg
= NULL_RTX
;
4451 info
->must_load_z
= 1;
4452 info
->need_save_z
= 1;
4453 info
->must_save_reg
= 1;
4454 info
->must_restore_reg
= 1;
4458 info
->can_use_d
= TARGET_M6811
? 1 : 0;
4459 info
->found_call
= 0;
4463 info
->z_set_count
= 0;
4464 info
->z_value
= NULL_RTX
;
4465 info
->must_push_reg
= 0;
4466 info
->save_before_last
= 0;
4467 info
->z_loaded_with_sp
= 0;
4469 /* Scan the insn forward to find an address register that is not used.
4471 - the flow of the program changes,
4472 - when we detect that both X and Y are necessary,
4473 - when the Z register dies,
4474 - when the condition codes are set. */
4476 for (; insn
&& info
->z_died
== 0; insn
= NEXT_INSN (insn
))
4478 if (m68hc11_check_z_replacement (insn
, info
) == 0)
4482 /* May be we can use Y or X if they contain the same value as Z.
4483 This happens very often after the reload. */
4484 if (info
->z_set_count
== 1)
4486 rtx p
= info
->first
;
4491 v
= find_last_value (iy_reg
, &p
, insn
, 1);
4493 else if (info
->y_used
)
4495 v
= find_last_value (ix_reg
, &p
, insn
, 1);
4497 if (v
&& (v
!= iy_reg
&& v
!= ix_reg
) && rtx_equal_p (v
, info
->z_value
))
4500 info
->regno
= HARD_Y_REGNUM
;
4502 info
->regno
= HARD_X_REGNUM
;
4503 info
->must_load_z
= 0;
4504 info
->must_save_reg
= 0;
4505 info
->must_restore_reg
= 0;
4506 info
->found_call
= 1;
4509 if (info
->z_set_count
== 0)
4510 info
->need_save_z
= 0;
4513 info
->need_save_z
= 0;
4515 if (info
->last
== 0)
4518 if (info
->regno
>= 0)
4521 info
->replace_reg
= gen_rtx (REG
, HImode
, reg
);
4523 else if (info
->can_use_d
)
4525 reg
= HARD_D_REGNUM
;
4526 info
->replace_reg
= d_reg
;
4528 else if (info
->x_used
)
4530 reg
= HARD_Y_REGNUM
;
4531 info
->replace_reg
= iy_reg
;
4535 reg
= HARD_X_REGNUM
;
4536 info
->replace_reg
= ix_reg
;
4540 if (info
->must_save_reg
&& info
->must_restore_reg
)
4542 if (insn
&& dead_register_here (insn
, info
->replace_reg
))
4544 info
->must_save_reg
= 0;
4545 info
->must_restore_reg
= 0;
4550 /* The insn uses the Z register. Find a replacement register for it
4551 (either X or Y) and replace it in the insn and the next ones until
4552 the flow changes or the replacement register is used. Instructions
4553 are emited before and after the Z-block to preserve the value of
4554 Z and of the replacement register. */
4557 m68hc11_z_replacement (insn
)
4562 struct replace_info info
;
4564 /* Find trivial case where we only need to replace z with the
4565 equivalent soft register. */
4566 if (GET_CODE (insn
) == INSN
&& GET_CODE (PATTERN (insn
)) == SET
)
4568 rtx body
= PATTERN (insn
);
4569 rtx src
= XEXP (body
, 1);
4570 rtx dst
= XEXP (body
, 0);
4572 if (Z_REG_P (dst
) && (H_REG_P (src
) && !SP_REG_P (src
)))
4574 XEXP (body
, 0) = gen_rtx (REG
, GET_MODE (dst
), SOFT_Z_REGNUM
);
4577 else if (Z_REG_P (src
)
4578 && ((H_REG_P (dst
) && !SP_REG_P (src
)) || dst
== cc0_rtx
))
4580 XEXP (body
, 1) = gen_rtx (REG
, GET_MODE (src
), SOFT_Z_REGNUM
);
4583 else if (D_REG_P (dst
)
4584 && m68hc11_arith_operator (src
, GET_MODE (src
))
4585 && D_REG_P (XEXP (src
, 0)) && Z_REG_P (XEXP (src
, 1)))
4587 XEXP (src
, 1) = gen_rtx (REG
, GET_MODE (src
), SOFT_Z_REGNUM
);
4590 else if (Z_REG_P (dst
) && GET_CODE (src
) == CONST_INT
4591 && INTVAL (src
) == 0)
4593 XEXP (body
, 0) = gen_rtx (REG
, GET_MODE (dst
), SOFT_Z_REGNUM
);
4594 /* Force it to be re-recognized. */
4595 INSN_CODE (insn
) = -1;
4600 m68hc11_find_z_replacement (insn
, &info
);
4602 replace_reg
= info
.replace_reg
;
4603 replace_reg_qi
= NULL_RTX
;
4605 /* Save the X register in a .page0 location. */
4606 if (info
.must_save_reg
&& !info
.must_push_reg
)
4610 if (info
.must_push_reg
&& 0)
4611 dst
= gen_rtx (MEM
, HImode
,
4612 gen_rtx (PRE_DEC
, HImode
,
4613 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
4615 dst
= gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
);
4617 emit_insn_before (gen_movhi (dst
,
4618 gen_rtx (REG
, HImode
, info
.regno
)), insn
);
4620 if (info
.must_load_z
&& !info
.must_push_reg
)
4622 emit_insn_before (gen_movhi (gen_rtx (REG
, HImode
, info
.regno
),
4623 gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
)),
4628 /* Replace all occurrence of Z by replace_reg.
4629 Stop when the last instruction to replace is reached.
4630 Also stop when we detect a change in the flow (but it's not
4631 necessary; just safeguard). */
4633 for (; insn
&& insn
!= info
.last
; insn
= NEXT_INSN (insn
))
4637 if (GET_CODE (insn
) == CODE_LABEL
|| GET_CODE (insn
) == BARRIER
)
4640 if (GET_CODE (insn
) != INSN
4641 && GET_CODE (insn
) != CALL_INSN
&& GET_CODE (insn
) != JUMP_INSN
)
4644 body
= PATTERN (insn
);
4645 if (GET_CODE (body
) == SET
|| GET_CODE (body
) == PARALLEL
4646 || GET_CODE (insn
) == CALL_INSN
|| GET_CODE (insn
) == JUMP_INSN
)
4648 if (debug_m6811
&& reg_mentioned_p (replace_reg
, body
))
4650 printf ("Reg mentioned here...:\n");
4655 /* Stack pointer was decremented by 2 due to the push.
4656 Correct that by adding 2 to the destination. */
4657 if (info
.must_push_reg
4658 && info
.z_loaded_with_sp
&& GET_CODE (body
) == SET
)
4662 src
= SET_SRC (body
);
4663 dst
= SET_DEST (body
);
4664 if (SP_REG_P (src
) && Z_REG_P (dst
))
4666 emit_insn_after (gen_addhi3 (dst
,
4669 VOIDmode
, 2)), insn
);
4673 /* Replace any (REG:HI Z) occurrence by either X or Y. */
4674 if (!validate_replace_rtx (z_reg
, replace_reg
, insn
))
4676 INSN_CODE (insn
) = -1;
4677 if (!validate_replace_rtx (z_reg
, replace_reg
, insn
))
4678 fatal_insn ("cannot do z-register replacement", insn
);
4681 /* Likewise for (REG:QI Z). */
4682 if (reg_mentioned_p (z_reg
, insn
))
4684 if (replace_reg_qi
== NULL_RTX
)
4685 replace_reg_qi
= gen_rtx (REG
, QImode
, REGNO (replace_reg
));
4686 validate_replace_rtx (z_reg_qi
, replace_reg_qi
, insn
);
4689 if (GET_CODE (insn
) == CALL_INSN
|| GET_CODE (insn
) == JUMP_INSN
)
4693 /* Save Z before restoring the old value. */
4694 if (insn
&& info
.need_save_z
&& !info
.must_push_reg
)
4696 rtx save_pos_insn
= insn
;
4698 /* If Z is clobber by the last insn, we have to save its value
4699 before the last instruction. */
4700 if (info
.save_before_last
)
4701 save_pos_insn
= PREV_INSN (save_pos_insn
);
4703 emit_insn_before (gen_movhi (gen_rtx (REG
, HImode
, SOFT_Z_REGNUM
),
4704 gen_rtx (REG
, HImode
, info
.regno
)),
4708 if (info
.must_push_reg
&& info
.last
)
4712 body
= PATTERN (info
.last
);
4713 new_body
= gen_rtx (PARALLEL
, VOIDmode
,
4715 gen_rtx (USE
, VOIDmode
,
4717 gen_rtx (USE
, VOIDmode
,
4718 gen_rtx (REG
, HImode
,
4720 PATTERN (info
.last
) = new_body
;
4722 /* Force recognition on insn since we changed it. */
4723 INSN_CODE (insn
) = -1;
4725 if (!validate_replace_rtx (z_reg
, replace_reg
, info
.last
))
4727 fatal_insn ("invalid Z register replacement for insn", insn
);
4729 insn
= NEXT_INSN (info
.last
);
4732 /* Restore replacement register unless it was died. */
4733 if (insn
&& info
.must_restore_reg
&& !info
.must_push_reg
)
4737 if (info
.must_push_reg
&& 0)
4738 dst
= gen_rtx (MEM
, HImode
,
4739 gen_rtx (POST_INC
, HImode
,
4740 gen_rtx (REG
, HImode
, HARD_SP_REGNUM
)));
4742 dst
= gen_rtx (REG
, HImode
, SOFT_SAVED_XY_REGNUM
);
4744 emit_insn_before (gen_movhi (gen_rtx (REG
, HImode
, info
.regno
),
4751 /* Scan all the insn and re-affects some registers
4752 - The Z register (if it was used), is affected to X or Y depending
4753 on the instruction. */
4756 m68hc11_reassign_regs (first
)
4761 ix_reg
= gen_rtx (REG
, HImode
, HARD_X_REGNUM
);
4762 iy_reg
= gen_rtx (REG
, HImode
, HARD_Y_REGNUM
);
4763 z_reg
= gen_rtx (REG
, HImode
, HARD_Z_REGNUM
);
4764 z_reg_qi
= gen_rtx (REG
, QImode
, HARD_Z_REGNUM
);
4766 /* Scan all insns to replace Z by X or Y preserving the old value
4767 of X/Y and restoring it afterward. */
4769 for (insn
= first
; insn
; insn
= NEXT_INSN (insn
))
4773 if (GET_CODE (insn
) == CODE_LABEL
4774 || GET_CODE (insn
) == NOTE
|| GET_CODE (insn
) == BARRIER
)
4777 if (GET_RTX_CLASS (GET_CODE (insn
)) != 'i')
4780 body
= PATTERN (insn
);
4781 if (GET_CODE (body
) == CLOBBER
|| GET_CODE (body
) == USE
)
4784 if (GET_CODE (body
) == CONST_INT
|| GET_CODE (body
) == ASM_INPUT
4785 || GET_CODE (body
) == ASM_OPERANDS
4786 || GET_CODE (body
) == UNSPEC
|| GET_CODE (body
) == UNSPEC_VOLATILE
)
4789 if (GET_CODE (body
) == SET
|| GET_CODE (body
) == PARALLEL
4790 || GET_CODE (insn
) == CALL_INSN
|| GET_CODE (insn
) == JUMP_INSN
)
4793 /* If Z appears in this insn, replace it in the current insn
4794 and the next ones until the flow changes or we have to
4795 restore back the replacement register. */
4797 if (reg_mentioned_p (z_reg
, body
))
4799 m68hc11_z_replacement (insn
);
4804 printf ("insn not handled by Z replacement:\n");
4813 m68hc11_reorg (first
)
4819 z_replacement_completed
= 0;
4820 z_reg
= gen_rtx (REG
, HImode
, HARD_Z_REGNUM
);
4822 /* Some RTX are shared at this point. This breaks the Z register
4823 replacement, unshare everything. */
4824 unshare_all_rtl_again (first
);
4826 /* Force a split of all splitable insn. This is necessary for the
4827 Z register replacement mechanism because we end up with basic insns. */
4828 split_all_insns_noflow ();
4831 z_replacement_completed
= 1;
4832 m68hc11_reassign_regs (first
);
4834 /* After some splitting, there are some oportunities for CSE pass.
4835 This happens quite often when 32-bit or above patterns are split. */
4836 if (optimize
> 0 && split_done
)
4837 reload_cse_regs (first
);
4839 /* Re-create the REG_DEAD notes. These notes are used in the machine
4840 description to use the best assembly directives. */
4843 /* Before recomputing the REG_DEAD notes, remove all of them.
4844 This is necessary because the reload_cse_regs() pass can
4845 have replaced some (MEM) with a register. In that case,
4846 the REG_DEAD that could exist for that register may become
4848 for (insn
= first
; insn
; insn
= NEXT_INSN (insn
))
4854 pnote
= ®_NOTES (insn
);
4857 if (REG_NOTE_KIND (*pnote
) == REG_DEAD
)
4858 *pnote
= XEXP (*pnote
, 1);
4860 pnote
= &XEXP (*pnote
, 1);
4865 find_basic_blocks (first
, max_reg_num (), 0);
4866 life_analysis (first
, 0, PROP_REG_INFO
| PROP_DEATH_NOTES
);
4869 z_replacement_completed
= 2;
4871 /* If optimizing, then go ahead and split insns that must be
4872 split after Z register replacement. This gives more opportunities
4873 for peephole (in particular for consecutives xgdx/xgdy). */
4875 split_all_insns_noflow ();
4877 /* Once insns are split after the z_replacement_completed == 2,
4878 we must not re-run the life_analysis. The xgdx/xgdy patterns
4879 are not recognized and the life_analysis pass removes some
4880 insns because it thinks some (SETs) are noops or made to dead
4881 stores (which is false due to the swap).
4883 Do a simple pass to eliminate the noop set that the final
4884 split could generate (because it was easier for split definition). */
4888 for (insn
= first
; insn
; insn
= NEXT_INSN (insn
))
4892 if (INSN_DELETED_P (insn
))
4894 if (GET_RTX_CLASS (GET_CODE (insn
)) != 'i')
4897 /* Remove the (set (R) (R)) insns generated by some splits. */
4898 body
= PATTERN (insn
);
4899 if (GET_CODE (body
) == SET
4900 && rtx_equal_p (SET_SRC (body
), SET_DEST (body
)))
4902 PUT_CODE (insn
, NOTE
);
4903 NOTE_LINE_NUMBER (insn
) = NOTE_INSN_DELETED
;
4904 NOTE_SOURCE_FILE (insn
) = 0;
4912 /* Cost functions. */
4914 /* Cost of moving memory. */
4916 m68hc11_memory_move_cost (mode
, class, in
)
4917 enum machine_mode mode
;
4918 enum reg_class
class;
4919 int in ATTRIBUTE_UNUSED
;
4921 if (class <= H_REGS
)
4923 if (GET_MODE_SIZE (mode
) <= 2)
4924 return COSTS_N_INSNS (1) + (reload_completed
| reload_in_progress
);
4926 return COSTS_N_INSNS (2) + (reload_completed
| reload_in_progress
);
4930 if (GET_MODE_SIZE (mode
) <= 2)
4931 return COSTS_N_INSNS (2);
4933 return COSTS_N_INSNS (4);
4938 /* Cost of moving data from a register of class 'from' to on in class 'to'.
4939 Reload does not check the constraint of set insns when the two registers
4940 have a move cost of 2. Setting a higher cost will force reload to check
4943 m68hc11_register_move_cost (from
, to
)
4944 enum reg_class from
;
4947 if (from
>= S_REGS
&& to
>= S_REGS
)
4949 return COSTS_N_INSNS (3);
4951 if (from
<= S_REGS
&& to
<= S_REGS
)
4953 return COSTS_N_INSNS (1) + (reload_completed
| reload_in_progress
);
4955 return COSTS_N_INSNS (2);
4959 /* Provide the costs of an addressing mode that contains ADDR.
4960 If ADDR is not a valid address, its cost is irrelevant. */
4963 m68hc11_address_cost (addr
)
4968 switch (GET_CODE (addr
))
4971 /* Make the cost of hard registers and specially SP, FP small. */
4972 if (REGNO (addr
) < FIRST_PSEUDO_REGISTER
)
4989 register rtx plus0
= XEXP (addr
, 0);
4990 register rtx plus1
= XEXP (addr
, 1);
4992 if (GET_CODE (plus0
) != REG
)
4995 switch (GET_CODE (plus1
))
4998 if (INTVAL (plus1
) >= 2 * m68hc11_max_offset
4999 || INTVAL (plus1
) < m68hc11_min_offset
)
5001 else if (INTVAL (plus1
) >= m68hc11_max_offset
)
5005 if (REGNO (plus0
) < FIRST_PSEUDO_REGISTER
)
5027 if (SP_REG_P (XEXP (addr
, 0)))
5036 printf ("Address cost: %d for :", cost
);
5045 m68hc11_shift_cost (mode
, x
, shift
)
5046 enum machine_mode mode
;
5052 total
= rtx_cost (x
, SET
);
5054 total
+= m68hc11_cost
->shiftQI_const
[shift
% 8];
5055 else if (mode
== HImode
)
5056 total
+= m68hc11_cost
->shiftHI_const
[shift
% 16];
5057 else if (shift
== 8 || shift
== 16 || shift
== 32)
5058 total
+= m68hc11_cost
->shiftHI_const
[8];
5059 else if (shift
!= 0 && shift
!= 16 && shift
!= 32)
5061 total
+= m68hc11_cost
->shiftHI_const
[1] * shift
;
5064 /* For SI and others, the cost is higher. */
5065 if (GET_MODE_SIZE (mode
) > 2 && (shift
% 16) != 0)
5066 total
*= GET_MODE_SIZE (mode
) / 2;
5068 /* When optimizing for size, make shift more costly so that
5069 multiplications are preferred. */
5070 if (optimize_size
&& (shift
% 8) != 0)
5077 m68hc11_rtx_costs (x
, code
, outer_code
)
5080 enum rtx_code outer_code ATTRIBUTE_UNUSED
;
5082 enum machine_mode mode
= GET_MODE (x
);
5093 if (GET_CODE (XEXP (x
, 1)) == CONST_INT
)
5095 return m68hc11_shift_cost (mode
, XEXP (x
, 0), INTVAL (XEXP (x
, 1)));
5098 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5099 total
+= m68hc11_cost
->shift_var
;
5105 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5106 total
+= m68hc11_cost
->logical
;
5108 /* Logical instructions are byte instructions only. */
5109 total
*= GET_MODE_SIZE (mode
);
5114 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5115 total
+= m68hc11_cost
->add
;
5116 if (GET_MODE_SIZE (mode
) > 2)
5118 total
*= GET_MODE_SIZE (mode
) / 2;
5125 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5129 total
+= m68hc11_cost
->divQI
;
5133 total
+= m68hc11_cost
->divHI
;
5138 total
+= m68hc11_cost
->divSI
;
5144 /* mul instruction produces 16-bit result. */
5145 if (mode
== HImode
&& GET_CODE (XEXP (x
, 0)) == ZERO_EXTEND
5146 && GET_CODE (XEXP (x
, 1)) == ZERO_EXTEND
)
5147 return m68hc11_cost
->multQI
5148 + rtx_cost (XEXP (XEXP (x
, 0), 0), code
)
5149 + rtx_cost (XEXP (XEXP (x
, 1), 0), code
);
5151 /* emul instruction produces 32-bit result for 68HC12. */
5152 if (TARGET_M6812
&& mode
== SImode
5153 && GET_CODE (XEXP (x
, 0)) == ZERO_EXTEND
5154 && GET_CODE (XEXP (x
, 1)) == ZERO_EXTEND
)
5155 return m68hc11_cost
->multHI
5156 + rtx_cost (XEXP (XEXP (x
, 0), 0), code
)
5157 + rtx_cost (XEXP (XEXP (x
, 1), 0), code
);
5159 total
= rtx_cost (XEXP (x
, 0), code
) + rtx_cost (XEXP (x
, 1), code
);
5163 total
+= m68hc11_cost
->multQI
;
5167 total
+= m68hc11_cost
->multHI
;
5172 total
+= m68hc11_cost
->multSI
;
5179 extra_cost
= COSTS_N_INSNS (2);
5186 total
= extra_cost
+ rtx_cost (XEXP (x
, 0), code
);
5189 return total
+ COSTS_N_INSNS (1);
5193 return total
+ COSTS_N_INSNS (2);
5197 return total
+ COSTS_N_INSNS (4);
5199 return total
+ COSTS_N_INSNS (8);
5202 if (GET_CODE (XEXP (x
, 1)) == PC
|| GET_CODE (XEXP (x
, 2)) == PC
)
5203 return COSTS_N_INSNS (1);
5205 return COSTS_N_INSNS (1);
5208 return COSTS_N_INSNS (4);
5213 /* print_options - called at the start of the code generation for a
5216 extern char *asm_file_name
;
5219 #include <sys/types.h>
5228 extern int save_argc
;
5229 extern char **save_argv
;
5231 fprintf (out
, ";;; Command:\t");
5232 for (i
= 0; i
< save_argc
; i
++)
5234 fprintf (out
, "%s", save_argv
[i
]);
5235 if (i
+ 1 < save_argc
)
5238 fprintf (out
, "\n");
5240 a_time
= ctime (&c_time
);
5241 fprintf (out
, ";;; Compiled:\t%s", a_time
);
5244 #define __VERSION__ "[unknown]"
5246 fprintf (out
, ";;; (META)compiled by GNU C version %s.\n", __VERSION__
);
5248 fprintf (out
, ";;; (META)compiled by CC.\n");
5253 m68hc11_asm_file_start (out
, main_file
)
5255 const char *main_file
;
5257 fprintf (out
, ";;;-----------------------------------------\n");
5258 fprintf (out
, ";;; Start MC68HC11 gcc assembly output\n");
5259 fprintf (out
, ";;; gcc compiler %s\n", version_string
);
5260 print_options (out
);
5261 fprintf (out
, ";;;-----------------------------------------\n");
5262 output_file_directive (out
, main_file
);
5267 m68hc11_add_gc_roots ()
5269 ggc_add_rtx_root (&m68hc11_soft_tmp_reg
, 1);
5270 ggc_add_rtx_root (&ix_reg
, 1);
5271 ggc_add_rtx_root (&iy_reg
, 1);
5272 ggc_add_rtx_root (&d_reg
, 1);
5273 ggc_add_rtx_root (&da_reg
, 1);
5274 ggc_add_rtx_root (&z_reg
, 1);
5275 ggc_add_rtx_root (&z_reg_qi
, 1);
5276 ggc_add_rtx_root (&stack_push_word
, 1);
5277 ggc_add_rtx_root (&stack_pop_word
, 1);
5281 m68hc11_asm_out_constructor (symbol
, priority
)
5285 default_ctor_section_asm_out_constructor (symbol
, priority
);
5286 fprintf (asm_out_file
, "\t.globl\t__do_global_ctors\n");
5290 m68hc11_asm_out_destructor (symbol
, priority
)
5294 default_dtor_section_asm_out_destructor (symbol
, priority
);
5295 fprintf (asm_out_file
, "\t.globl\t__do_global_dtors\n");