m68hc11.c: Don't use the `0' flag for asm_fprintf specifiers.
[gcc.git] / gcc / config / m68hc11 / m68hc11.c
1 /* Subroutines for code generation on Motorola 68HC11 and 68HC12.
2 Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3 Contributed by Stephane Carrez (stcarrez@nerim.fr)
4
5 This file is part of GNU CC.
6
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)
10 any later version.
11
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.
16
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.
21
22 Note:
23 A first 68HC11 port was made by Otto Lind (otto@coactive.com)
24 on gcc 2.6.3. I have used it as a starting point for this port.
25 However, this new port is a complete re-write. Its internal
26 design is completely different. The generated code is not
27 compatible with the gcc 2.6.3 port.
28
29 The gcc 2.6.3 port is available at:
30
31 ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
32
33 */
34
35 #include <stdio.h>
36 #include "config.h"
37 #include "system.h"
38 #include "coretypes.h"
39 #include "tm.h"
40 #include "rtl.h"
41 #include "tree.h"
42 #include "tm_p.h"
43 #include "regs.h"
44 #include "hard-reg-set.h"
45 #include "real.h"
46 #include "insn-config.h"
47 #include "conditions.h"
48 #include "output.h"
49 #include "insn-attr.h"
50 #include "flags.h"
51 #include "recog.h"
52 #include "expr.h"
53 #include "toplev.h"
54 #include "basic-block.h"
55 #include "function.h"
56 #include "ggc.h"
57 #include "reload.h"
58 #include "target.h"
59 #include "target-def.h"
60
61 static void print_options PARAMS ((FILE *));
62 static void emit_move_after_reload PARAMS ((rtx, rtx, rtx));
63 static rtx simplify_logical PARAMS ((enum machine_mode, int, rtx, rtx *));
64 static void m68hc11_emit_logical PARAMS ((enum machine_mode, int, rtx *));
65 static void m68hc11_reorg PARAMS ((void));
66 static int go_if_legitimate_address_internal PARAMS((rtx, enum machine_mode,
67 int));
68 static int register_indirect_p PARAMS((rtx, enum machine_mode, int));
69 static rtx m68hc11_expand_compare PARAMS((enum rtx_code, rtx, rtx));
70 static int must_parenthesize PARAMS ((rtx));
71 static int m68hc11_address_cost PARAMS ((rtx));
72 static int m68hc11_shift_cost PARAMS ((enum machine_mode, rtx, int));
73 static int m68hc11_rtx_costs_1 PARAMS ((rtx, enum rtx_code, enum rtx_code));
74 static bool m68hc11_rtx_costs PARAMS ((rtx, int, int, int *));
75 static int m68hc11_auto_inc_p PARAMS ((rtx));
76 static tree m68hc11_handle_fntype_attribute PARAMS ((tree *, tree, tree, int, bool *));
77 const struct attribute_spec m68hc11_attribute_table[];
78
79 void create_regs_rtx PARAMS ((void));
80
81 static void asm_print_register PARAMS ((FILE *, int));
82 static void m68hc11_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
83 static void m68hc11_asm_out_constructor PARAMS ((rtx, int));
84 static void m68hc11_asm_out_destructor PARAMS ((rtx, int));
85 static void m68hc11_encode_section_info PARAMS((tree, rtx, int));
86 static int autoinc_mode PARAMS((rtx));
87 static int m68hc11_make_autoinc_notes PARAMS((rtx *, void *));
88
89 /* Must be set to 1 to produce debug messages. */
90 int debug_m6811 = 0;
91
92 extern FILE *asm_out_file;
93
94 rtx ix_reg;
95 rtx iy_reg;
96 rtx d_reg;
97 rtx m68hc11_soft_tmp_reg;
98 static GTY(()) rtx stack_push_word;
99 static GTY(()) rtx stack_pop_word;
100 static GTY(()) rtx z_reg;
101 static GTY(()) rtx z_reg_qi;
102 static int regs_inited = 0;
103
104 /* Set to 1 by expand_prologue() when the function is an interrupt handler. */
105 int current_function_interrupt;
106
107 /* Set to 1 by expand_prologue() when the function is a trap handler. */
108 int current_function_trap;
109
110 /* Set to 1 when the current function is placed in 68HC12 banked
111 memory and must return with rtc. */
112 int current_function_far;
113
114 /* Min offset that is valid for the indirect addressing mode. */
115 HOST_WIDE_INT m68hc11_min_offset = 0;
116
117 /* Max offset that is valid for the indirect addressing mode. */
118 HOST_WIDE_INT m68hc11_max_offset = 256;
119
120 /* The class value for base registers. */
121 enum reg_class m68hc11_base_reg_class = A_REGS;
122
123 /* The class value for index registers. This is NO_REGS for 68HC11. */
124 enum reg_class m68hc11_index_reg_class = NO_REGS;
125
126 enum reg_class m68hc11_tmp_regs_class = NO_REGS;
127
128 /* Tables that tell whether a given hard register is valid for
129 a base or an index register. It is filled at init time depending
130 on the target processor. */
131 unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
132 unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
133
134 /* A correction offset which is applied to the stack pointer.
135 This is 1 for 68HC11 and 0 for 68HC12. */
136 int m68hc11_sp_correction;
137
138 /* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns. */
139 rtx m68hc11_compare_op0;
140 rtx m68hc11_compare_op1;
141 \f
142
143 const struct processor_costs *m68hc11_cost;
144
145 /* Costs for a 68HC11. */
146 static const struct processor_costs m6811_cost = {
147 /* add */
148 COSTS_N_INSNS (2),
149 /* logical */
150 COSTS_N_INSNS (2),
151 /* non-constant shift */
152 COSTS_N_INSNS (20),
153 /* shiftQI const */
154 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
155 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
156 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
157
158 /* shiftHI const */
159 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
160 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
161 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
162 COSTS_N_INSNS (2), COSTS_N_INSNS (4),
163 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
164 COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
165 },
166 /* mulQI */
167 COSTS_N_INSNS (20),
168 /* mulHI */
169 COSTS_N_INSNS (20 * 4),
170 /* mulSI */
171 COSTS_N_INSNS (20 * 16),
172 /* divQI */
173 COSTS_N_INSNS (20),
174 /* divHI */
175 COSTS_N_INSNS (80),
176 /* divSI */
177 COSTS_N_INSNS (100)
178 };
179
180 /* Costs for a 68HC12. */
181 static const struct processor_costs m6812_cost = {
182 /* add */
183 COSTS_N_INSNS (2),
184 /* logical */
185 COSTS_N_INSNS (2),
186 /* non-constant shift */
187 COSTS_N_INSNS (20),
188 /* shiftQI const */
189 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
190 COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
191 COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
192
193 /* shiftHI const */
194 { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
195 COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
196 COSTS_N_INSNS (4), COSTS_N_INSNS (2),
197 COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
198 COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
199 COSTS_N_INSNS (6), COSTS_N_INSNS (4)
200 },
201 /* mulQI */
202 COSTS_N_INSNS (3),
203 /* mulHI */
204 COSTS_N_INSNS (3),
205 /* mulSI */
206 COSTS_N_INSNS (3 * 4),
207 /* divQI */
208 COSTS_N_INSNS (12),
209 /* divHI */
210 COSTS_N_INSNS (12),
211 /* divSI */
212 COSTS_N_INSNS (100)
213 };
214
215 /* Machine specific options */
216
217 const char *m68hc11_regparm_string;
218 const char *m68hc11_reg_alloc_order;
219 const char *m68hc11_soft_reg_count;
220
221 static int nb_soft_regs;
222 \f
223 /* Initialize the GCC target structure. */
224 #undef TARGET_ATTRIBUTE_TABLE
225 #define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
226
227 #undef TARGET_ASM_ALIGNED_HI_OP
228 #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
229
230 #undef TARGET_ASM_FUNCTION_EPILOGUE
231 #define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
232
233 #undef TARGET_ENCODE_SECTION_INFO
234 #define TARGET_ENCODE_SECTION_INFO m68hc11_encode_section_info
235
236 #undef TARGET_RTX_COSTS
237 #define TARGET_RTX_COSTS m68hc11_rtx_costs
238 #undef TARGET_ADDRESS_COST
239 #define TARGET_ADDRESS_COST m68hc11_address_cost
240
241 #undef TARGET_MACHINE_DEPENDENT_REORG
242 #define TARGET_MACHINE_DEPENDENT_REORG m68hc11_reorg
243
244 struct gcc_target targetm = TARGET_INITIALIZER;
245 \f
246 int
247 m68hc11_override_options ()
248 {
249 memset (m68hc11_reg_valid_for_index, 0,
250 sizeof (m68hc11_reg_valid_for_index));
251 memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
252
253 /* Compilation with -fpic generates a wrong code. */
254 if (flag_pic)
255 {
256 warning ("-f%s ignored for 68HC11/68HC12 (not supported)",
257 (flag_pic > 1) ? "PIC" : "pic");
258 flag_pic = 0;
259 }
260
261 /* Configure for a 68hc11 processor. */
262 if (TARGET_M6811)
263 {
264 /* If gcc was built for a 68hc12, invalidate that because
265 a -m68hc11 option was specified on the command line. */
266 if (TARGET_DEFAULT != MASK_M6811)
267 target_flags &= ~TARGET_DEFAULT;
268
269 if (!TARGET_M6812)
270 target_flags &= ~(TARGET_AUTO_INC_DEC | TARGET_MIN_MAX);
271 m68hc11_cost = &m6811_cost;
272 m68hc11_min_offset = 0;
273 m68hc11_max_offset = 256;
274 m68hc11_index_reg_class = NO_REGS;
275 m68hc11_base_reg_class = A_REGS;
276 m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
277 m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
278 m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
279 m68hc11_sp_correction = 1;
280 m68hc11_tmp_regs_class = D_REGS;
281 if (m68hc11_soft_reg_count == 0 && !TARGET_M6812)
282 m68hc11_soft_reg_count = "4";
283 }
284
285 /* Configure for a 68hc12 processor. */
286 if (TARGET_M6812)
287 {
288 m68hc11_cost = &m6812_cost;
289 m68hc11_min_offset = -65536;
290 m68hc11_max_offset = 65536;
291 m68hc11_index_reg_class = D_REGS;
292 m68hc11_base_reg_class = A_OR_SP_REGS;
293 m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
294 m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
295 m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
296 m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
297 m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
298 m68hc11_sp_correction = 0;
299 m68hc11_tmp_regs_class = TMP_REGS;
300 target_flags &= ~MASK_M6811;
301 target_flags |= MASK_NO_DIRECT_MODE;
302 if (m68hc11_soft_reg_count == 0)
303 m68hc11_soft_reg_count = "0";
304
305 if (TARGET_LONG_CALLS)
306 current_function_far = 1;
307 }
308 return 0;
309 }
310
311
312 void
313 m68hc11_conditional_register_usage ()
314 {
315 int i;
316 int cnt = atoi (m68hc11_soft_reg_count);
317
318 if (cnt < 0)
319 cnt = 0;
320 if (cnt > SOFT_REG_LAST - SOFT_REG_FIRST)
321 cnt = SOFT_REG_LAST - SOFT_REG_FIRST;
322
323 nb_soft_regs = cnt;
324 for (i = SOFT_REG_FIRST + cnt; i < SOFT_REG_LAST; i++)
325 {
326 fixed_regs[i] = 1;
327 call_used_regs[i] = 1;
328 }
329
330 /* For 68HC12, the Z register emulation is not necessary when the
331 frame pointer is not used. The frame pointer is eliminated and
332 replaced by the stack register (which is a BASE_REG_CLASS). */
333 if (TARGET_M6812 && flag_omit_frame_pointer && optimize)
334 {
335 fixed_regs[HARD_Z_REGNUM] = 1;
336 }
337 }
338 \f
339
340 /* Reload and register operations. */
341
342 static const char *const reg_class_names[] = REG_CLASS_NAMES;
343
344
345 void
346 create_regs_rtx ()
347 {
348 /* regs_inited = 1; */
349 ix_reg = gen_rtx (REG, HImode, HARD_X_REGNUM);
350 iy_reg = gen_rtx (REG, HImode, HARD_Y_REGNUM);
351 d_reg = gen_rtx (REG, HImode, HARD_D_REGNUM);
352 m68hc11_soft_tmp_reg = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
353
354 stack_push_word = gen_rtx (MEM, HImode,
355 gen_rtx (PRE_DEC, HImode,
356 gen_rtx (REG, HImode, HARD_SP_REGNUM)));
357 stack_pop_word = gen_rtx (MEM, HImode,
358 gen_rtx (POST_INC, HImode,
359 gen_rtx (REG, HImode, HARD_SP_REGNUM)));
360
361 }
362
363 /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
364 - 8 bit values are stored anywhere (except the SP register).
365 - 16 bit values can be stored in any register whose mode is 16
366 - 32 bit values can be stored in D, X registers or in a soft register
367 (except the last one because we need 2 soft registers)
368 - Values whose size is > 32 bit are not stored in real hard
369 registers. They may be stored in soft registers if there are
370 enough of them. */
371 int
372 hard_regno_mode_ok (regno, mode)
373 int regno;
374 enum machine_mode mode;
375 {
376 switch (GET_MODE_SIZE (mode))
377 {
378 case 8:
379 return S_REGNO_P (regno) && nb_soft_regs >= 4;
380
381 case 4:
382 return X_REGNO_P (regno) || (S_REGNO_P (regno) && nb_soft_regs >= 2);
383
384 case 2:
385 return G_REGNO_P (regno);
386
387 case 1:
388 /* We have to accept a QImode in X or Y registers. Otherwise, the
389 reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
390 in the insns. Reload fails if the insn rejects the register class 'a'
391 as well as if it accepts it. Patterns that failed were
392 zero_extend_qihi2 and iorqi3. */
393
394 return G_REGNO_P (regno) && !SP_REGNO_P (regno);
395
396 default:
397 return 0;
398 }
399 }
400
401 int
402 m68hc11_hard_regno_rename_ok (reg1, reg2)
403 int reg1, reg2;
404 {
405 /* Don't accept renaming to Z register. We will replace it to
406 X,Y or D during machine reorg pass. */
407 if (reg2 == HARD_Z_REGNUM)
408 return 0;
409
410 /* Don't accept renaming D,X to Y register as the code will be bigger. */
411 if (TARGET_M6811 && reg2 == HARD_Y_REGNUM
412 && (D_REGNO_P (reg1) || X_REGNO_P (reg1)))
413 return 0;
414
415 return 1;
416 }
417
418 enum reg_class
419 preferred_reload_class (operand, class)
420 rtx operand;
421 enum reg_class class;
422 {
423 enum machine_mode mode;
424
425 mode = GET_MODE (operand);
426
427 if (debug_m6811)
428 {
429 printf ("Preferred reload: (class=%s): ", reg_class_names[class]);
430 }
431
432 if (class == D_OR_A_OR_S_REGS && SP_REG_P (operand))
433 return m68hc11_base_reg_class;
434
435 if (class >= S_REGS && (GET_CODE (operand) == MEM
436 || GET_CODE (operand) == CONST_INT))
437 {
438 /* S_REGS class must not be used. The movhi template does not
439 work to move a memory to a soft register.
440 Restrict to a hard reg. */
441 switch (class)
442 {
443 default:
444 case G_REGS:
445 case D_OR_A_OR_S_REGS:
446 class = A_OR_D_REGS;
447 break;
448 case A_OR_S_REGS:
449 class = A_REGS;
450 break;
451 case D_OR_SP_OR_S_REGS:
452 class = D_OR_SP_REGS;
453 break;
454 case D_OR_Y_OR_S_REGS:
455 class = D_OR_Y_REGS;
456 break;
457 case D_OR_X_OR_S_REGS:
458 class = D_OR_X_REGS;
459 break;
460 case SP_OR_S_REGS:
461 class = SP_REGS;
462 break;
463 case Y_OR_S_REGS:
464 class = Y_REGS;
465 break;
466 case X_OR_S_REGS:
467 class = X_REGS;
468 break;
469 case D_OR_S_REGS:
470 class = D_REGS;
471 }
472 }
473 else if (class == Y_REGS && GET_CODE (operand) == MEM)
474 {
475 class = Y_REGS;
476 }
477 else if (class == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
478 {
479 class = D_OR_X_REGS;
480 }
481 else if (class >= S_REGS && S_REG_P (operand))
482 {
483 switch (class)
484 {
485 default:
486 case G_REGS:
487 case D_OR_A_OR_S_REGS:
488 class = A_OR_D_REGS;
489 break;
490 case A_OR_S_REGS:
491 class = A_REGS;
492 break;
493 case D_OR_SP_OR_S_REGS:
494 class = D_OR_SP_REGS;
495 break;
496 case D_OR_Y_OR_S_REGS:
497 class = D_OR_Y_REGS;
498 break;
499 case D_OR_X_OR_S_REGS:
500 class = D_OR_X_REGS;
501 break;
502 case SP_OR_S_REGS:
503 class = SP_REGS;
504 break;
505 case Y_OR_S_REGS:
506 class = Y_REGS;
507 break;
508 case X_OR_S_REGS:
509 class = X_REGS;
510 break;
511 case D_OR_S_REGS:
512 class = D_REGS;
513 }
514 }
515 else if (class >= S_REGS)
516 {
517 if (debug_m6811)
518 {
519 printf ("Class = %s for: ", reg_class_names[class]);
520 fflush (stdout);
521 debug_rtx (operand);
522 }
523 }
524
525 if (debug_m6811)
526 {
527 printf (" => class=%s\n", reg_class_names[class]);
528 fflush (stdout);
529 debug_rtx (operand);
530 }
531
532 return class;
533 }
534
535 /* Return 1 if the operand is a valid indexed addressing mode.
536 For 68hc11: n,r with n in [0..255] and r in A_REGS class
537 For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
538 static int
539 register_indirect_p (operand, mode, strict)
540 rtx operand;
541 enum machine_mode mode;
542 int strict;
543 {
544 rtx base, offset;
545
546 switch (GET_CODE (operand))
547 {
548 case POST_INC:
549 case PRE_INC:
550 case POST_DEC:
551 case PRE_DEC:
552 if (TARGET_M6812 && TARGET_AUTO_INC_DEC)
553 return register_indirect_p (XEXP (operand, 0), mode, strict);
554 return 0;
555
556 case PLUS:
557 base = XEXP (operand, 0);
558 if (GET_CODE (base) == MEM)
559 return 0;
560
561 offset = XEXP (operand, 1);
562 if (GET_CODE (offset) == MEM)
563 return 0;
564
565 if (GET_CODE (base) == REG)
566 {
567 if (!VALID_CONSTANT_OFFSET_P (offset, mode))
568 return 0;
569
570 if (strict == 0)
571 return 1;
572
573 return REGNO_OK_FOR_BASE_P2 (REGNO (base), strict);
574 }
575 if (GET_CODE (offset) == REG)
576 {
577 if (!VALID_CONSTANT_OFFSET_P (base, mode))
578 return 0;
579
580 if (strict == 0)
581 return 1;
582
583 return REGNO_OK_FOR_BASE_P2 (REGNO (offset), strict);
584 }
585 return 0;
586
587 case REG:
588 return REGNO_OK_FOR_BASE_P2 (REGNO (operand), strict);
589
590 case CONST_INT:
591 if (TARGET_M6811)
592 return 0;
593
594 return VALID_CONSTANT_OFFSET_P (operand, mode);
595
596 default:
597 return 0;
598 }
599 }
600
601 /* Returns 1 if the operand fits in a 68HC11 indirect mode or in
602 a 68HC12 1-byte index addressing mode. */
603 int
604 m68hc11_small_indexed_indirect_p (operand, mode)
605 rtx operand;
606 enum machine_mode mode;
607 {
608 rtx base, offset;
609
610 if (GET_CODE (operand) == REG && reload_in_progress
611 && REGNO (operand) >= FIRST_PSEUDO_REGISTER
612 && reg_equiv_memory_loc[REGNO (operand)])
613 {
614 operand = reg_equiv_memory_loc[REGNO (operand)];
615 operand = eliminate_regs (operand, 0, NULL_RTX);
616 }
617
618 if (GET_CODE (operand) != MEM)
619 return 0;
620
621 operand = XEXP (operand, 0);
622 if (CONSTANT_ADDRESS_P (operand))
623 return 1;
624
625 if (PUSH_POP_ADDRESS_P (operand))
626 return 1;
627
628 if (!register_indirect_p (operand, mode, reload_completed))
629 return 0;
630
631 if (TARGET_M6812 && GET_CODE (operand) == PLUS
632 && (reload_completed | reload_in_progress))
633 {
634 base = XEXP (operand, 0);
635 offset = XEXP (operand, 1);
636
637 /* The offset can be a symbol address and this is too big
638 for the operand constraint. */
639 if (GET_CODE (base) != CONST_INT && GET_CODE (offset) != CONST_INT)
640 return 0;
641
642 if (GET_CODE (base) == CONST_INT)
643 offset = base;
644
645 switch (GET_MODE_SIZE (mode))
646 {
647 case 8:
648 if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
649 return 0;
650 break;
651
652 case 4:
653 if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
654 return 0;
655 break;
656
657 default:
658 if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
659 return 0;
660 break;
661 }
662 }
663 return 1;
664 }
665
666 int
667 m68hc11_register_indirect_p (operand, mode)
668 rtx operand;
669 enum machine_mode mode;
670 {
671 if (GET_CODE (operand) != MEM)
672 return 0;
673
674 operand = XEXP (operand, 0);
675 return register_indirect_p (operand, mode,
676 (reload_completed | reload_in_progress));
677 }
678
679 static int
680 go_if_legitimate_address_internal (operand, mode, strict)
681 rtx operand;
682 enum machine_mode mode;
683 int strict;
684 {
685 if (CONSTANT_ADDRESS_P (operand) && TARGET_M6812)
686 {
687 /* Reject the global variables if they are too wide. This forces
688 a load of their address in a register and generates smaller code. */
689 if (GET_MODE_SIZE (mode) == 8)
690 return 0;
691
692 return 1;
693 }
694 if (register_indirect_p (operand, mode, strict))
695 {
696 return 1;
697 }
698 if (PUSH_POP_ADDRESS_P (operand))
699 {
700 return 1;
701 }
702 if (symbolic_memory_operand (operand, mode))
703 {
704 return 1;
705 }
706 return 0;
707 }
708
709 int
710 m68hc11_go_if_legitimate_address (operand, mode, strict)
711 rtx operand;
712 enum machine_mode mode;
713 int strict;
714 {
715 int result;
716
717 if (debug_m6811)
718 {
719 printf ("Checking: ");
720 fflush (stdout);
721 debug_rtx (operand);
722 }
723
724 result = go_if_legitimate_address_internal (operand, mode, strict);
725
726 if (debug_m6811)
727 {
728 printf (" -> %s\n", result == 0 ? "NO" : "YES");
729 }
730
731 if (result == 0)
732 {
733 if (debug_m6811)
734 {
735 printf ("go_if_legitimate%s, ret 0: %d:",
736 (strict ? "_strict" : ""), mode);
737 fflush (stdout);
738 debug_rtx (operand);
739 }
740 }
741 return result;
742 }
743
744 int
745 m68hc11_legitimize_address (operand, old_operand, mode)
746 rtx *operand ATTRIBUTE_UNUSED;
747 rtx old_operand ATTRIBUTE_UNUSED;
748 enum machine_mode mode ATTRIBUTE_UNUSED;
749 {
750 return 0;
751 }
752
753
754 int
755 m68hc11_reload_operands (operands)
756 rtx operands[];
757 {
758 enum machine_mode mode;
759
760 if (regs_inited == 0)
761 create_regs_rtx ();
762
763 mode = GET_MODE (operands[1]);
764
765 /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
766 if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
767 {
768 rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
769 rtx base = XEXP (XEXP (operands[1], 0), 0);
770
771 if (GET_CODE (base) != REG)
772 {
773 rtx tmp = base;
774 base = big_offset;
775 big_offset = tmp;
776 }
777
778 /* If the offset is out of range, we have to compute the address
779 with a separate add instruction. We try to do with with an 8-bit
780 add on the A register. This is possible only if the lowest part
781 of the offset (ie, big_offset % 256) is a valid constant offset
782 with respect to the mode. If it's not, we have to generate a
783 16-bit add on the D register. From:
784
785 (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
786
787 we generate:
788
789 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
790 (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
791 [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
792 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
793
794 (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
795 (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
796
797 */
798 if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
799 {
800 int vh, vl;
801 rtx reg = operands[0];
802 rtx offset;
803 int val = INTVAL (big_offset);
804
805
806 /* We use the 'operands[0]' as a scratch register to compute the
807 address. Make sure 'base' is in that register. */
808 if (!rtx_equal_p (base, operands[0]))
809 {
810 emit_move_insn (reg, base);
811 }
812
813 if (val > 0)
814 {
815 vh = val >> 8;
816 vl = val & 0x0FF;
817 }
818 else
819 {
820 vh = (val >> 8) & 0x0FF;
821 vl = val & 0x0FF;
822 }
823
824 /* Create the lowest part offset that still remains to be added.
825 If it's not a valid offset, do a 16-bit add. */
826 offset = GEN_INT (vl);
827 if (!VALID_CONSTANT_OFFSET_P (offset, mode))
828 {
829 emit_insn (gen_rtx (SET, VOIDmode, reg,
830 gen_rtx (PLUS, HImode, reg, big_offset)));
831 offset = const0_rtx;
832 }
833 else
834 {
835 emit_insn (gen_rtx (SET, VOIDmode, reg,
836 gen_rtx (PLUS, HImode, reg,
837 GEN_INT (vh << 8))));
838 }
839 emit_move_insn (operands[0],
840 gen_rtx (MEM, GET_MODE (operands[1]),
841 gen_rtx (PLUS, Pmode, reg, offset)));
842 return 1;
843 }
844 }
845
846 /* Use the normal gen_movhi pattern. */
847 return 0;
848 }
849
850 void
851 m68hc11_emit_libcall (name, code, dmode, smode, noperands, operands)
852 const char *name;
853 enum rtx_code code;
854 enum machine_mode dmode;
855 enum machine_mode smode;
856 int noperands;
857 rtx *operands;
858 {
859 rtx ret;
860 rtx insns;
861 rtx libcall;
862 rtx equiv;
863
864 start_sequence ();
865 libcall = gen_rtx_SYMBOL_REF (Pmode, name);
866 switch (noperands)
867 {
868 case 2:
869 ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
870 dmode, 1, operands[1], smode);
871 equiv = gen_rtx (code, dmode, operands[1]);
872 break;
873
874 case 3:
875 ret = emit_library_call_value (libcall, NULL_RTX,
876 LCT_CONST, dmode, 2,
877 operands[1], smode, operands[2],
878 smode);
879 equiv = gen_rtx (code, dmode, operands[1], operands[2]);
880 break;
881
882 default:
883 abort ();
884 }
885
886 insns = get_insns ();
887 end_sequence ();
888 emit_libcall_block (insns, operands[0], ret, equiv);
889 }
890
891 /* Returns true if X is a PRE/POST increment decrement
892 (same as auto_inc_p() in rtlanal.c but do not take into
893 account the stack). */
894 static int
895 m68hc11_auto_inc_p (x)
896 rtx x;
897 {
898 return GET_CODE (x) == PRE_DEC
899 || GET_CODE (x) == POST_INC
900 || GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
901 }
902 \f
903
904 /* Predicates for machine description. */
905
906 int
907 memory_reload_operand (operand, mode)
908 rtx operand;
909 enum machine_mode mode ATTRIBUTE_UNUSED;
910 {
911 return GET_CODE (operand) == MEM
912 && GET_CODE (XEXP (operand, 0)) == PLUS
913 && ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
914 && GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
915 || (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
916 && GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
917 }
918
919 int
920 tst_operand (operand, mode)
921 rtx operand;
922 enum machine_mode mode;
923 {
924 if (GET_CODE (operand) == MEM && reload_completed == 0)
925 {
926 rtx addr = XEXP (operand, 0);
927 if (m68hc11_auto_inc_p (addr))
928 return 0;
929 }
930 return nonimmediate_operand (operand, mode);
931 }
932
933 int
934 cmp_operand (operand, mode)
935 rtx operand;
936 enum machine_mode mode;
937 {
938 if (GET_CODE (operand) == MEM)
939 {
940 rtx addr = XEXP (operand, 0);
941 if (m68hc11_auto_inc_p (addr))
942 return 0;
943 }
944 return general_operand (operand, mode);
945 }
946
947 int
948 non_push_operand (operand, mode)
949 rtx operand;
950 enum machine_mode mode;
951 {
952 if (general_operand (operand, mode) == 0)
953 return 0;
954
955 if (push_operand (operand, mode) == 1)
956 return 0;
957 return 1;
958 }
959
960 int
961 reg_or_some_mem_operand (operand, mode)
962 rtx operand;
963 enum machine_mode mode;
964 {
965 if (GET_CODE (operand) == MEM)
966 {
967 rtx op = XEXP (operand, 0);
968
969 if (symbolic_memory_operand (op, mode))
970 return 1;
971
972 if (IS_STACK_PUSH (operand))
973 return 1;
974
975 if (m68hc11_register_indirect_p (operand, mode))
976 return 1;
977
978 return 0;
979 }
980
981 return register_operand (operand, mode);
982 }
983
984 int
985 m68hc11_symbolic_p (operand, mode)
986 rtx operand;
987 enum machine_mode mode;
988 {
989 if (GET_CODE (operand) == MEM)
990 {
991 rtx op = XEXP (operand, 0);
992
993 if (symbolic_memory_operand (op, mode))
994 return 1;
995 }
996 return 0;
997 }
998
999 int
1000 m68hc11_indirect_p (operand, mode)
1001 rtx operand;
1002 enum machine_mode mode;
1003 {
1004 if (GET_CODE (operand) == MEM)
1005 {
1006 rtx op = XEXP (operand, 0);
1007
1008 if (symbolic_memory_operand (op, mode))
1009 return 0;
1010
1011 if (reload_in_progress)
1012 return 1;
1013
1014 operand = XEXP (operand, 0);
1015 return register_indirect_p (operand, mode, reload_completed);
1016 }
1017 return 0;
1018 }
1019
1020 int
1021 stack_register_operand (operand, mode)
1022 rtx operand;
1023 enum machine_mode mode ATTRIBUTE_UNUSED;
1024 {
1025 return SP_REG_P (operand);
1026 }
1027
1028 int
1029 d_register_operand (operand, mode)
1030 rtx operand;
1031 enum machine_mode mode ATTRIBUTE_UNUSED;
1032 {
1033 if (GET_MODE (operand) != mode && mode != VOIDmode)
1034 return 0;
1035
1036 if (GET_CODE (operand) == SUBREG)
1037 operand = XEXP (operand, 0);
1038
1039 return GET_CODE (operand) == REG
1040 && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
1041 || REGNO (operand) == HARD_D_REGNUM
1042 || (mode == QImode && REGNO (operand) == HARD_B_REGNUM));
1043 }
1044
1045 int
1046 hard_addr_reg_operand (operand, mode)
1047 rtx operand;
1048 enum machine_mode mode ATTRIBUTE_UNUSED;
1049 {
1050 if (GET_MODE (operand) != mode && mode != VOIDmode)
1051 return 0;
1052
1053 if (GET_CODE (operand) == SUBREG)
1054 operand = XEXP (operand, 0);
1055
1056 return GET_CODE (operand) == REG
1057 && (REGNO (operand) == HARD_X_REGNUM
1058 || REGNO (operand) == HARD_Y_REGNUM
1059 || REGNO (operand) == HARD_Z_REGNUM);
1060 }
1061
1062 int
1063 hard_reg_operand (operand, mode)
1064 rtx operand;
1065 enum machine_mode mode;
1066 {
1067 if (GET_MODE (operand) != mode && mode != VOIDmode)
1068 return 0;
1069
1070 if (GET_CODE (operand) == SUBREG)
1071 operand = XEXP (operand, 0);
1072
1073 return GET_CODE (operand) == REG
1074 && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
1075 || H_REGNO_P (REGNO (operand)));
1076 }
1077
1078 int
1079 memory_indexed_operand (operand, mode)
1080 rtx operand;
1081 enum machine_mode mode ATTRIBUTE_UNUSED;
1082 {
1083 if (GET_CODE (operand) != MEM)
1084 return 0;
1085
1086 operand = XEXP (operand, 0);
1087 if (GET_CODE (operand) == PLUS)
1088 {
1089 if (GET_CODE (XEXP (operand, 0)) == REG)
1090 operand = XEXP (operand, 0);
1091 else if (GET_CODE (XEXP (operand, 1)) == REG)
1092 operand = XEXP (operand, 1);
1093 }
1094 return GET_CODE (operand) == REG
1095 && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
1096 || A_REGNO_P (REGNO (operand)));
1097 }
1098
1099 int
1100 push_pop_operand_p (operand)
1101 rtx operand;
1102 {
1103 if (GET_CODE (operand) != MEM)
1104 {
1105 return 0;
1106 }
1107 operand = XEXP (operand, 0);
1108 return PUSH_POP_ADDRESS_P (operand);
1109 }
1110
1111 /* Returns 1 if OP is either a symbol reference or a sum of a symbol
1112 reference and a constant. */
1113
1114 int
1115 symbolic_memory_operand (op, mode)
1116 register rtx op;
1117 enum machine_mode mode;
1118 {
1119 switch (GET_CODE (op))
1120 {
1121 case SYMBOL_REF:
1122 case LABEL_REF:
1123 return 1;
1124
1125 case CONST:
1126 op = XEXP (op, 0);
1127 return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
1128 || GET_CODE (XEXP (op, 0)) == LABEL_REF)
1129 && GET_CODE (XEXP (op, 1)) == CONST_INT);
1130
1131 /* ??? This clause seems to be irrelevant. */
1132 case CONST_DOUBLE:
1133 return GET_MODE (op) == mode;
1134
1135 case PLUS:
1136 return symbolic_memory_operand (XEXP (op, 0), mode)
1137 && symbolic_memory_operand (XEXP (op, 1), mode);
1138
1139 default:
1140 return 0;
1141 }
1142 }
1143
1144 int
1145 m68hc11_eq_compare_operator (op, mode)
1146 register rtx op;
1147 enum machine_mode mode ATTRIBUTE_UNUSED;
1148 {
1149 return GET_CODE (op) == EQ || GET_CODE (op) == NE;
1150 }
1151
1152 int
1153 m68hc11_logical_operator (op, mode)
1154 register rtx op;
1155 enum machine_mode mode ATTRIBUTE_UNUSED;
1156 {
1157 return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
1158 }
1159
1160 int
1161 m68hc11_arith_operator (op, mode)
1162 register rtx op;
1163 enum machine_mode mode ATTRIBUTE_UNUSED;
1164 {
1165 return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
1166 || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS
1167 || GET_CODE (op) == ASHIFT || GET_CODE (op) == ASHIFTRT
1168 || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATE
1169 || GET_CODE (op) == ROTATERT;
1170 }
1171
1172 int
1173 m68hc11_non_shift_operator (op, mode)
1174 register rtx op;
1175 enum machine_mode mode ATTRIBUTE_UNUSED;
1176 {
1177 return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
1178 || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS;
1179 }
1180
1181 /* Return true if op is a shift operator. */
1182 int
1183 m68hc11_shift_operator (op, mode)
1184 register rtx op;
1185 enum machine_mode mode ATTRIBUTE_UNUSED;
1186 {
1187 return GET_CODE (op) == ROTATE || GET_CODE (op) == ROTATERT
1188 || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ASHIFT
1189 || GET_CODE (op) == ASHIFTRT;
1190 }
1191
1192 int
1193 m68hc11_unary_operator (op, mode)
1194 register rtx op;
1195 enum machine_mode mode ATTRIBUTE_UNUSED;
1196 {
1197 return GET_CODE (op) == NEG || GET_CODE (op) == NOT
1198 || GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
1199 }
1200 \f
1201 /* Emit the code to build the trampoline used to call a nested function.
1202
1203 68HC11 68HC12
1204
1205 ldy #&CXT movw #&CXT,*_.d1
1206 sty *_.d1 jmp FNADDR
1207 jmp FNADDR
1208
1209 */
1210 void
1211 m68hc11_initialize_trampoline (tramp, fnaddr, cxt)
1212 rtx tramp;
1213 rtx fnaddr;
1214 rtx cxt;
1215 {
1216 const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
1217
1218 /* Skip the '*'. */
1219 if (*static_chain_reg == '*')
1220 static_chain_reg++;
1221 if (TARGET_M6811)
1222 {
1223 emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
1224 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
1225 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
1226 GEN_INT (0x18df));
1227 emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
1228 gen_rtx_CONST (QImode,
1229 gen_rtx_SYMBOL_REF (Pmode,
1230 static_chain_reg)));
1231 emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
1232 GEN_INT (0x7e));
1233 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
1234 }
1235 else
1236 {
1237 emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
1238 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
1239 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
1240 gen_rtx_CONST (HImode,
1241 gen_rtx_SYMBOL_REF (Pmode,
1242 static_chain_reg)));
1243 emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
1244 GEN_INT (0x06));
1245 emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
1246 }
1247 }
1248 \f
1249 /* Declaration of types. */
1250
1251 const struct attribute_spec m68hc11_attribute_table[] =
1252 {
1253 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
1254 { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
1255 { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
1256 { "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
1257 { "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
1258 { NULL, 0, 0, false, false, false, NULL }
1259 };
1260
1261 /* Keep track of the symbol which has a `trap' attribute and which uses
1262 the `swi' calling convention. Since there is only one trap, we only
1263 record one such symbol. If there are several, a warning is reported. */
1264 static rtx trap_handler_symbol = 0;
1265
1266 /* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
1267 arguments as in struct attribute_spec.handler. */
1268 static tree
1269 m68hc11_handle_fntype_attribute (node, name, args, flags, no_add_attrs)
1270 tree *node;
1271 tree name;
1272 tree args ATTRIBUTE_UNUSED;
1273 int flags ATTRIBUTE_UNUSED;
1274 bool *no_add_attrs;
1275 {
1276 if (TREE_CODE (*node) != FUNCTION_TYPE
1277 && TREE_CODE (*node) != METHOD_TYPE
1278 && TREE_CODE (*node) != FIELD_DECL
1279 && TREE_CODE (*node) != TYPE_DECL)
1280 {
1281 warning ("`%s' attribute only applies to functions",
1282 IDENTIFIER_POINTER (name));
1283 *no_add_attrs = true;
1284 }
1285
1286 return NULL_TREE;
1287 }
1288
1289 /* We want to recognize trap handlers so that we handle calls to traps
1290 in a special manner (by issuing the trap). This information is stored
1291 in SYMBOL_REF_FLAG. */
1292
1293 static void
1294 m68hc11_encode_section_info (decl, rtl, first)
1295 tree decl;
1296 rtx rtl;
1297 int first ATTRIBUTE_UNUSED;
1298 {
1299 tree func_attr;
1300 int trap_handler;
1301 int is_far = 0;
1302
1303 if (TREE_CODE (decl) != FUNCTION_DECL)
1304 return;
1305
1306 func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
1307
1308
1309 if (lookup_attribute ("far", func_attr) != NULL_TREE)
1310 is_far = 1;
1311 else if (lookup_attribute ("near", func_attr) == NULL_TREE)
1312 is_far = TARGET_LONG_CALLS != 0;
1313
1314 trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
1315 if (trap_handler && is_far)
1316 {
1317 warning ("`trap' and `far' attributes are not compatible, ignoring `far'");
1318 trap_handler = 0;
1319 }
1320 if (trap_handler)
1321 {
1322 if (trap_handler_symbol != 0)
1323 warning ("`trap' attribute is already used");
1324 else
1325 trap_handler_symbol = XEXP (rtl, 0);
1326 }
1327 SYMBOL_REF_FLAG (XEXP (rtl, 0)) = is_far;
1328 }
1329
1330 int
1331 m68hc11_is_far_symbol (sym)
1332 rtx sym;
1333 {
1334 if (GET_CODE (sym) == MEM)
1335 sym = XEXP (sym, 0);
1336
1337 return SYMBOL_REF_FLAG (sym);
1338 }
1339
1340 int
1341 m68hc11_is_trap_symbol (sym)
1342 rtx sym;
1343 {
1344 if (GET_CODE (sym) == MEM)
1345 sym = XEXP (sym, 0);
1346
1347 return trap_handler_symbol != 0 && rtx_equal_p (trap_handler_symbol, sym);
1348 }
1349 \f
1350
1351 /* Argument support functions. */
1352
1353 /* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
1354 Arrays are passed by references and other types by value.
1355
1356 SCz: I tried to pass DImode by reference but it seems that this
1357 does not work very well. */
1358 int
1359 m68hc11_function_arg_pass_by_reference (cum, mode, type, named)
1360 const CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
1361 enum machine_mode mode ATTRIBUTE_UNUSED;
1362 tree type;
1363 int named ATTRIBUTE_UNUSED;
1364 {
1365 return ((type && TREE_CODE (type) == ARRAY_TYPE)
1366 /* Consider complex values as aggregates, so care for TCmode. */
1367 /*|| GET_MODE_SIZE (mode) > 4 SCz, temporary */
1368 /*|| (type && AGGREGATE_TYPE_P (type))) */ );
1369 }
1370
1371
1372 /* Define the offset between two registers, one to be eliminated, and the
1373 other its replacement, at the start of a routine. */
1374 int
1375 m68hc11_initial_elimination_offset (from, to)
1376 int from;
1377 int to;
1378 {
1379 int trap_handler;
1380 tree func_attr;
1381 int size;
1382 int regno;
1383
1384 /* For a trap handler, we must take into account the registers which
1385 are pushed on the stack during the trap (except the PC). */
1386 func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1387
1388 if (lookup_attribute ("far", func_attr) != 0)
1389 current_function_far = 1;
1390 else if (lookup_attribute ("near", func_attr) != 0)
1391 current_function_far = 0;
1392 else
1393 current_function_far = TARGET_LONG_CALLS != 0;
1394
1395 trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
1396 if (trap_handler && from == ARG_POINTER_REGNUM)
1397 size = 7;
1398
1399 /* For a function using 'call/rtc' we must take into account the
1400 page register which is pushed in the call. */
1401 else if (current_function_far && from == ARG_POINTER_REGNUM)
1402 size = 1;
1403 else
1404 size = 0;
1405
1406 if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1407 {
1408 /* 2 is for the saved frame.
1409 1 is for the 'sts' correction when creating the frame. */
1410 return get_frame_size () + 2 + m68hc11_sp_correction + size;
1411 }
1412
1413 if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1414 {
1415 return m68hc11_sp_correction;
1416 }
1417
1418 /* Push any 2 byte pseudo hard registers that we need to save. */
1419 for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
1420 {
1421 if (regs_ever_live[regno] && !call_used_regs[regno])
1422 {
1423 size += 2;
1424 }
1425 }
1426
1427 if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
1428 {
1429 return get_frame_size () + size;
1430 }
1431
1432 if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
1433 {
1434 return size;
1435 }
1436 return 0;
1437 }
1438
1439 /* Initialize a variable CUM of type CUMULATIVE_ARGS
1440 for a call to a function whose data type is FNTYPE.
1441 For a library call, FNTYPE is 0. */
1442
1443 void
1444 m68hc11_init_cumulative_args (cum, fntype, libname)
1445 CUMULATIVE_ARGS *cum;
1446 tree fntype;
1447 rtx libname;
1448 {
1449 tree ret_type;
1450
1451 z_replacement_completed = 0;
1452 cum->words = 0;
1453 cum->nregs = 0;
1454
1455 /* For a library call, we must find out the type of the return value.
1456 When the return value is bigger than 4 bytes, it is returned in
1457 memory. In that case, the first argument of the library call is a
1458 pointer to the memory location. Because the first argument is passed in
1459 register D, we have to identify this, so that the first function
1460 parameter is not passed in D either. */
1461 if (fntype == 0)
1462 {
1463 const char *name;
1464 size_t len;
1465
1466 if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
1467 return;
1468
1469 /* If the library ends in 'di' or in 'df', we assume it's
1470 returning some DImode or some DFmode which are 64-bit wide. */
1471 name = XSTR (libname, 0);
1472 len = strlen (name);
1473 if (len > 3
1474 && ((name[len - 2] == 'd'
1475 && (name[len - 1] == 'f' || name[len - 1] == 'i'))
1476 || (name[len - 3] == 'd'
1477 && (name[len - 2] == 'i' || name[len - 2] == 'f'))))
1478 {
1479 /* We are in. Mark the first parameter register as already used. */
1480 cum->words = 1;
1481 cum->nregs = 1;
1482 }
1483 return;
1484 }
1485
1486 ret_type = TREE_TYPE (fntype);
1487
1488 if (ret_type && aggregate_value_p (ret_type))
1489 {
1490 cum->words = 1;
1491 cum->nregs = 1;
1492 }
1493 }
1494
1495 /* Update the data in CUM to advance over an argument
1496 of mode MODE and data type TYPE.
1497 (TYPE is null for libcalls where that information may not be available.) */
1498
1499 void
1500 m68hc11_function_arg_advance (cum, mode, type, named)
1501 CUMULATIVE_ARGS *cum;
1502 enum machine_mode mode;
1503 tree type;
1504 int named ATTRIBUTE_UNUSED;
1505 {
1506 if (mode != BLKmode)
1507 {
1508 if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
1509 {
1510 cum->nregs = 2;
1511 cum->words = GET_MODE_SIZE (mode);
1512 }
1513 else
1514 {
1515 cum->words += GET_MODE_SIZE (mode);
1516 if (cum->words <= HARD_REG_SIZE)
1517 cum->nregs = 1;
1518 }
1519 }
1520 else
1521 {
1522 cum->words += int_size_in_bytes (type);
1523 }
1524 return;
1525 }
1526
1527 /* Define where to put the arguments to a function.
1528 Value is zero to push the argument on the stack,
1529 or a hard register in which to store the argument.
1530
1531 MODE is the argument's machine mode.
1532 TYPE is the data type of the argument (as a tree).
1533 This is null for libcalls where that information may
1534 not be available.
1535 CUM is a variable of type CUMULATIVE_ARGS which gives info about
1536 the preceding args and about the function being called.
1537 NAMED is nonzero if this argument is a named parameter
1538 (otherwise it is an extra parameter matching an ellipsis). */
1539
1540 struct rtx_def *
1541 m68hc11_function_arg (cum, mode, type, named)
1542 const CUMULATIVE_ARGS *cum;
1543 enum machine_mode mode;
1544 tree type ATTRIBUTE_UNUSED;
1545 int named ATTRIBUTE_UNUSED;
1546 {
1547 if (cum->words != 0)
1548 {
1549 return NULL_RTX;
1550 }
1551
1552 if (mode != BLKmode)
1553 {
1554 if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
1555 return gen_rtx (REG, mode, HARD_X_REGNUM);
1556
1557 if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
1558 {
1559 return NULL_RTX;
1560 }
1561 return gen_rtx (REG, mode, HARD_D_REGNUM);
1562 }
1563 return NULL_RTX;
1564 }
1565
1566 /* If defined, a C expression which determines whether, and in which direction,
1567 to pad out an argument with extra space. The value should be of type
1568 `enum direction': either `upward' to pad above the argument,
1569 `downward' to pad below, or `none' to inhibit padding.
1570
1571 Structures are stored left shifted in their argument slot. */
1572 int
1573 m68hc11_function_arg_padding (mode, type)
1574 enum machine_mode mode;
1575 tree type;
1576 {
1577 if (type != 0 && AGGREGATE_TYPE_P (type))
1578 return upward;
1579
1580 /* This is the default definition. */
1581 return (!BYTES_BIG_ENDIAN
1582 ? upward
1583 : ((mode == BLKmode
1584 ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
1585 && int_size_in_bytes (type) <
1586 (PARM_BOUNDARY / BITS_PER_UNIT)) : GET_MODE_BITSIZE (mode) <
1587 PARM_BOUNDARY) ? downward : upward));
1588 }
1589 \f
1590
1591 /* Function prologue and epilogue. */
1592
1593 /* Emit a move after the reload pass has completed. This is used to
1594 emit the prologue and epilogue. */
1595 static void
1596 emit_move_after_reload (to, from, scratch)
1597 rtx to, from, scratch;
1598 {
1599 rtx insn;
1600
1601 if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
1602 {
1603 insn = emit_move_insn (to, from);
1604 }
1605 else
1606 {
1607 emit_move_insn (scratch, from);
1608 insn = emit_move_insn (to, scratch);
1609 }
1610
1611 /* Put a REG_INC note to tell the flow analysis that the instruction
1612 is necessary. */
1613 if (IS_STACK_PUSH (to))
1614 {
1615 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
1616 XEXP (XEXP (to, 0), 0),
1617 REG_NOTES (insn));
1618 }
1619 else if (IS_STACK_POP (from))
1620 {
1621 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
1622 XEXP (XEXP (from, 0), 0),
1623 REG_NOTES (insn));
1624 }
1625
1626 /* For 68HC11, put a REG_INC note on `sts _.frame' to prevent the cse-reg
1627 to think that sp == _.frame and later replace a x = sp with x = _.frame.
1628 The problem is that we are lying to gcc and use `txs' for x = sp
1629 (which is not really true because txs is really x = sp + 1). */
1630 else if (TARGET_M6811 && SP_REG_P (from))
1631 {
1632 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
1633 from,
1634 REG_NOTES (insn));
1635 }
1636 }
1637
1638 int
1639 m68hc11_total_frame_size ()
1640 {
1641 int size;
1642 int regno;
1643
1644 size = get_frame_size ();
1645 if (current_function_interrupt)
1646 {
1647 size += 3 * HARD_REG_SIZE;
1648 }
1649 if (frame_pointer_needed)
1650 size += HARD_REG_SIZE;
1651
1652 for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
1653 if (regs_ever_live[regno] && !call_used_regs[regno])
1654 size += HARD_REG_SIZE;
1655
1656 return size;
1657 }
1658
1659 static void
1660 m68hc11_output_function_epilogue (out, size)
1661 FILE *out ATTRIBUTE_UNUSED;
1662 HOST_WIDE_INT size ATTRIBUTE_UNUSED;
1663 {
1664 /* We catch the function epilogue generation to have a chance
1665 to clear the z_replacement_completed flag. */
1666 z_replacement_completed = 0;
1667 }
1668
1669 void
1670 expand_prologue ()
1671 {
1672 tree func_attr;
1673 int size;
1674 int regno;
1675 rtx scratch;
1676
1677 if (reload_completed != 1)
1678 abort ();
1679
1680 size = get_frame_size ();
1681
1682 create_regs_rtx ();
1683
1684 /* Generate specific prologue for interrupt handlers. */
1685 func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1686 current_function_interrupt = lookup_attribute ("interrupt",
1687 func_attr) != NULL_TREE;
1688 current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
1689 if (lookup_attribute ("far", func_attr) != NULL_TREE)
1690 current_function_far = 1;
1691 else if (lookup_attribute ("near", func_attr) != NULL_TREE)
1692 current_function_far = 0;
1693 else
1694 current_function_far = TARGET_LONG_CALLS != 0;
1695
1696 /* Get the scratch register to build the frame and push registers.
1697 If the first argument is a 32-bit quantity, the D+X registers
1698 are used. Use Y to compute the frame. Otherwise, X is cheaper.
1699 For 68HC12, this scratch register is not used. */
1700 if (current_function_args_info.nregs == 2)
1701 scratch = iy_reg;
1702 else
1703 scratch = ix_reg;
1704
1705 /* Save current stack frame. */
1706 if (frame_pointer_needed)
1707 emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
1708
1709 /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
1710 Other soft registers in page0 need not to be saved because they
1711 will be restored by C functions. For a trap handler, we don't
1712 need to preserve these registers because this is a synchronous call. */
1713 if (current_function_interrupt)
1714 {
1715 emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
1716 emit_move_after_reload (stack_push_word,
1717 gen_rtx (REG, HImode, SOFT_Z_REGNUM), scratch);
1718 emit_move_after_reload (stack_push_word,
1719 gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM),
1720 scratch);
1721 }
1722
1723 /* Allocate local variables. */
1724 if (TARGET_M6812 && (size > 4 || size == 3))
1725 {
1726 emit_insn (gen_addhi3 (stack_pointer_rtx,
1727 stack_pointer_rtx, GEN_INT (-size)));
1728 }
1729 else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
1730 {
1731 rtx insn;
1732
1733 insn = gen_rtx_PARALLEL
1734 (VOIDmode,
1735 gen_rtvec (2,
1736 gen_rtx_SET (VOIDmode,
1737 stack_pointer_rtx,
1738 gen_rtx_PLUS (HImode,
1739 stack_pointer_rtx,
1740 GEN_INT (-size))),
1741 gen_rtx_CLOBBER (VOIDmode, scratch)));
1742 emit_insn (insn);
1743 }
1744 else
1745 {
1746 int i;
1747
1748 /* Allocate by pushing scratch values. */
1749 for (i = 2; i <= size; i += 2)
1750 emit_move_after_reload (stack_push_word, ix_reg, 0);
1751
1752 if (size & 1)
1753 emit_insn (gen_addhi3 (stack_pointer_rtx,
1754 stack_pointer_rtx, GEN_INT (-1)));
1755 }
1756
1757 /* Create the frame pointer. */
1758 if (frame_pointer_needed)
1759 emit_move_after_reload (hard_frame_pointer_rtx,
1760 stack_pointer_rtx, scratch);
1761
1762 /* Push any 2 byte pseudo hard registers that we need to save. */
1763 for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
1764 {
1765 if (regs_ever_live[regno] && !call_used_regs[regno])
1766 {
1767 emit_move_after_reload (stack_push_word,
1768 gen_rtx (REG, HImode, regno), scratch);
1769 }
1770 }
1771 }
1772
1773 void
1774 expand_epilogue ()
1775 {
1776 int size;
1777 register int regno;
1778 int return_size;
1779 rtx scratch;
1780
1781 if (reload_completed != 1)
1782 abort ();
1783
1784 size = get_frame_size ();
1785
1786 /* If we are returning a value in two registers, we have to preserve the
1787 X register and use the Y register to restore the stack and the saved
1788 registers. Otherwise, use X because it's faster (and smaller). */
1789 if (current_function_return_rtx == 0)
1790 return_size = 0;
1791 else if (GET_CODE (current_function_return_rtx) == MEM)
1792 return_size = HARD_REG_SIZE;
1793 else
1794 return_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));
1795
1796 if (return_size > HARD_REG_SIZE && return_size <= 2 * HARD_REG_SIZE)
1797 scratch = iy_reg;
1798 else
1799 scratch = ix_reg;
1800
1801 /* Pop any 2 byte pseudo hard registers that we saved. */
1802 for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
1803 {
1804 if (regs_ever_live[regno] && !call_used_regs[regno])
1805 {
1806 emit_move_after_reload (gen_rtx (REG, HImode, regno),
1807 stack_pop_word, scratch);
1808 }
1809 }
1810
1811 /* de-allocate auto variables */
1812 if (TARGET_M6812 && (size > 4 || size == 3))
1813 {
1814 emit_insn (gen_addhi3 (stack_pointer_rtx,
1815 stack_pointer_rtx, GEN_INT (size)));
1816 }
1817 else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
1818 {
1819 rtx insn;
1820
1821 insn = gen_rtx_PARALLEL
1822 (VOIDmode,
1823 gen_rtvec (2,
1824 gen_rtx_SET (VOIDmode,
1825 stack_pointer_rtx,
1826 gen_rtx_PLUS (HImode,
1827 stack_pointer_rtx,
1828 GEN_INT (size))),
1829 gen_rtx_CLOBBER (VOIDmode, scratch)));
1830 emit_insn (insn);
1831 }
1832 else
1833 {
1834 int i;
1835
1836 for (i = 2; i <= size; i += 2)
1837 emit_move_after_reload (scratch, stack_pop_word, scratch);
1838 if (size & 1)
1839 emit_insn (gen_addhi3 (stack_pointer_rtx,
1840 stack_pointer_rtx, GEN_INT (1)));
1841 }
1842
1843 /* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
1844 if (current_function_interrupt)
1845 {
1846 emit_move_after_reload (gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM),
1847 stack_pop_word, scratch);
1848 emit_move_after_reload (gen_rtx (REG, HImode, SOFT_Z_REGNUM),
1849 stack_pop_word, scratch);
1850 emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
1851 }
1852
1853 /* Restore previous frame pointer. */
1854 if (frame_pointer_needed)
1855 emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
1856
1857 /* If the trap handler returns some value, copy the value
1858 in D, X onto the stack so that the rti will pop the return value
1859 correctly. */
1860 else if (current_function_trap && return_size != 0)
1861 {
1862 rtx addr_reg = stack_pointer_rtx;
1863
1864 if (!TARGET_M6812)
1865 {
1866 emit_move_after_reload (scratch, stack_pointer_rtx, 0);
1867 addr_reg = scratch;
1868 }
1869 emit_move_after_reload (gen_rtx (MEM, HImode,
1870 gen_rtx (PLUS, HImode, addr_reg,
1871 GEN_INT (1))), d_reg, 0);
1872 if (return_size > HARD_REG_SIZE)
1873 emit_move_after_reload (gen_rtx (MEM, HImode,
1874 gen_rtx (PLUS, HImode, addr_reg,
1875 GEN_INT (3))), ix_reg, 0);
1876 }
1877
1878 emit_jump_insn (gen_return ());
1879 }
1880 \f
1881
1882 /* Low and High part extraction for 68HC11. These routines are
1883 similar to gen_lowpart and gen_highpart but they have been
1884 fixed to work for constants and 68HC11 specific registers. */
1885
1886 rtx
1887 m68hc11_gen_lowpart (mode, x)
1888 enum machine_mode mode;
1889 rtx x;
1890 {
1891 /* We assume that the low part of an auto-inc mode is the same with
1892 the mode changed and that the caller split the larger mode in the
1893 correct order. */
1894 if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
1895 {
1896 return gen_rtx (MEM, mode, XEXP (x, 0));
1897 }
1898
1899 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1900 floating-point constant. A CONST_DOUBLE is used whenever the
1901 constant requires more than one word in order to be adequately
1902 represented. */
1903 if (GET_CODE (x) == CONST_DOUBLE)
1904 {
1905 long l[2];
1906
1907 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1908 {
1909 REAL_VALUE_TYPE r;
1910
1911 if (GET_MODE (x) == SFmode)
1912 {
1913 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1914 REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
1915 }
1916 else
1917 {
1918 rtx first, second;
1919
1920 split_double (x, &first, &second);
1921 return second;
1922 }
1923 if (mode == SImode)
1924 return GEN_INT (l[0]);
1925
1926 return gen_int_mode (l[0], HImode);
1927 }
1928 else
1929 {
1930 l[0] = CONST_DOUBLE_LOW (x);
1931 }
1932 if (mode == SImode)
1933 return GEN_INT (l[0]);
1934 else if (mode == HImode && GET_MODE (x) == SFmode)
1935 return gen_int_mode (l[0], HImode);
1936 else
1937 abort ();
1938 }
1939
1940 if (mode == QImode && D_REG_P (x))
1941 return gen_rtx (REG, mode, HARD_B_REGNUM);
1942
1943 /* gen_lowpart crashes when it is called with a SUBREG. */
1944 if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
1945 {
1946 if (mode == SImode)
1947 return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
1948 else if (mode == HImode)
1949 return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
1950 else
1951 abort ();
1952 }
1953 x = gen_lowpart (mode, x);
1954
1955 /* Return a different rtx to avoid to share it in several insns
1956 (when used by a split pattern). Sharing addresses within
1957 a MEM breaks the Z register replacement (and reloading). */
1958 if (GET_CODE (x) == MEM)
1959 x = copy_rtx (x);
1960 return x;
1961 }
1962
1963 rtx
1964 m68hc11_gen_highpart (mode, x)
1965 enum machine_mode mode;
1966 rtx x;
1967 {
1968 /* We assume that the high part of an auto-inc mode is the same with
1969 the mode changed and that the caller split the larger mode in the
1970 correct order. */
1971 if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
1972 {
1973 return gen_rtx (MEM, mode, XEXP (x, 0));
1974 }
1975
1976 /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1977 floating-point constant. A CONST_DOUBLE is used whenever the
1978 constant requires more than one word in order to be adequately
1979 represented. */
1980 if (GET_CODE (x) == CONST_DOUBLE)
1981 {
1982 long l[2];
1983
1984 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1985 {
1986 REAL_VALUE_TYPE r;
1987
1988 if (GET_MODE (x) == SFmode)
1989 {
1990 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1991 REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
1992 }
1993 else
1994 {
1995 rtx first, second;
1996
1997 split_double (x, &first, &second);
1998 return first;
1999 }
2000 if (mode == SImode)
2001 return GEN_INT (l[1]);
2002
2003 return gen_int_mode ((l[1] >> 16), HImode);
2004 }
2005 else
2006 {
2007 l[1] = CONST_DOUBLE_HIGH (x);
2008 }
2009
2010 if (mode == SImode)
2011 return GEN_INT (l[1]);
2012 else if (mode == HImode && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
2013 return gen_int_mode ((l[0] >> 16), HImode);
2014 else
2015 abort ();
2016 }
2017 if (GET_CODE (x) == CONST_INT)
2018 {
2019 HOST_WIDE_INT val = INTVAL (x);
2020
2021 if (mode == QImode)
2022 {
2023 return gen_int_mode (val >> 8, QImode);
2024 }
2025 else if (mode == HImode)
2026 {
2027 return gen_int_mode (val >> 16, HImode);
2028 }
2029 }
2030 if (mode == QImode && D_REG_P (x))
2031 return gen_rtx (REG, mode, HARD_A_REGNUM);
2032
2033 /* There is no way in GCC to represent the upper part of a word register.
2034 To obtain the 8-bit upper part of a soft register, we change the
2035 reg into a mem rtx. This is possible because they are physically
2036 located in memory. There is no offset because we are big-endian. */
2037 if (mode == QImode && S_REG_P (x))
2038 {
2039 int pos;
2040
2041 /* Avoid the '*' for direct addressing mode when this
2042 addressing mode is disabled. */
2043 pos = TARGET_NO_DIRECT_MODE ? 1 : 0;
2044 return gen_rtx (MEM, QImode,
2045 gen_rtx (SYMBOL_REF, Pmode,
2046 &reg_names[REGNO (x)][pos]));
2047 }
2048
2049 /* gen_highpart crashes when it is called with a SUBREG. */
2050 if (GET_CODE (x) == SUBREG)
2051 {
2052 return gen_rtx (SUBREG, mode, XEXP (x, 0), XEXP (x, 1));
2053 }
2054 if (GET_CODE (x) == REG)
2055 {
2056 if (REGNO (x) < FIRST_PSEUDO_REGISTER)
2057 return gen_rtx (REG, mode, REGNO (x));
2058 else
2059 return gen_rtx_SUBREG (mode, x, 0);
2060 }
2061
2062 if (GET_CODE (x) == MEM)
2063 {
2064 x = change_address (x, mode, 0);
2065
2066 /* Return a different rtx to avoid to share it in several insns
2067 (when used by a split pattern). Sharing addresses within
2068 a MEM breaks the Z register replacement (and reloading). */
2069 if (GET_CODE (x) == MEM)
2070 x = copy_rtx (x);
2071 return x;
2072 }
2073 abort ();
2074 }
2075 \f
2076
2077 /* Obscure register manipulation. */
2078
2079 /* Finds backward in the instructions to see if register 'reg' is
2080 dead. This is used when generating code to see if we can use 'reg'
2081 as a scratch register. This allows us to choose a better generation
2082 of code when we know that some register dies or can be clobbered. */
2083
2084 int
2085 dead_register_here (x, reg)
2086 rtx x;
2087 rtx reg;
2088 {
2089 rtx x_reg;
2090 rtx p;
2091
2092 if (D_REG_P (reg))
2093 x_reg = gen_rtx (REG, SImode, HARD_X_REGNUM);
2094 else
2095 x_reg = 0;
2096
2097 for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
2098 if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
2099 {
2100 rtx body;
2101
2102 body = PATTERN (p);
2103
2104 if (GET_CODE (body) == CALL_INSN)
2105 break;
2106 if (GET_CODE (body) == JUMP_INSN)
2107 break;
2108
2109 if (GET_CODE (body) == SET)
2110 {
2111 rtx dst = XEXP (body, 0);
2112
2113 if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
2114 break;
2115 if (x_reg && rtx_equal_p (dst, x_reg))
2116 break;
2117
2118 if (find_regno_note (p, REG_DEAD, REGNO (reg)))
2119 return 1;
2120 }
2121 else if (reg_mentioned_p (reg, p)
2122 || (x_reg && reg_mentioned_p (x_reg, p)))
2123 break;
2124 }
2125
2126 /* Scan forward to see if the register is set in some insns and never
2127 used since then. */
2128 for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
2129 {
2130 rtx body;
2131
2132 if (GET_CODE (p) == CODE_LABEL
2133 || GET_CODE (p) == JUMP_INSN
2134 || GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
2135 break;
2136
2137 if (GET_CODE (p) != INSN)
2138 continue;
2139
2140 body = PATTERN (p);
2141 if (GET_CODE (body) == SET)
2142 {
2143 rtx src = XEXP (body, 1);
2144 rtx dst = XEXP (body, 0);
2145
2146 if (GET_CODE (dst) == REG
2147 && REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
2148 return 1;
2149 }
2150
2151 /* Register is used (may be in source or in dest). */
2152 if (reg_mentioned_p (reg, p)
2153 || (x_reg != 0 && GET_MODE (p) == SImode
2154 && reg_mentioned_p (x_reg, p)))
2155 break;
2156 }
2157 return p == 0 ? 1 : 0;
2158 }
2159 \f
2160
2161 /* Code generation operations called from machine description file. */
2162
2163 /* Print the name of register 'regno' in the assembly file. */
2164 static void
2165 asm_print_register (file, regno)
2166 FILE *file;
2167 int regno;
2168 {
2169 const char *name = reg_names[regno];
2170
2171 if (TARGET_NO_DIRECT_MODE && name[0] == '*')
2172 name++;
2173
2174 fprintf (file, "%s", name);
2175 }
2176
2177 /* A C compound statement to output to stdio stream STREAM the
2178 assembler syntax for an instruction operand X. X is an RTL
2179 expression.
2180
2181 CODE is a value that can be used to specify one of several ways
2182 of printing the operand. It is used when identical operands
2183 must be printed differently depending on the context. CODE
2184 comes from the `%' specification that was used to request
2185 printing of the operand. If the specification was just `%DIGIT'
2186 then CODE is 0; if the specification was `%LTR DIGIT' then CODE
2187 is the ASCII code for LTR.
2188
2189 If X is a register, this macro should print the register's name.
2190 The names can be found in an array `reg_names' whose type is
2191 `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
2192
2193 When the machine description has a specification `%PUNCT' (a `%'
2194 followed by a punctuation character), this macro is called with
2195 a null pointer for X and the punctuation character for CODE.
2196
2197 The M68HC11 specific codes are:
2198
2199 'b' for the low part of the operand.
2200 'h' for the high part of the operand
2201 The 'b' or 'h' modifiers have no effect if the operand has
2202 the QImode and is not a S_REG_P (soft register). If the
2203 operand is a hard register, these two modifiers have no effect.
2204 't' generate the temporary scratch register. The operand is
2205 ignored.
2206 'T' generate the low-part temporary scratch register. The operand is
2207 ignored. */
2208
2209 void
2210 print_operand (file, op, letter)
2211 FILE *file;
2212 rtx op;
2213 int letter;
2214 {
2215 if (letter == 't')
2216 {
2217 asm_print_register (file, SOFT_TMP_REGNUM);
2218 return;
2219 }
2220 else if (letter == 'T')
2221 {
2222 asm_print_register (file, SOFT_TMP_REGNUM);
2223 fprintf (file, "+1");
2224 return;
2225 }
2226 else if (letter == '#')
2227 {
2228 asm_fprintf (file, "%I");
2229 }
2230
2231 if (GET_CODE (op) == REG)
2232 {
2233 if (letter == 'b' && S_REG_P (op))
2234 {
2235 asm_print_register (file, REGNO (op));
2236 fprintf (file, "+1");
2237 }
2238 else if (letter == 'b' && D_REG_P (op))
2239 {
2240 asm_print_register (file, HARD_B_REGNUM);
2241 }
2242 else
2243 {
2244 asm_print_register (file, REGNO (op));
2245 }
2246 return;
2247 }
2248
2249 if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
2250 {
2251 if (letter == 'b')
2252 asm_fprintf (file, "%I%%lo(");
2253 else
2254 asm_fprintf (file, "%I%%hi(");
2255
2256 output_addr_const (file, op);
2257 fprintf (file, ")");
2258 return;
2259 }
2260
2261 /* Get the low or high part of the operand when 'b' or 'h' modifiers
2262 are specified. If we already have a QImode, there is nothing to do. */
2263 if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
2264 {
2265 if (letter == 'b')
2266 {
2267 op = m68hc11_gen_lowpart (QImode, op);
2268 }
2269 else if (letter == 'h')
2270 {
2271 op = m68hc11_gen_highpart (QImode, op);
2272 }
2273 }
2274
2275 if (GET_CODE (op) == MEM)
2276 {
2277 rtx base = XEXP (op, 0);
2278 switch (GET_CODE (base))
2279 {
2280 case PRE_DEC:
2281 if (TARGET_M6812)
2282 {
2283 fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
2284 asm_print_register (file, REGNO (XEXP (base, 0)));
2285 }
2286 else
2287 abort ();
2288 break;
2289
2290 case POST_DEC:
2291 if (TARGET_M6812)
2292 {
2293 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
2294 asm_print_register (file, REGNO (XEXP (base, 0)));
2295 fprintf (file, "-");
2296 }
2297 else
2298 abort ();
2299 break;
2300
2301 case POST_INC:
2302 if (TARGET_M6812)
2303 {
2304 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
2305 asm_print_register (file, REGNO (XEXP (base, 0)));
2306 fprintf (file, "+");
2307 }
2308 else
2309 abort ();
2310 break;
2311
2312 case PRE_INC:
2313 if (TARGET_M6812)
2314 {
2315 fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
2316 asm_print_register (file, REGNO (XEXP (base, 0)));
2317 }
2318 else
2319 abort ();
2320 break;
2321
2322 default:
2323 output_address (base);
2324 break;
2325 }
2326 }
2327 else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
2328 {
2329 REAL_VALUE_TYPE r;
2330 long l;
2331
2332 REAL_VALUE_FROM_CONST_DOUBLE (r, op);
2333 REAL_VALUE_TO_TARGET_SINGLE (r, l);
2334 asm_fprintf (file, "%I0x%lx", l);
2335 }
2336 else if (GET_CODE (op) == CONST_DOUBLE
2337 && (GET_MODE (op) == DFmode || GET_MODE (op) == XFmode))
2338 {
2339 char dstr[30];
2340
2341 real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (op),
2342 sizeof (dstr), 0, 1);
2343 asm_fprintf (file, "%I0r%s", dstr);
2344 }
2345 else
2346 {
2347 int need_parenthesize = 0;
2348
2349 if (letter != 'i')
2350 asm_fprintf (file, "%I");
2351 else
2352 need_parenthesize = must_parenthesize (op);
2353
2354 if (need_parenthesize)
2355 fprintf (file, "(");
2356
2357 output_addr_const (file, op);
2358 if (need_parenthesize)
2359 fprintf (file, ")");
2360 }
2361 }
2362
2363 /* Returns true if the operand 'op' must be printed with parenthesis
2364 arround it. This must be done only if there is a symbol whose name
2365 is a processor register. */
2366 static int
2367 must_parenthesize (op)
2368 rtx op;
2369 {
2370 const char *name;
2371
2372 switch (GET_CODE (op))
2373 {
2374 case SYMBOL_REF:
2375 name = XSTR (op, 0);
2376 /* Avoid a conflict between symbol name and a possible
2377 register. */
2378 return (strcasecmp (name, "a") == 0
2379 || strcasecmp (name, "b") == 0
2380 || strcasecmp (name, "d") == 0
2381 || strcasecmp (name, "x") == 0
2382 || strcasecmp (name, "y") == 0
2383 || strcasecmp (name, "ix") == 0
2384 || strcasecmp (name, "iy") == 0
2385 || strcasecmp (name, "pc") == 0
2386 || strcasecmp (name, "sp") == 0
2387 || strcasecmp (name, "ccr") == 0) ? 1 : 0;
2388
2389 case PLUS:
2390 case MINUS:
2391 return must_parenthesize (XEXP (op, 0))
2392 || must_parenthesize (XEXP (op, 1));
2393
2394 case MEM:
2395 case CONST:
2396 case ZERO_EXTEND:
2397 case SIGN_EXTEND:
2398 return must_parenthesize (XEXP (op, 0));
2399
2400 case CONST_DOUBLE:
2401 case CONST_INT:
2402 case LABEL_REF:
2403 case CODE_LABEL:
2404 default:
2405 return 0;
2406 }
2407 }
2408
2409 /* A C compound statement to output to stdio stream STREAM the
2410 assembler syntax for an instruction operand that is a memory
2411 reference whose address is ADDR. ADDR is an RTL expression. */
2412
2413 void
2414 print_operand_address (file, addr)
2415 FILE *file;
2416 rtx addr;
2417 {
2418 rtx base;
2419 rtx offset;
2420 int need_parenthesis = 0;
2421
2422 switch (GET_CODE (addr))
2423 {
2424 case REG:
2425 if (!REG_P (addr) || !REG_OK_FOR_BASE_STRICT_P (addr))
2426 abort ();
2427
2428 fprintf (file, "0,");
2429 asm_print_register (file, REGNO (addr));
2430 break;
2431
2432 case MEM:
2433 base = XEXP (addr, 0);
2434 switch (GET_CODE (base))
2435 {
2436 case PRE_DEC:
2437 if (TARGET_M6812)
2438 {
2439 fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
2440 asm_print_register (file, REGNO (XEXP (base, 0)));
2441 }
2442 else
2443 abort ();
2444 break;
2445
2446 case POST_DEC:
2447 if (TARGET_M6812)
2448 {
2449 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
2450 asm_print_register (file, REGNO (XEXP (base, 0)));
2451 fprintf (file, "-");
2452 }
2453 else
2454 abort ();
2455 break;
2456
2457 case POST_INC:
2458 if (TARGET_M6812)
2459 {
2460 fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
2461 asm_print_register (file, REGNO (XEXP (base, 0)));
2462 fprintf (file, "+");
2463 }
2464 else
2465 abort ();
2466 break;
2467
2468 case PRE_INC:
2469 if (TARGET_M6812)
2470 {
2471 fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
2472 asm_print_register (file, REGNO (XEXP (base, 0)));
2473 }
2474 else
2475 abort ();
2476 break;
2477
2478 default:
2479 need_parenthesis = must_parenthesize (base);
2480 if (need_parenthesis)
2481 fprintf (file, "(");
2482
2483 output_addr_const (file, base);
2484 if (need_parenthesis)
2485 fprintf (file, ")");
2486 break;
2487 }
2488 break;
2489
2490 case PLUS:
2491 base = XEXP (addr, 0);
2492 offset = XEXP (addr, 1);
2493 if (!G_REG_P (base) && G_REG_P (offset))
2494 {
2495 base = XEXP (addr, 1);
2496 offset = XEXP (addr, 0);
2497 }
2498 if ((CONSTANT_ADDRESS_P (base)) && (CONSTANT_ADDRESS_P (offset)))
2499 {
2500 need_parenthesis = must_parenthesize (addr);
2501
2502 if (need_parenthesis)
2503 fprintf (file, "(");
2504
2505 output_addr_const (file, base);
2506 fprintf (file, "+");
2507 output_addr_const (file, offset);
2508 if (need_parenthesis)
2509 fprintf (file, ")");
2510 }
2511 else if (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base))
2512 {
2513 if (REG_P (offset))
2514 {
2515 if (TARGET_M6812)
2516 {
2517 asm_print_register (file, REGNO (offset));
2518 fprintf (file, ",");
2519 asm_print_register (file, REGNO (base));
2520 }
2521 else
2522 abort ();
2523 }
2524 else
2525 {
2526 need_parenthesis = must_parenthesize (offset);
2527 if (need_parenthesis)
2528 fprintf (file, "(");
2529
2530 output_addr_const (file, offset);
2531 if (need_parenthesis)
2532 fprintf (file, ")");
2533 fprintf (file, ",");
2534 asm_print_register (file, REGNO (base));
2535 }
2536 }
2537 else
2538 {
2539 abort ();
2540 }
2541 break;
2542
2543 default:
2544 if (GET_CODE (addr) == CONST_INT
2545 && INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
2546 {
2547 fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
2548 }
2549 else
2550 {
2551 need_parenthesis = must_parenthesize (addr);
2552 if (need_parenthesis)
2553 fprintf (file, "(");
2554
2555 output_addr_const (file, addr);
2556 if (need_parenthesis)
2557 fprintf (file, ")");
2558 }
2559 break;
2560 }
2561 }
2562 \f
2563
2564 /* Splitting of some instructions. */
2565
2566 static rtx
2567 m68hc11_expand_compare (code, op0, op1)
2568 enum rtx_code code;
2569 rtx op0, op1;
2570 {
2571 rtx ret = 0;
2572
2573 if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
2574 abort ();
2575 else
2576 {
2577 emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
2578 gen_rtx_COMPARE (VOIDmode, op0, op1)));
2579 ret = gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx);
2580 }
2581
2582 return ret;
2583 }
2584
2585 rtx
2586 m68hc11_expand_compare_and_branch (code, op0, op1, label)
2587 enum rtx_code code;
2588 rtx op0, op1, label;
2589 {
2590 rtx tmp;
2591
2592 switch (GET_MODE (op0))
2593 {
2594 case QImode:
2595 case HImode:
2596 tmp = m68hc11_expand_compare (code, op0, op1);
2597 tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
2598 gen_rtx_LABEL_REF (VOIDmode, label),
2599 pc_rtx);
2600 emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
2601 return 0;
2602 #if 0
2603
2604 /* SCz: from i386.c */
2605 case SFmode:
2606 case DFmode:
2607 /* Don't expand the comparison early, so that we get better code
2608 when jump or whoever decides to reverse the comparison. */
2609 {
2610 rtvec vec;
2611 int use_fcomi;
2612
2613 code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
2614 &m68hc11_compare_op1);
2615
2616 tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
2617 m68hc11_compare_op0, m68hc11_compare_op1);
2618 tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
2619 gen_rtx_LABEL_REF (VOIDmode, label),
2620 pc_rtx);
2621 tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
2622
2623 use_fcomi = ix86_use_fcomi_compare (code);
2624 vec = rtvec_alloc (3 + !use_fcomi);
2625 RTVEC_ELT (vec, 0) = tmp;
2626 RTVEC_ELT (vec, 1)
2627 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
2628 RTVEC_ELT (vec, 2)
2629 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
2630 if (!use_fcomi)
2631 RTVEC_ELT (vec, 3)
2632 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
2633
2634 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
2635 return;
2636 }
2637 #endif
2638
2639 case SImode:
2640 /* Expand SImode branch into multiple compare+branch. */
2641 {
2642 rtx lo[2], hi[2], label2;
2643 enum rtx_code code1, code2, code3;
2644
2645 if (CONSTANT_P (op0) && !CONSTANT_P (op1))
2646 {
2647 tmp = op0;
2648 op0 = op1;
2649 op1 = tmp;
2650 code = swap_condition (code);
2651 }
2652 lo[0] = m68hc11_gen_lowpart (HImode, op0);
2653 lo[1] = m68hc11_gen_lowpart (HImode, op1);
2654 hi[0] = m68hc11_gen_highpart (HImode, op0);
2655 hi[1] = m68hc11_gen_highpart (HImode, op1);
2656
2657 /* Otherwise, if we are doing less-than, op1 is a constant and the
2658 low word is zero, then we can just examine the high word. */
2659
2660 if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
2661 && (code == LT || code == LTU))
2662 {
2663 return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
2664 label);
2665 }
2666
2667 /* Otherwise, we need two or three jumps. */
2668
2669 label2 = gen_label_rtx ();
2670
2671 code1 = code;
2672 code2 = swap_condition (code);
2673 code3 = unsigned_condition (code);
2674
2675 switch (code)
2676 {
2677 case LT:
2678 case GT:
2679 case LTU:
2680 case GTU:
2681 break;
2682
2683 case LE:
2684 code1 = LT;
2685 code2 = GT;
2686 break;
2687 case GE:
2688 code1 = GT;
2689 code2 = LT;
2690 break;
2691 case LEU:
2692 code1 = LTU;
2693 code2 = GTU;
2694 break;
2695 case GEU:
2696 code1 = GTU;
2697 code2 = LTU;
2698 break;
2699
2700 case EQ:
2701 code1 = NIL;
2702 code2 = NE;
2703 break;
2704 case NE:
2705 code2 = NIL;
2706 break;
2707
2708 default:
2709 abort ();
2710 }
2711
2712 /*
2713 * a < b =>
2714 * if (hi(a) < hi(b)) goto true;
2715 * if (hi(a) > hi(b)) goto false;
2716 * if (lo(a) < lo(b)) goto true;
2717 * false:
2718 */
2719 if (code1 != NIL)
2720 m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
2721 if (code2 != NIL)
2722 m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
2723
2724 m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
2725
2726 if (code2 != NIL)
2727 emit_label (label2);
2728 return 0;
2729 }
2730
2731 default:
2732 abort ();
2733 }
2734 return 0;
2735 }
2736
2737 /* Return the increment/decrement mode of a MEM if it is such.
2738 Return CONST if it is anything else. */
2739 static int
2740 autoinc_mode (x)
2741 rtx x;
2742 {
2743 if (GET_CODE (x) != MEM)
2744 return CONST;
2745
2746 x = XEXP (x, 0);
2747 if (GET_CODE (x) == PRE_INC
2748 || GET_CODE (x) == PRE_DEC
2749 || GET_CODE (x) == POST_INC
2750 || GET_CODE (x) == POST_DEC)
2751 return GET_CODE (x);
2752
2753 return CONST;
2754 }
2755
2756 static int
2757 m68hc11_make_autoinc_notes (x, data)
2758 rtx *x;
2759 void *data;
2760 {
2761 rtx insn;
2762
2763 switch (GET_CODE (*x))
2764 {
2765 case PRE_DEC:
2766 case PRE_INC:
2767 case POST_DEC:
2768 case POST_INC:
2769 insn = (rtx) data;
2770 REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, XEXP (*x, 0),
2771 REG_NOTES (insn));
2772 return -1;
2773
2774 default:
2775 return 0;
2776 }
2777 }
2778
2779 /* Split a DI, SI or HI move into several smaller move operations.
2780 The scratch register 'scratch' is used as a temporary to load
2781 store intermediate values. It must be a hard register. */
2782 void
2783 m68hc11_split_move (to, from, scratch)
2784 rtx to, from, scratch;
2785 {
2786 rtx low_to, low_from;
2787 rtx high_to, high_from;
2788 rtx insn;
2789 enum machine_mode mode;
2790 int offset = 0;
2791 int autoinc_from = autoinc_mode (from);
2792 int autoinc_to = autoinc_mode (to);
2793
2794 mode = GET_MODE (to);
2795
2796 /* If the TO and FROM contain autoinc modes that are not compatible
2797 together (one pop and the other a push), we must change one to
2798 an offsetable operand and generate an appropriate add at the end. */
2799 if (TARGET_M6812 && GET_MODE_SIZE (mode) > 2)
2800 {
2801 rtx reg;
2802 int code;
2803
2804 /* The source uses an autoinc mode which is not compatible with
2805 a split (this would result in a word swap). */
2806 if (autoinc_from == PRE_INC || autoinc_from == POST_DEC)
2807 {
2808 code = GET_CODE (XEXP (from, 0));
2809 reg = XEXP (XEXP (from, 0), 0);
2810 offset = GET_MODE_SIZE (GET_MODE (from));
2811 if (code == POST_DEC)
2812 offset = -offset;
2813
2814 if (code == PRE_INC)
2815 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2816
2817 m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
2818 if (code == POST_DEC)
2819 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2820 return;
2821 }
2822
2823 /* Likewise for destination. */
2824 if (autoinc_to == PRE_INC || autoinc_to == POST_DEC)
2825 {
2826 code = GET_CODE (XEXP (to, 0));
2827 reg = XEXP (XEXP (to, 0), 0);
2828 offset = GET_MODE_SIZE (GET_MODE (to));
2829 if (code == POST_DEC)
2830 offset = -offset;
2831
2832 if (code == PRE_INC)
2833 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2834
2835 m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
2836 if (code == POST_DEC)
2837 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2838 return;
2839 }
2840
2841 /* The source and destination auto increment modes must be compatible
2842 with each other: same direction. */
2843 if ((autoinc_to != autoinc_from
2844 && autoinc_to != CONST && autoinc_from != CONST)
2845 /* The destination address register must not be used within
2846 the source operand because the source address would change
2847 while doing the copy. */
2848 || (autoinc_to != CONST
2849 && reg_mentioned_p (XEXP (XEXP (to, 0), 0), from)
2850 && !IS_STACK_PUSH (to)))
2851 {
2852 /* Must change the destination. */
2853 code = GET_CODE (XEXP (to, 0));
2854 reg = XEXP (XEXP (to, 0), 0);
2855 offset = GET_MODE_SIZE (GET_MODE (to));
2856 if (code == PRE_DEC || code == POST_DEC)
2857 offset = -offset;
2858
2859 if (code == PRE_DEC || code == PRE_INC)
2860 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2861 m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
2862 if (code == POST_DEC || code == POST_INC)
2863 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2864
2865 return;
2866 }
2867
2868 /* Likewise, the source address register must not be used within
2869 the destination operand. */
2870 if (autoinc_from != CONST
2871 && reg_mentioned_p (XEXP (XEXP (from, 0), 0), to)
2872 && !IS_STACK_PUSH (to))
2873 {
2874 /* Must change the source. */
2875 code = GET_CODE (XEXP (from, 0));
2876 reg = XEXP (XEXP (from, 0), 0);
2877 offset = GET_MODE_SIZE (GET_MODE (from));
2878 if (code == PRE_DEC || code == POST_DEC)
2879 offset = -offset;
2880
2881 if (code == PRE_DEC || code == PRE_INC)
2882 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2883 m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
2884 if (code == POST_DEC || code == POST_INC)
2885 emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
2886
2887 return;
2888 }
2889 }
2890
2891 if (GET_MODE_SIZE (mode) == 8)
2892 mode = SImode;
2893 else if (GET_MODE_SIZE (mode) == 4)
2894 mode = HImode;
2895 else
2896 mode = QImode;
2897
2898 if (TARGET_M6812
2899 && IS_STACK_PUSH (to)
2900 && reg_mentioned_p (gen_rtx (REG, HImode, HARD_SP_REGNUM), from))
2901 {
2902 if (mode == SImode)
2903 {
2904 offset = 4;
2905 }
2906 else if (mode == HImode)
2907 {
2908 offset = 2;
2909 }
2910 else
2911 offset = 0;
2912 }
2913
2914 low_to = m68hc11_gen_lowpart (mode, to);
2915 high_to = m68hc11_gen_highpart (mode, to);
2916
2917 low_from = m68hc11_gen_lowpart (mode, from);
2918 if (mode == SImode && GET_CODE (from) == CONST_INT)
2919 {
2920 if (INTVAL (from) >= 0)
2921 high_from = const0_rtx;
2922 else
2923 high_from = constm1_rtx;
2924 }
2925 else
2926 high_from = m68hc11_gen_highpart (mode, from);
2927
2928 if (offset)
2929 {
2930 high_from = adjust_address (high_from, mode, offset);
2931 low_from = high_from;
2932 }
2933
2934 /* When copying with a POST_INC mode, we must copy the
2935 high part and then the low part to guarantee a correct
2936 32/64-bit copy. */
2937 if (TARGET_M6812
2938 && GET_MODE_SIZE (mode) >= 2
2939 && autoinc_from != autoinc_to
2940 && (autoinc_from == POST_INC || autoinc_to == POST_INC))
2941 {
2942 rtx swap;
2943
2944 swap = low_to;
2945 low_to = high_to;
2946 high_to = swap;
2947
2948 swap = low_from;
2949 low_from = high_from;
2950 high_from = swap;
2951 }
2952 if (mode == SImode)
2953 {
2954 m68hc11_split_move (low_to, low_from, scratch);
2955 m68hc11_split_move (high_to, high_from, scratch);
2956 }
2957 else if (H_REG_P (to) || H_REG_P (from)
2958 || (low_from == const0_rtx
2959 && high_from == const0_rtx
2960 && ! push_operand (to, GET_MODE (to))
2961 && ! H_REG_P (scratch))
2962 || (TARGET_M6812
2963 && (!m68hc11_register_indirect_p (from, GET_MODE (from))
2964 || m68hc11_small_indexed_indirect_p (from,
2965 GET_MODE (from)))
2966 && (!m68hc11_register_indirect_p (to, GET_MODE (to))
2967 || m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
2968 {
2969 insn = emit_move_insn (low_to, low_from);
2970 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2971
2972 insn = emit_move_insn (high_to, high_from);
2973 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2974 }
2975 else
2976 {
2977 insn = emit_move_insn (scratch, low_from);
2978 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2979 insn = emit_move_insn (low_to, scratch);
2980 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2981
2982 insn = emit_move_insn (scratch, high_from);
2983 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2984 insn = emit_move_insn (high_to, scratch);
2985 for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
2986 }
2987 }
2988
2989 static rtx
2990 simplify_logical (mode, code, operand, result)
2991 enum machine_mode mode;
2992 int code;
2993 rtx operand;
2994 rtx *result;
2995 {
2996 int val;
2997 int mask;
2998
2999 *result = 0;
3000 if (GET_CODE (operand) != CONST_INT)
3001 return operand;
3002
3003 if (mode == HImode)
3004 mask = 0x0ffff;
3005 else
3006 mask = 0x0ff;
3007
3008 val = INTVAL (operand);
3009 switch (code)
3010 {
3011 case IOR:
3012 if ((val & mask) == 0)
3013 return 0;
3014 if ((val & mask) == mask)
3015 *result = constm1_rtx;
3016 break;
3017
3018 case AND:
3019 if ((val & mask) == 0)
3020 *result = const0_rtx;
3021 if ((val & mask) == mask)
3022 return 0;
3023 break;
3024
3025 case XOR:
3026 if ((val & mask) == 0)
3027 return 0;
3028 break;
3029 }
3030 return operand;
3031 }
3032
3033 static void
3034 m68hc11_emit_logical (mode, code, operands)
3035 enum machine_mode mode;
3036 int code;
3037 rtx *operands;
3038 {
3039 rtx result;
3040 int need_copy;
3041
3042 need_copy = (rtx_equal_p (operands[0], operands[1])
3043 || rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
3044
3045 operands[1] = simplify_logical (mode, code, operands[1], &result);
3046 operands[2] = simplify_logical (mode, code, operands[2], &result);
3047
3048 if (result && GET_CODE (result) == CONST_INT)
3049 {
3050 if (!H_REG_P (operands[0]) && operands[3]
3051 && (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
3052 {
3053 emit_move_insn (operands[3], result);
3054 emit_move_insn (operands[0], operands[3]);
3055 }
3056 else
3057 {
3058 emit_move_insn (operands[0], result);
3059 }
3060 }
3061 else if (operands[1] != 0 && operands[2] != 0)
3062 {
3063 rtx insn;
3064
3065 if (!H_REG_P (operands[0]) && operands[3])
3066 {
3067 emit_move_insn (operands[3], operands[1]);
3068 emit_insn (gen_rtx (SET, mode,
3069 operands[3],
3070 gen_rtx (code, mode,
3071 operands[3], operands[2])));
3072 insn = emit_move_insn (operands[0], operands[3]);
3073 }
3074 else
3075 {
3076 insn = emit_insn (gen_rtx (SET, mode,
3077 operands[0],
3078 gen_rtx (code, mode,
3079 operands[0], operands[2])));
3080 }
3081 }
3082
3083 /* The logical operation is similar to a copy. */
3084 else if (need_copy)
3085 {
3086 rtx src;
3087
3088 if (GET_CODE (operands[1]) == CONST_INT)
3089 src = operands[2];
3090 else
3091 src = operands[1];
3092
3093 if (!H_REG_P (operands[0]) && !H_REG_P (src))
3094 {
3095 emit_move_insn (operands[3], src);
3096 emit_move_insn (operands[0], operands[3]);
3097 }
3098 else
3099 {
3100 emit_move_insn (operands[0], src);
3101 }
3102 }
3103 }
3104
3105 void
3106 m68hc11_split_logical (mode, code, operands)
3107 enum machine_mode mode;
3108 int code;
3109 rtx *operands;
3110 {
3111 rtx low[4];
3112 rtx high[4];
3113
3114 low[0] = m68hc11_gen_lowpart (mode, operands[0]);
3115 low[1] = m68hc11_gen_lowpart (mode, operands[1]);
3116 low[2] = m68hc11_gen_lowpart (mode, operands[2]);
3117
3118 high[0] = m68hc11_gen_highpart (mode, operands[0]);
3119
3120 if (mode == SImode && GET_CODE (operands[1]) == CONST_INT)
3121 {
3122 if (INTVAL (operands[1]) >= 0)
3123 high[1] = const0_rtx;
3124 else
3125 high[1] = constm1_rtx;
3126 }
3127 else
3128 high[1] = m68hc11_gen_highpart (mode, operands[1]);
3129
3130 if (mode == SImode && GET_CODE (operands[2]) == CONST_INT)
3131 {
3132 if (INTVAL (operands[2]) >= 0)
3133 high[2] = const0_rtx;
3134 else
3135 high[2] = constm1_rtx;
3136 }
3137 else
3138 high[2] = m68hc11_gen_highpart (mode, operands[2]);
3139
3140 low[3] = operands[3];
3141 high[3] = operands[3];
3142 if (mode == SImode)
3143 {
3144 m68hc11_split_logical (HImode, code, low);
3145 m68hc11_split_logical (HImode, code, high);
3146 return;
3147 }
3148
3149 m68hc11_emit_logical (mode, code, low);
3150 m68hc11_emit_logical (mode, code, high);
3151 }
3152 \f
3153
3154 /* Code generation. */
3155
3156 void
3157 m68hc11_output_swap (insn, operands)
3158 rtx insn ATTRIBUTE_UNUSED;
3159 rtx operands[];
3160 {
3161 /* We have to be careful with the cc_status. An address register swap
3162 is generated for some comparison. The comparison is made with D
3163 but the branch really uses the address register. See the split
3164 pattern for compare. The xgdx/xgdy preserve the flags but after
3165 the exchange, the flags will reflect to the value of X and not D.
3166 Tell this by setting the cc_status according to the cc_prev_status. */
3167 if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
3168 {
3169 if (cc_prev_status.value1 != 0
3170 && (D_REG_P (cc_prev_status.value1)
3171 || X_REG_P (cc_prev_status.value1)))
3172 {
3173 cc_status = cc_prev_status;
3174 if (D_REG_P (cc_status.value1))
3175 cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
3176 HARD_X_REGNUM);
3177 else
3178 cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
3179 HARD_D_REGNUM);
3180 }
3181 else
3182 CC_STATUS_INIT;
3183
3184 output_asm_insn ("xgdx", operands);
3185 }
3186 else
3187 {
3188 if (cc_prev_status.value1 != 0
3189 && (D_REG_P (cc_prev_status.value1)
3190 || Y_REG_P (cc_prev_status.value1)))
3191 {
3192 cc_status = cc_prev_status;
3193 if (D_REG_P (cc_status.value1))
3194 cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
3195 HARD_Y_REGNUM);
3196 else
3197 cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
3198 HARD_D_REGNUM);
3199 }
3200 else
3201 CC_STATUS_INIT;
3202
3203 output_asm_insn ("xgdy", operands);
3204 }
3205 }
3206
3207 /* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
3208 This is used to decide whether a move that set flags should be used
3209 instead. */
3210 int
3211 next_insn_test_reg (insn, reg)
3212 rtx insn;
3213 rtx reg;
3214 {
3215 rtx body;
3216
3217 insn = next_nonnote_insn (insn);
3218 if (GET_CODE (insn) != INSN)
3219 return 0;
3220
3221 body = PATTERN (insn);
3222 if (sets_cc0_p (body) != 1)
3223 return 0;
3224
3225 if (rtx_equal_p (XEXP (body, 1), reg) == 0)
3226 return 0;
3227
3228 return 1;
3229 }
3230
3231 /* Generate the code to move a 16-bit operand into another one. */
3232
3233 void
3234 m68hc11_gen_movhi (insn, operands)
3235 rtx insn;
3236 rtx *operands;
3237 {
3238 int reg;
3239
3240 /* Move a register or memory to the same location.
3241 This is possible because such insn can appear
3242 in a non-optimizing mode. */
3243 if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
3244 {
3245 cc_status = cc_prev_status;
3246 return;
3247 }
3248
3249 if (TARGET_M6812)
3250 {
3251 if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
3252 {
3253 cc_status = cc_prev_status;
3254 switch (REGNO (operands[1]))
3255 {
3256 case HARD_X_REGNUM:
3257 case HARD_Y_REGNUM:
3258 case HARD_D_REGNUM:
3259 output_asm_insn ("psh%1", operands);
3260 break;
3261 case HARD_SP_REGNUM:
3262 output_asm_insn ("sts\t-2,sp", operands);
3263 break;
3264 default:
3265 abort ();
3266 }
3267 return;
3268 }
3269 if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
3270 {
3271 cc_status = cc_prev_status;
3272 switch (REGNO (operands[0]))
3273 {
3274 case HARD_X_REGNUM:
3275 case HARD_Y_REGNUM:
3276 case HARD_D_REGNUM:
3277 output_asm_insn ("pul%0", operands);
3278 break;
3279 default:
3280 abort ();
3281 }
3282 return;
3283 }
3284 if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
3285 {
3286 m68hc11_notice_keep_cc (operands[0]);
3287 output_asm_insn ("tfr\t%1,%0", operands);
3288 }
3289 else if (H_REG_P (operands[0]))
3290 {
3291 if (SP_REG_P (operands[0]))
3292 output_asm_insn ("lds\t%1", operands);
3293 else if (0 /* REG_WAS_0 note is boggus; don't rely on it. */
3294 && !D_REG_P (operands[0])
3295 && GET_CODE (operands[1]) == CONST_INT
3296 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)
3297 && find_reg_note (insn, REG_WAS_0, 0))
3298 {
3299 if (INTVAL (operands[1]) == 1)
3300 output_asm_insn ("in%0", operands);
3301 else
3302 output_asm_insn ("de%0", operands);
3303 }
3304 else
3305 output_asm_insn ("ld%0\t%1", operands);
3306 }
3307 else if (H_REG_P (operands[1]))
3308 {
3309 if (SP_REG_P (operands[1]))
3310 output_asm_insn ("sts\t%0", operands);
3311 else
3312 output_asm_insn ("st%1\t%0", operands);
3313 }
3314 else
3315 {
3316 rtx from = operands[1];
3317 rtx to = operands[0];
3318
3319 if ((m68hc11_register_indirect_p (from, GET_MODE (from))
3320 && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
3321 || (m68hc11_register_indirect_p (to, GET_MODE (to))
3322 && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
3323 {
3324 rtx ops[3];
3325
3326 if (operands[2])
3327 {
3328 ops[0] = operands[2];
3329 ops[1] = from;
3330 ops[2] = 0;
3331 m68hc11_gen_movhi (insn, ops);
3332 ops[0] = to;
3333 ops[1] = operands[2];
3334 m68hc11_gen_movhi (insn, ops);
3335 }
3336 else
3337 {
3338 /* !!!! SCz wrong here. */
3339 fatal_insn ("move insn not handled", insn);
3340 }
3341 }
3342 else
3343 {
3344 if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
3345 {
3346 output_asm_insn ("clr\t%h0", operands);
3347 output_asm_insn ("clr\t%b0", operands);
3348 }
3349 else
3350 {
3351 m68hc11_notice_keep_cc (operands[0]);
3352 output_asm_insn ("movw\t%1,%0", operands);
3353 }
3354 }
3355 }
3356 return;
3357 }
3358
3359 if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
3360 {
3361 cc_status = cc_prev_status;
3362 switch (REGNO (operands[0]))
3363 {
3364 case HARD_X_REGNUM:
3365 case HARD_Y_REGNUM:
3366 output_asm_insn ("pul%0", operands);
3367 break;
3368 case HARD_D_REGNUM:
3369 output_asm_insn ("pula", operands);
3370 output_asm_insn ("pulb", operands);
3371 break;
3372 default:
3373 abort ();
3374 }
3375 return;
3376 }
3377 /* Some moves to a hard register are special. Not all of them
3378 are really supported and we have to use a temporary
3379 location to provide them (either the stack of a temp var). */
3380 if (H_REG_P (operands[0]))
3381 {
3382 switch (REGNO (operands[0]))
3383 {
3384 case HARD_D_REGNUM:
3385 if (X_REG_P (operands[1]))
3386 {
3387 if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
3388 {
3389 m68hc11_output_swap (insn, operands);
3390 }
3391 else if (next_insn_test_reg (insn, operands[0]))
3392 {
3393 output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
3394 }
3395 else
3396 {
3397 m68hc11_notice_keep_cc (operands[0]);
3398 output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
3399 }
3400 }
3401 else if (Y_REG_P (operands[1]))
3402 {
3403 if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
3404 {
3405 m68hc11_output_swap (insn, operands);
3406 }
3407 else
3408 {
3409 /* %t means *ZTMP scratch register. */
3410 output_asm_insn ("sty\t%t1", operands);
3411 output_asm_insn ("ldd\t%t1", operands);
3412 }
3413 }
3414 else if (SP_REG_P (operands[1]))
3415 {
3416 CC_STATUS_INIT;
3417 if (ix_reg == 0)
3418 create_regs_rtx ();
3419 if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
3420 output_asm_insn ("xgdx", operands);
3421 output_asm_insn ("tsx", operands);
3422 output_asm_insn ("xgdx", operands);
3423 }
3424 else if (IS_STACK_POP (operands[1]))
3425 {
3426 output_asm_insn ("pula\n\tpulb", operands);
3427 }
3428 else if (GET_CODE (operands[1]) == CONST_INT
3429 && INTVAL (operands[1]) == 0)
3430 {
3431 output_asm_insn ("clra\n\tclrb", operands);
3432 }
3433 else
3434 {
3435 output_asm_insn ("ldd\t%1", operands);
3436 }
3437 break;
3438
3439 case HARD_X_REGNUM:
3440 if (D_REG_P (operands[1]))
3441 {
3442 if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3443 {
3444 m68hc11_output_swap (insn, operands);
3445 }
3446 else if (next_insn_test_reg (insn, operands[0]))
3447 {
3448 output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
3449 }
3450 else
3451 {
3452 m68hc11_notice_keep_cc (operands[0]);
3453 output_asm_insn ("pshb", operands);
3454 output_asm_insn ("psha", operands);
3455 output_asm_insn ("pulx", operands);
3456 }
3457 }
3458 else if (Y_REG_P (operands[1]))
3459 {
3460 /* When both D and Y are dead, use the sequence xgdy, xgdx
3461 to move Y into X. The D and Y registers are modified. */
3462 if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
3463 && dead_register_here (insn, d_reg))
3464 {
3465 output_asm_insn ("xgdy", operands);
3466 output_asm_insn ("xgdx", operands);
3467 CC_STATUS_INIT;
3468 }
3469 else if (!optimize_size)
3470 {
3471 output_asm_insn ("sty\t%t1", operands);
3472 output_asm_insn ("ldx\t%t1", operands);
3473 }
3474 else
3475 {
3476 CC_STATUS_INIT;
3477 output_asm_insn ("pshy", operands);
3478 output_asm_insn ("pulx", operands);
3479 }
3480 }
3481 else if (SP_REG_P (operands[1]))
3482 {
3483 /* tsx, tsy preserve the flags */
3484 cc_status = cc_prev_status;
3485 output_asm_insn ("tsx", operands);
3486 }
3487 else if (0 /* REG_WAS_0 note is boggus; don't rely on it. */
3488 && GET_CODE (operands[1]) == CONST_INT
3489 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)
3490 && find_reg_note (insn, REG_WAS_0, 0))
3491 {
3492 if (INTVAL (operands[1]) == 1)
3493 output_asm_insn ("in%0", operands);
3494 else
3495 output_asm_insn ("de%0", operands);
3496 }
3497 else
3498 {
3499 output_asm_insn ("ldx\t%1", operands);
3500 }
3501 break;
3502
3503 case HARD_Y_REGNUM:
3504 if (D_REG_P (operands[1]))
3505 {
3506 if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3507 {
3508 m68hc11_output_swap (insn, operands);
3509 }
3510 else
3511 {
3512 output_asm_insn ("std\t%t1", operands);
3513 output_asm_insn ("ldy\t%t1", operands);
3514 }
3515 }
3516 else if (X_REG_P (operands[1]))
3517 {
3518 /* When both D and X are dead, use the sequence xgdx, xgdy
3519 to move X into Y. The D and X registers are modified. */
3520 if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
3521 && dead_register_here (insn, d_reg))
3522 {
3523 output_asm_insn ("xgdx", operands);
3524 output_asm_insn ("xgdy", operands);
3525 CC_STATUS_INIT;
3526 }
3527 else if (!optimize_size)
3528 {
3529 output_asm_insn ("stx\t%t1", operands);
3530 output_asm_insn ("ldy\t%t1", operands);
3531 }
3532 else
3533 {
3534 CC_STATUS_INIT;
3535 output_asm_insn ("pshx", operands);
3536 output_asm_insn ("puly", operands);
3537 }
3538 }
3539 else if (SP_REG_P (operands[1]))
3540 {
3541 /* tsx, tsy preserve the flags */
3542 cc_status = cc_prev_status;
3543 output_asm_insn ("tsy", operands);
3544 }
3545 else if (0 /* REG_WAS_0 note is boggus; don't rely on it. */
3546 && GET_CODE (operands[1]) == CONST_INT
3547 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)
3548 && find_reg_note (insn, REG_WAS_0, 0))
3549 {
3550 if (INTVAL (operands[1]) == 1)
3551 output_asm_insn ("in%0", operands);
3552 else
3553 output_asm_insn ("de%0", operands);
3554 }
3555 else
3556 {
3557 output_asm_insn ("ldy\t%1", operands);
3558 }
3559 break;
3560
3561 case HARD_SP_REGNUM:
3562 if (D_REG_P (operands[1]))
3563 {
3564 m68hc11_notice_keep_cc (operands[0]);
3565 output_asm_insn ("xgdx", operands);
3566 output_asm_insn ("txs", operands);
3567 output_asm_insn ("xgdx", operands);
3568 }
3569 else if (X_REG_P (operands[1]))
3570 {
3571 /* tys, txs preserve the flags */
3572 cc_status = cc_prev_status;
3573 output_asm_insn ("txs", operands);
3574 }
3575 else if (Y_REG_P (operands[1]))
3576 {
3577 /* tys, txs preserve the flags */
3578 cc_status = cc_prev_status;
3579 output_asm_insn ("tys", operands);
3580 }
3581 else
3582 {
3583 /* lds sets the flags but the des does not. */
3584 CC_STATUS_INIT;
3585 output_asm_insn ("lds\t%1", operands);
3586 output_asm_insn ("des", operands);
3587 }
3588 break;
3589
3590 default:
3591 fatal_insn ("invalid register in the move instruction", insn);
3592 break;
3593 }
3594 return;
3595 }
3596 if (SP_REG_P (operands[1]) && REG_P (operands[0])
3597 && REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
3598 {
3599 output_asm_insn ("sts\t%0", operands);
3600 return;
3601 }
3602
3603 if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
3604 {
3605 cc_status = cc_prev_status;
3606 switch (REGNO (operands[1]))
3607 {
3608 case HARD_X_REGNUM:
3609 case HARD_Y_REGNUM:
3610 output_asm_insn ("psh%1", operands);
3611 break;
3612 case HARD_D_REGNUM:
3613 output_asm_insn ("pshb", operands);
3614 output_asm_insn ("psha", operands);
3615 break;
3616 default:
3617 abort ();
3618 }
3619 return;
3620 }
3621
3622 /* Operand 1 must be a hard register. */
3623 if (!H_REG_P (operands[1]))
3624 {
3625 fatal_insn ("invalid operand in the instruction", insn);
3626 }
3627
3628 reg = REGNO (operands[1]);
3629 switch (reg)
3630 {
3631 case HARD_D_REGNUM:
3632 output_asm_insn ("std\t%0", operands);
3633 break;
3634
3635 case HARD_X_REGNUM:
3636 output_asm_insn ("stx\t%0", operands);
3637 break;
3638
3639 case HARD_Y_REGNUM:
3640 output_asm_insn ("sty\t%0", operands);
3641 break;
3642
3643 case HARD_SP_REGNUM:
3644 if (ix_reg == 0)
3645 create_regs_rtx ();
3646
3647 if (REG_P (operands[0]) && REGNO (operands[0]) == SOFT_TMP_REGNUM)
3648 {
3649 output_asm_insn ("pshx", operands);
3650 output_asm_insn ("tsx", operands);
3651 output_asm_insn ("inx", operands);
3652 output_asm_insn ("inx", operands);
3653 output_asm_insn ("stx\t%0", operands);
3654 output_asm_insn ("pulx", operands);
3655 }
3656
3657 else if (reg_mentioned_p (ix_reg, operands[0]))
3658 {
3659 output_asm_insn ("sty\t%t0", operands);
3660 output_asm_insn ("tsy", operands);
3661 output_asm_insn ("sty\t%0", operands);
3662 output_asm_insn ("ldy\t%t0", operands);
3663 }
3664 else
3665 {
3666 output_asm_insn ("stx\t%t0", operands);
3667 output_asm_insn ("tsx", operands);
3668 output_asm_insn ("stx\t%0", operands);
3669 output_asm_insn ("ldx\t%t0", operands);
3670 }
3671 CC_STATUS_INIT;
3672 break;
3673
3674 default:
3675 fatal_insn ("invalid register in the move instruction", insn);
3676 break;
3677 }
3678 }
3679
3680 void
3681 m68hc11_gen_movqi (insn, operands)
3682 rtx insn;
3683 rtx *operands;
3684 {
3685 /* Move a register or memory to the same location.
3686 This is possible because such insn can appear
3687 in a non-optimizing mode. */
3688 if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
3689 {
3690 cc_status = cc_prev_status;
3691 return;
3692 }
3693
3694 if (TARGET_M6812)
3695 {
3696
3697 if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
3698 {
3699 m68hc11_notice_keep_cc (operands[0]);
3700 output_asm_insn ("tfr\t%1,%0", operands);
3701 }
3702 else if (H_REG_P (operands[0]))
3703 {
3704 if (Q_REG_P (operands[0]))
3705 output_asm_insn ("lda%0\t%b1", operands);
3706 else if (D_REG_P (operands[0]))
3707 output_asm_insn ("ldab\t%b1", operands);
3708 else
3709 goto m6811_move;
3710 }
3711 else if (H_REG_P (operands[1]))
3712 {
3713 if (Q_REG_P (operands[1]))
3714 output_asm_insn ("sta%1\t%b0", operands);
3715 else if (D_REG_P (operands[1]))
3716 output_asm_insn ("stab\t%b0", operands);
3717 else
3718 goto m6811_move;
3719 }
3720 else
3721 {
3722 rtx from = operands[1];
3723 rtx to = operands[0];
3724
3725 if ((m68hc11_register_indirect_p (from, GET_MODE (from))
3726 && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
3727 || (m68hc11_register_indirect_p (to, GET_MODE (to))
3728 && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
3729 {
3730 rtx ops[3];
3731
3732 if (operands[2])
3733 {
3734 ops[0] = operands[2];
3735 ops[1] = from;
3736 ops[2] = 0;
3737 m68hc11_gen_movqi (insn, ops);
3738 ops[0] = to;
3739 ops[1] = operands[2];
3740 m68hc11_gen_movqi (insn, ops);
3741 }
3742 else
3743 {
3744 /* !!!! SCz wrong here. */
3745 fatal_insn ("move insn not handled", insn);
3746 }
3747 }
3748 else
3749 {
3750 if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
3751 {
3752 output_asm_insn ("clr\t%b0", operands);
3753 }
3754 else
3755 {
3756 m68hc11_notice_keep_cc (operands[0]);
3757 output_asm_insn ("movb\t%b1,%b0", operands);
3758 }
3759 }
3760 }
3761 return;
3762 }
3763
3764 m6811_move:
3765 if (H_REG_P (operands[0]))
3766 {
3767 switch (REGNO (operands[0]))
3768 {
3769 case HARD_B_REGNUM:
3770 case HARD_D_REGNUM:
3771 if (X_REG_P (operands[1]))
3772 {
3773 if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
3774 {
3775 m68hc11_output_swap (insn, operands);
3776 }
3777 else
3778 {
3779 output_asm_insn ("stx\t%t1", operands);
3780 output_asm_insn ("ldab\t%T0", operands);
3781 }
3782 }
3783 else if (Y_REG_P (operands[1]))
3784 {
3785 if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
3786 {
3787 m68hc11_output_swap (insn, operands);
3788 }
3789 else
3790 {
3791 output_asm_insn ("sty\t%t1", operands);
3792 output_asm_insn ("ldab\t%T0", operands);
3793 }
3794 }
3795 else if (0 /* REG_WAS_0 note is boggus; don't rely on it. */
3796 && GET_CODE (operands[1]) == CONST_INT
3797 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)
3798 && find_reg_note (insn, REG_WAS_0, 0))
3799 {
3800 if (INTVAL (operands[1]) == 1)
3801 output_asm_insn ("inc%b0", operands);
3802 else
3803 output_asm_insn ("dec%b0", operands);
3804 }
3805 else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
3806 && !DA_REG_P (operands[1]))
3807 {
3808 output_asm_insn ("ldab\t%b1", operands);
3809 }
3810 else if (DA_REG_P (operands[1]))
3811 {
3812 output_asm_insn ("tab", operands);
3813 }
3814 else
3815 {
3816 cc_status = cc_prev_status;
3817 return;
3818 }
3819 break;
3820
3821 case HARD_A_REGNUM:
3822 if (X_REG_P (operands[1]))
3823 {
3824 output_asm_insn ("stx\t%t1", operands);
3825 output_asm_insn ("ldaa\t%T0", operands);
3826 }
3827 else if (Y_REG_P (operands[1]))
3828 {
3829 output_asm_insn ("sty\t%t1", operands);
3830 output_asm_insn ("ldaa\t%T0", operands);
3831 }
3832 else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
3833 && !DA_REG_P (operands[1]))
3834 {
3835 output_asm_insn ("ldaa\t%b1", operands);
3836 }
3837 else if (!DA_REG_P (operands[1]))
3838 {
3839 output_asm_insn ("tba", operands);
3840 }
3841 else
3842 {
3843 cc_status = cc_prev_status;
3844 }
3845 break;
3846
3847 case HARD_X_REGNUM:
3848 if (D_REG_P (operands[1]))
3849 {
3850 if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3851 {
3852 m68hc11_output_swap (insn, operands);
3853 }
3854 else
3855 {
3856 output_asm_insn ("stab\t%T1", operands);
3857 output_asm_insn ("ldx\t%t1", operands);
3858 }
3859 CC_STATUS_INIT;
3860 }
3861 else if (Y_REG_P (operands[1]))
3862 {
3863 output_asm_insn ("sty\t%t0", operands);
3864 output_asm_insn ("ldx\t%t0", operands);
3865 }
3866 else if (GET_CODE (operands[1]) == CONST_INT)
3867 {
3868 output_asm_insn ("ldx\t%1", operands);
3869 }
3870 else if (dead_register_here (insn, d_reg))
3871 {
3872 output_asm_insn ("ldab\t%b1", operands);
3873 output_asm_insn ("xgdx", operands);
3874 }
3875 else if (!reg_mentioned_p (operands[0], operands[1]))
3876 {
3877 output_asm_insn ("xgdx", operands);
3878 output_asm_insn ("ldab\t%b1", operands);
3879 output_asm_insn ("xgdx", operands);
3880 }
3881 else
3882 {
3883 output_asm_insn ("pshb", operands);
3884 output_asm_insn ("ldab\t%b1", operands);
3885 output_asm_insn ("stab\t%T1", operands);
3886 output_asm_insn ("ldx\t%t1", operands);
3887 output_asm_insn ("pulb", operands);
3888 CC_STATUS_INIT;
3889 }
3890 break;
3891
3892 case HARD_Y_REGNUM:
3893 if (D_REG_P (operands[1]))
3894 {
3895 output_asm_insn ("stab\t%T1", operands);
3896 output_asm_insn ("ldy\t%t1", operands);
3897 CC_STATUS_INIT;
3898 }
3899 else if (X_REG_P (operands[1]))
3900 {
3901 output_asm_insn ("stx\t%t1", operands);
3902 output_asm_insn ("ldy\t%t1", operands);
3903 CC_STATUS_INIT;
3904 }
3905 else if (GET_CODE (operands[1]) == CONST_INT)
3906 {
3907 output_asm_insn ("ldy\t%1", operands);
3908 }
3909 else if (dead_register_here (insn, d_reg))
3910 {
3911 output_asm_insn ("ldab\t%b1", operands);
3912 output_asm_insn ("xgdy", operands);
3913 }
3914 else if (!reg_mentioned_p (operands[0], operands[1]))
3915 {
3916 output_asm_insn ("xgdy", operands);
3917 output_asm_insn ("ldab\t%b1", operands);
3918 output_asm_insn ("xgdy", operands);
3919 }
3920 else
3921 {
3922 output_asm_insn ("pshb", operands);
3923 output_asm_insn ("ldab\t%b1", operands);
3924 output_asm_insn ("stab\t%T1", operands);
3925 output_asm_insn ("ldy\t%t1", operands);
3926 output_asm_insn ("pulb", operands);
3927 CC_STATUS_INIT;
3928 }
3929 break;
3930
3931 default:
3932 fatal_insn ("invalid register in the instruction", insn);
3933 break;
3934 }
3935 }
3936 else if (H_REG_P (operands[1]))
3937 {
3938 switch (REGNO (operands[1]))
3939 {
3940 case HARD_D_REGNUM:
3941 case HARD_B_REGNUM:
3942 output_asm_insn ("stab\t%b0", operands);
3943 break;
3944
3945 case HARD_A_REGNUM:
3946 output_asm_insn ("staa\t%b0", operands);
3947 break;
3948
3949 case HARD_X_REGNUM:
3950 output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
3951 break;
3952
3953 case HARD_Y_REGNUM:
3954 output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
3955 break;
3956
3957 default:
3958 fatal_insn ("invalid register in the move instruction", insn);
3959 break;
3960 }
3961 return;
3962 }
3963 else
3964 {
3965 fatal_insn ("operand 1 must be a hard register", insn);
3966 }
3967 }
3968
3969 /* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
3970 The source and destination must be D or A and the shift must
3971 be a constant. */
3972 void
3973 m68hc11_gen_rotate (code, insn, operands)
3974 enum rtx_code code;
3975 rtx insn;
3976 rtx operands[];
3977 {
3978 int val;
3979
3980 if (GET_CODE (operands[2]) != CONST_INT
3981 || (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
3982 fatal_insn ("invalid rotate insn", insn);
3983
3984 val = INTVAL (operands[2]);
3985 if (code == ROTATERT)
3986 val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
3987
3988 if (GET_MODE (operands[0]) != QImode)
3989 CC_STATUS_INIT;
3990
3991 /* Rotate by 8-bits if the shift is within [5..11]. */
3992 if (val >= 5 && val <= 11)
3993 {
3994 if (TARGET_M6812)
3995 output_asm_insn ("exg\ta,b", operands);
3996 else
3997 {
3998 output_asm_insn ("psha", operands);
3999 output_asm_insn ("tba", operands);
4000 output_asm_insn ("pulb", operands);
4001 }
4002 val -= 8;
4003 }
4004
4005 /* If the shift is big, invert the rotation. */
4006 else if (val >= 12)
4007 {
4008 val = val - 16;
4009 }
4010
4011 if (val > 0)
4012 {
4013 while (--val >= 0)
4014 {
4015 /* Set the carry to bit-15, but don't change D yet. */
4016 if (GET_MODE (operands[0]) != QImode)
4017 {
4018 output_asm_insn ("asra", operands);
4019 output_asm_insn ("rola", operands);
4020 }
4021
4022 /* Rotate B first to move the carry to bit-0. */
4023 if (D_REG_P (operands[0]))
4024 output_asm_insn ("rolb", operands);
4025
4026 if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
4027 output_asm_insn ("rola", operands);
4028 }
4029 }
4030 else
4031 {
4032 while (++val <= 0)
4033 {
4034 /* Set the carry to bit-8 of D. */
4035 if (GET_MODE (operands[0]) != QImode)
4036 output_asm_insn ("tap", operands);
4037
4038 /* Rotate B first to move the carry to bit-7. */
4039 if (D_REG_P (operands[0]))
4040 output_asm_insn ("rorb", operands);
4041
4042 if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
4043 output_asm_insn ("rora", operands);
4044 }
4045 }
4046 }
4047
4048 \f
4049
4050 /* Store in cc_status the expressions that the condition codes will
4051 describe after execution of an instruction whose pattern is EXP.
4052 Do not alter them if the instruction would not alter the cc's. */
4053
4054 void
4055 m68hc11_notice_update_cc (exp, insn)
4056 rtx exp;
4057 rtx insn ATTRIBUTE_UNUSED;
4058 {
4059 /* recognize SET insn's. */
4060 if (GET_CODE (exp) == SET)
4061 {
4062 /* Jumps do not alter the cc's. */
4063 if (SET_DEST (exp) == pc_rtx)
4064 ;
4065
4066 /* NOTE: most instructions don't affect the carry bit, but the
4067 bhi/bls/bhs/blo instructions use it. This isn't mentioned in
4068 the conditions.h header. */
4069
4070 /* Function calls clobber the cc's. */
4071 else if (GET_CODE (SET_SRC (exp)) == CALL)
4072 {
4073 CC_STATUS_INIT;
4074 }
4075
4076 /* Tests and compares set the cc's in predictable ways. */
4077 else if (SET_DEST (exp) == cc0_rtx)
4078 {
4079 cc_status.flags = 0;
4080 cc_status.value1 = XEXP (exp, 0);
4081 cc_status.value2 = XEXP (exp, 1);
4082 }
4083 else
4084 {
4085 /* All other instructions affect the condition codes. */
4086 cc_status.flags = 0;
4087 cc_status.value1 = XEXP (exp, 0);
4088 cc_status.value2 = XEXP (exp, 1);
4089 }
4090 }
4091 else
4092 {
4093 /* Default action if we haven't recognized something
4094 and returned earlier. */
4095 CC_STATUS_INIT;
4096 }
4097
4098 if (cc_status.value2 != 0)
4099 switch (GET_CODE (cc_status.value2))
4100 {
4101 /* These logical operations can generate several insns.
4102 The flags are setup according to what is generated. */
4103 case IOR:
4104 case XOR:
4105 case AND:
4106 break;
4107
4108 /* The (not ...) generates several 'com' instructions for
4109 non QImode. We have to invalidate the flags. */
4110 case NOT:
4111 if (GET_MODE (cc_status.value2) != QImode)
4112 CC_STATUS_INIT;
4113 break;
4114
4115 case PLUS:
4116 case MINUS:
4117 case MULT:
4118 case DIV:
4119 case UDIV:
4120 case MOD:
4121 case UMOD:
4122 case NEG:
4123 if (GET_MODE (cc_status.value2) != VOIDmode)
4124 cc_status.flags |= CC_NO_OVERFLOW;
4125 break;
4126
4127 /* The asl sets the overflow bit in such a way that this
4128 makes the flags unusable for a next compare insn. */
4129 case ASHIFT:
4130 case ROTATE:
4131 case ROTATERT:
4132 if (GET_MODE (cc_status.value2) != VOIDmode)
4133 cc_status.flags |= CC_NO_OVERFLOW;
4134 break;
4135
4136 /* A load/store instruction does not affect the carry. */
4137 case MEM:
4138 case SYMBOL_REF:
4139 case REG:
4140 case CONST_INT:
4141 cc_status.flags |= CC_NO_OVERFLOW;
4142 break;
4143
4144 default:
4145 break;
4146 }
4147 if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
4148 && cc_status.value2
4149 && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
4150 cc_status.value2 = 0;
4151 }
4152
4153 /* The current instruction does not affect the flags but changes
4154 the register 'reg'. See if the previous flags can be kept for the
4155 next instruction to avoid a comparison. */
4156 void
4157 m68hc11_notice_keep_cc (reg)
4158 rtx reg;
4159 {
4160 if (reg == 0
4161 || cc_prev_status.value1 == 0
4162 || rtx_equal_p (reg, cc_prev_status.value1)
4163 || (cc_prev_status.value2
4164 && reg_mentioned_p (reg, cc_prev_status.value2)))
4165 CC_STATUS_INIT;
4166 else
4167 cc_status = cc_prev_status;
4168 }
4169
4170 \f
4171
4172 /* Machine Specific Reorg. */
4173
4174 /* Z register replacement:
4175
4176 GCC treats the Z register as an index base address register like
4177 X or Y. In general, it uses it during reload to compute the address
4178 of some operand. This helps the reload pass to avoid to fall into the
4179 register spill failure.
4180
4181 The Z register is in the A_REGS class. In the machine description,
4182 the 'A' constraint matches it. The 'x' or 'y' constraints do not.
4183
4184 It can appear everywhere an X or Y register can appear, except for
4185 some templates in the clobber section (when a clobber of X or Y is asked).
4186 For a given instruction, the template must ensure that no more than
4187 2 'A' registers are used. Otherwise, the register replacement is not
4188 possible.
4189
4190 To replace the Z register, the algorithm is not terrific:
4191 1. Insns that do not use the Z register are not changed
4192 2. When a Z register is used, we scan forward the insns to see
4193 a potential register to use: either X or Y and sometimes D.
4194 We stop when a call, a label or a branch is seen, or when we
4195 detect that both X and Y are used (probably at different times, but it does
4196 not matter).
4197 3. The register that will be used for the replacement of Z is saved
4198 in a .page0 register or on the stack. If the first instruction that
4199 used Z, uses Z as an input, the value is loaded from another .page0
4200 register. The replacement register is pushed on the stack in the
4201 rare cases where a compare insn uses Z and we couldn't find if X/Y
4202 are dead.
4203 4. The Z register is replaced in all instructions until we reach
4204 the end of the Z-block, as detected by step 2.
4205 5. If we detect that Z is still alive, its value is saved.
4206 If the replacement register is alive, its old value is loaded.
4207
4208 The Z register can be disabled with -ffixed-z.
4209 */
4210
4211 struct replace_info
4212 {
4213 rtx first;
4214 rtx replace_reg;
4215 int need_save_z;
4216 int must_load_z;
4217 int must_save_reg;
4218 int must_restore_reg;
4219 rtx last;
4220 int regno;
4221 int x_used;
4222 int y_used;
4223 int can_use_d;
4224 int found_call;
4225 int z_died;
4226 int z_set_count;
4227 rtx z_value;
4228 int must_push_reg;
4229 int save_before_last;
4230 int z_loaded_with_sp;
4231 };
4232
4233 static int m68hc11_check_z_replacement PARAMS ((rtx, struct replace_info *));
4234 static void m68hc11_find_z_replacement PARAMS ((rtx, struct replace_info *));
4235 static void m68hc11_z_replacement PARAMS ((rtx));
4236 static void m68hc11_reassign_regs PARAMS ((rtx));
4237
4238 int z_replacement_completed = 0;
4239
4240 /* Analyze the insn to find out which replacement register to use and
4241 the boundaries of the replacement.
4242 Returns 0 if we reached the last insn to be replaced, 1 if we can
4243 continue replacement in next insns. */
4244
4245 static int
4246 m68hc11_check_z_replacement (insn, info)
4247 rtx insn;
4248 struct replace_info *info;
4249 {
4250 int this_insn_uses_ix;
4251 int this_insn_uses_iy;
4252 int this_insn_uses_z;
4253 int this_insn_uses_z_in_dst;
4254 int this_insn_uses_d;
4255 rtx body;
4256 int z_dies_here;
4257
4258 /* A call is said to clobber the Z register, we don't need
4259 to save the value of Z. We also don't need to restore
4260 the replacement register (unless it is used by the call). */
4261 if (GET_CODE (insn) == CALL_INSN)
4262 {
4263 body = PATTERN (insn);
4264
4265 info->can_use_d = 0;
4266
4267 /* If the call is an indirect call with Z, we have to use the
4268 Y register because X can be used as an input (D+X).
4269 We also must not save Z nor restore Y. */
4270 if (reg_mentioned_p (z_reg, body))
4271 {
4272 insn = NEXT_INSN (insn);
4273 info->x_used = 1;
4274 info->y_used = 0;
4275 info->found_call = 1;
4276 info->must_restore_reg = 0;
4277 info->last = NEXT_INSN (insn);
4278 }
4279 info->need_save_z = 0;
4280 return 0;
4281 }
4282 if (GET_CODE (insn) == CODE_LABEL
4283 || GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
4284 return 0;
4285
4286 if (GET_CODE (insn) == JUMP_INSN)
4287 {
4288 if (reg_mentioned_p (z_reg, insn) == 0)
4289 return 0;
4290
4291 info->can_use_d = 0;
4292 info->must_save_reg = 0;
4293 info->must_restore_reg = 0;
4294 info->need_save_z = 0;
4295 info->last = NEXT_INSN (insn);
4296 return 0;
4297 }
4298 if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
4299 {
4300 return 1;
4301 }
4302
4303 /* Z register dies here. */
4304 z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
4305
4306 body = PATTERN (insn);
4307 if (GET_CODE (body) == SET)
4308 {
4309 rtx src = XEXP (body, 1);
4310 rtx dst = XEXP (body, 0);
4311
4312 /* Condition code is set here. We have to restore the X/Y and
4313 save into Z before any test/compare insn because once we save/restore
4314 we can change the condition codes. When the compare insn uses Z and
4315 we can't use X/Y, the comparison is made with the *ZREG soft register
4316 (this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
4317 if (dst == cc0_rtx)
4318 {
4319 if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
4320 || (GET_CODE (src) == COMPARE &&
4321 (rtx_equal_p (XEXP (src, 0), z_reg)
4322 || rtx_equal_p (XEXP (src, 1), z_reg))))
4323 {
4324 if (insn == info->first)
4325 {
4326 info->must_load_z = 0;
4327 info->must_save_reg = 0;
4328 info->must_restore_reg = 0;
4329 info->need_save_z = 0;
4330 info->found_call = 1;
4331 info->regno = SOFT_Z_REGNUM;
4332 info->last = NEXT_INSN (insn);
4333 }
4334 return 0;
4335 }
4336 if (reg_mentioned_p (z_reg, src) == 0)
4337 {
4338 info->can_use_d = 0;
4339 return 0;
4340 }
4341
4342 if (insn != info->first)
4343 return 0;
4344
4345 /* Compare insn which uses Z. We have to save/restore the X/Y
4346 register without modifying the condition codes. For this
4347 we have to use a push/pop insn. */
4348 info->must_push_reg = 1;
4349 info->last = insn;
4350 }
4351
4352 /* Z reg is set to something new. We don't need to load it. */
4353 if (Z_REG_P (dst))
4354 {
4355 if (!reg_mentioned_p (z_reg, src))
4356 {
4357 /* Z reg is used before being set. Treat this as
4358 a new sequence of Z register replacement. */
4359 if (insn != info->first)
4360 {
4361 return 0;
4362 }
4363 info->must_load_z = 0;
4364 }
4365 info->z_set_count++;
4366 info->z_value = src;
4367 if (SP_REG_P (src))
4368 info->z_loaded_with_sp = 1;
4369 }
4370 else if (reg_mentioned_p (z_reg, dst))
4371 info->can_use_d = 0;
4372
4373 this_insn_uses_d = reg_mentioned_p (d_reg, src)
4374 | reg_mentioned_p (d_reg, dst);
4375 this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
4376 | reg_mentioned_p (ix_reg, dst);
4377 this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
4378 | reg_mentioned_p (iy_reg, dst);
4379 this_insn_uses_z = reg_mentioned_p (z_reg, src);
4380
4381 /* If z is used as an address operand (like (MEM (reg z))),
4382 we can't replace it with d. */
4383 if (this_insn_uses_z && !Z_REG_P (src)
4384 && !(m68hc11_arith_operator (src, GET_MODE (src))
4385 && Z_REG_P (XEXP (src, 0))
4386 && !reg_mentioned_p (z_reg, XEXP (src, 1))
4387 && insn == info->first
4388 && dead_register_here (insn, d_reg)))
4389 info->can_use_d = 0;
4390
4391 this_insn_uses_z_in_dst = reg_mentioned_p (z_reg, dst);
4392 if (TARGET_M6812 && !z_dies_here
4393 && ((this_insn_uses_z && side_effects_p (src))
4394 || (this_insn_uses_z_in_dst && side_effects_p (dst))))
4395 {
4396 info->need_save_z = 1;
4397 info->z_set_count++;
4398 }
4399 this_insn_uses_z |= this_insn_uses_z_in_dst;
4400
4401 if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
4402 {
4403 fatal_insn ("registers IX, IY and Z used in the same INSN", insn);
4404 }
4405
4406 if (this_insn_uses_d)
4407 info->can_use_d = 0;
4408
4409 /* IX and IY are used at the same time, we have to restore
4410 the value of the scratch register before this insn. */
4411 if (this_insn_uses_ix && this_insn_uses_iy)
4412 {
4413 return 0;
4414 }
4415
4416 if (this_insn_uses_ix && X_REG_P (dst) && GET_MODE (dst) == SImode)
4417 info->can_use_d = 0;
4418
4419 if (info->x_used == 0 && this_insn_uses_ix)
4420 {
4421 if (info->y_used)
4422 {
4423 /* We have a (set (REG:HI X) (REG:HI Z)).
4424 Since we use Z as the replacement register, this insn
4425 is no longer necessary. We turn it into a note. We must
4426 not reload the old value of X. */
4427 if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
4428 {
4429 if (z_dies_here)
4430 {
4431 info->need_save_z = 0;
4432 info->z_died = 1;
4433 }
4434 info->must_save_reg = 0;
4435 info->must_restore_reg = 0;
4436 info->found_call = 1;
4437 info->can_use_d = 0;
4438 PUT_CODE (insn, NOTE);
4439 NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
4440 NOTE_SOURCE_FILE (insn) = 0;
4441 info->last = NEXT_INSN (insn);
4442 return 0;
4443 }
4444
4445 if (X_REG_P (dst)
4446 && (rtx_equal_p (src, z_reg)
4447 || (z_dies_here && !reg_mentioned_p (ix_reg, src))))
4448 {
4449 if (z_dies_here)
4450 {
4451 info->need_save_z = 0;
4452 info->z_died = 1;
4453 }
4454 info->last = NEXT_INSN (insn);
4455 info->must_save_reg = 0;
4456 info->must_restore_reg = 0;
4457 }
4458 else if (X_REG_P (dst) && reg_mentioned_p (z_reg, src)
4459 && !reg_mentioned_p (ix_reg, src))
4460 {
4461 if (z_dies_here)
4462 {
4463 info->z_died = 1;
4464 info->need_save_z = 0;
4465 }
4466 else if (TARGET_M6812 && side_effects_p (src))
4467 {
4468 info->last = 0;
4469 info->must_restore_reg = 0;
4470 return 0;
4471 }
4472 else
4473 {
4474 info->save_before_last = 1;
4475 }
4476 info->must_restore_reg = 0;
4477 info->last = NEXT_INSN (insn);
4478 }
4479 else if (info->can_use_d)
4480 {
4481 info->last = NEXT_INSN (insn);
4482 info->x_used = 1;
4483 }
4484 return 0;
4485 }
4486 info->x_used = 1;
4487 if (z_dies_here && !reg_mentioned_p (ix_reg, src)
4488 && GET_CODE (dst) == REG && REGNO (dst) == HARD_X_REGNUM)
4489 {
4490 info->need_save_z = 0;
4491 info->z_died = 1;
4492 info->last = NEXT_INSN (insn);
4493 info->regno = HARD_X_REGNUM;
4494 info->must_save_reg = 0;
4495 info->must_restore_reg = 0;
4496 return 0;
4497 }
4498 if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, ix_reg))
4499 {
4500 info->regno = HARD_X_REGNUM;
4501 info->must_restore_reg = 0;
4502 info->must_save_reg = 0;
4503 return 0;
4504 }
4505 }
4506 if (info->y_used == 0 && this_insn_uses_iy)
4507 {
4508 if (info->x_used)
4509 {
4510 if (Y_REG_P (dst) && rtx_equal_p (src, z_reg))
4511 {
4512 if (z_dies_here)
4513 {
4514 info->need_save_z = 0;
4515 info->z_died = 1;
4516 }
4517 info->must_save_reg = 0;
4518 info->must_restore_reg = 0;
4519 info->found_call = 1;
4520 info->can_use_d = 0;
4521 PUT_CODE (insn, NOTE);
4522 NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
4523 NOTE_SOURCE_FILE (insn) = 0;
4524 info->last = NEXT_INSN (insn);
4525 return 0;
4526 }
4527
4528 if (Y_REG_P (dst)
4529 && (rtx_equal_p (src, z_reg)
4530 || (z_dies_here && !reg_mentioned_p (iy_reg, src))))
4531 {
4532 if (z_dies_here)
4533 {
4534 info->z_died = 1;
4535 info->need_save_z = 0;
4536 }
4537 info->last = NEXT_INSN (insn);
4538 info->must_save_reg = 0;
4539 info->must_restore_reg = 0;
4540 }
4541 else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
4542 && !reg_mentioned_p (iy_reg, src))
4543 {
4544 if (z_dies_here)
4545 {
4546 info->z_died = 1;
4547 info->need_save_z = 0;
4548 }
4549 else if (TARGET_M6812 && side_effects_p (src))
4550 {
4551 info->last = 0;
4552 info->must_restore_reg = 0;
4553 return 0;
4554 }
4555 else
4556 {
4557 info->save_before_last = 1;
4558 }
4559 info->must_restore_reg = 0;
4560 info->last = NEXT_INSN (insn);
4561 }
4562 else if (info->can_use_d)
4563 {
4564 info->last = NEXT_INSN (insn);
4565 info->y_used = 1;
4566 }
4567
4568 return 0;
4569 }
4570 info->y_used = 1;
4571 if (z_dies_here && !reg_mentioned_p (iy_reg, src)
4572 && GET_CODE (dst) == REG && REGNO (dst) == HARD_Y_REGNUM)
4573 {
4574 info->need_save_z = 0;
4575 info->z_died = 1;
4576 info->last = NEXT_INSN (insn);
4577 info->regno = HARD_Y_REGNUM;
4578 info->must_save_reg = 0;
4579 info->must_restore_reg = 0;
4580 return 0;
4581 }
4582 if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, iy_reg))
4583 {
4584 info->regno = HARD_Y_REGNUM;
4585 info->must_restore_reg = 0;
4586 info->must_save_reg = 0;
4587 return 0;
4588 }
4589 }
4590 if (z_dies_here)
4591 {
4592 info->need_save_z = 0;
4593 info->z_died = 1;
4594 if (info->last == 0)
4595 info->last = NEXT_INSN (insn);
4596 return 0;
4597 }
4598 return info->last != NULL_RTX ? 0 : 1;
4599 }
4600 if (GET_CODE (body) == PARALLEL)
4601 {
4602 int i;
4603 char ix_clobber = 0;
4604 char iy_clobber = 0;
4605 char z_clobber = 0;
4606 this_insn_uses_iy = 0;
4607 this_insn_uses_ix = 0;
4608 this_insn_uses_z = 0;
4609
4610 for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
4611 {
4612 rtx x;
4613 int uses_ix, uses_iy, uses_z;
4614
4615 x = XVECEXP (body, 0, i);
4616
4617 if (info->can_use_d && reg_mentioned_p (d_reg, x))
4618 info->can_use_d = 0;
4619
4620 uses_ix = reg_mentioned_p (ix_reg, x);
4621 uses_iy = reg_mentioned_p (iy_reg, x);
4622 uses_z = reg_mentioned_p (z_reg, x);
4623 if (GET_CODE (x) == CLOBBER)
4624 {
4625 ix_clobber |= uses_ix;
4626 iy_clobber |= uses_iy;
4627 z_clobber |= uses_z;
4628 }
4629 else
4630 {
4631 this_insn_uses_ix |= uses_ix;
4632 this_insn_uses_iy |= uses_iy;
4633 this_insn_uses_z |= uses_z;
4634 }
4635 if (uses_z && GET_CODE (x) == SET)
4636 {
4637 rtx dst = XEXP (x, 0);
4638
4639 if (Z_REG_P (dst))
4640 info->z_set_count++;
4641 }
4642 if (TARGET_M6812 && uses_z && side_effects_p (x))
4643 info->need_save_z = 1;
4644
4645 if (z_clobber)
4646 info->need_save_z = 0;
4647 }
4648 if (debug_m6811)
4649 {
4650 printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
4651 this_insn_uses_ix, this_insn_uses_iy,
4652 this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
4653 debug_rtx (insn);
4654 }
4655 if (this_insn_uses_z)
4656 info->can_use_d = 0;
4657
4658 if (z_clobber && info->first != insn)
4659 {
4660 info->need_save_z = 0;
4661 info->last = insn;
4662 return 0;
4663 }
4664 if (z_clobber && info->x_used == 0 && info->y_used == 0)
4665 {
4666 if (this_insn_uses_z == 0 && insn == info->first)
4667 {
4668 info->must_load_z = 0;
4669 }
4670 if (dead_register_here (insn, d_reg))
4671 {
4672 info->regno = HARD_D_REGNUM;
4673 info->must_save_reg = 0;
4674 info->must_restore_reg = 0;
4675 }
4676 else if (dead_register_here (insn, ix_reg))
4677 {
4678 info->regno = HARD_X_REGNUM;
4679 info->must_save_reg = 0;
4680 info->must_restore_reg = 0;
4681 }
4682 else if (dead_register_here (insn, iy_reg))
4683 {
4684 info->regno = HARD_Y_REGNUM;
4685 info->must_save_reg = 0;
4686 info->must_restore_reg = 0;
4687 }
4688 if (info->regno >= 0)
4689 {
4690 info->last = NEXT_INSN (insn);
4691 return 0;
4692 }
4693 if (this_insn_uses_ix == 0)
4694 {
4695 info->regno = HARD_X_REGNUM;
4696 info->must_save_reg = 1;
4697 info->must_restore_reg = 1;
4698 }
4699 else if (this_insn_uses_iy == 0)
4700 {
4701 info->regno = HARD_Y_REGNUM;
4702 info->must_save_reg = 1;
4703 info->must_restore_reg = 1;
4704 }
4705 else
4706 {
4707 info->regno = HARD_D_REGNUM;
4708 info->must_save_reg = 1;
4709 info->must_restore_reg = 1;
4710 }
4711 info->last = NEXT_INSN (insn);
4712 return 0;
4713 }
4714
4715 if (((info->x_used || this_insn_uses_ix) && iy_clobber)
4716 || ((info->y_used || this_insn_uses_iy) && ix_clobber))
4717 {
4718 if (this_insn_uses_z)
4719 {
4720 if (info->y_used == 0 && iy_clobber)
4721 {
4722 info->regno = HARD_Y_REGNUM;
4723 info->must_save_reg = 0;
4724 info->must_restore_reg = 0;
4725 }
4726 if (info->first != insn
4727 && ((info->y_used && ix_clobber)
4728 || (info->x_used && iy_clobber)))
4729 info->last = insn;
4730 else
4731 info->last = NEXT_INSN (insn);
4732 info->save_before_last = 1;
4733 }
4734 return 0;
4735 }
4736 if (this_insn_uses_ix && this_insn_uses_iy)
4737 {
4738 if (this_insn_uses_z)
4739 {
4740 fatal_insn ("cannot do z-register replacement", insn);
4741 }
4742 return 0;
4743 }
4744 if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
4745 {
4746 if (info->y_used)
4747 {
4748 return 0;
4749 }
4750 info->x_used = 1;
4751 if (iy_clobber || z_clobber)
4752 {
4753 info->last = NEXT_INSN (insn);
4754 info->save_before_last = 1;
4755 return 0;
4756 }
4757 }
4758
4759 if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
4760 {
4761 if (info->x_used)
4762 {
4763 return 0;
4764 }
4765 info->y_used = 1;
4766 if (ix_clobber || z_clobber)
4767 {
4768 info->last = NEXT_INSN (insn);
4769 info->save_before_last = 1;
4770 return 0;
4771 }
4772 }
4773 if (z_dies_here)
4774 {
4775 info->z_died = 1;
4776 info->need_save_z = 0;
4777 }
4778 return 1;
4779 }
4780 if (GET_CODE (body) == CLOBBER)
4781 {
4782
4783 /* IX and IY are used at the same time, we have to restore
4784 the value of the scratch register before this insn. */
4785 if (this_insn_uses_ix && this_insn_uses_iy)
4786 {
4787 return 0;
4788 }
4789 if (info->x_used == 0 && this_insn_uses_ix)
4790 {
4791 if (info->y_used)
4792 {
4793 return 0;
4794 }
4795 info->x_used = 1;
4796 }
4797 if (info->y_used == 0 && this_insn_uses_iy)
4798 {
4799 if (info->x_used)
4800 {
4801 return 0;
4802 }
4803 info->y_used = 1;
4804 }
4805 return 1;
4806 }
4807 return 1;
4808 }
4809
4810 static void
4811 m68hc11_find_z_replacement (insn, info)
4812 rtx insn;
4813 struct replace_info *info;
4814 {
4815 int reg;
4816
4817 info->replace_reg = NULL_RTX;
4818 info->must_load_z = 1;
4819 info->need_save_z = 1;
4820 info->must_save_reg = 1;
4821 info->must_restore_reg = 1;
4822 info->first = insn;
4823 info->x_used = 0;
4824 info->y_used = 0;
4825 info->can_use_d = TARGET_M6811 ? 1 : 0;
4826 info->found_call = 0;
4827 info->z_died = 0;
4828 info->last = 0;
4829 info->regno = -1;
4830 info->z_set_count = 0;
4831 info->z_value = NULL_RTX;
4832 info->must_push_reg = 0;
4833 info->save_before_last = 0;
4834 info->z_loaded_with_sp = 0;
4835
4836 /* Scan the insn forward to find an address register that is not used.
4837 Stop when:
4838 - the flow of the program changes,
4839 - when we detect that both X and Y are necessary,
4840 - when the Z register dies,
4841 - when the condition codes are set. */
4842
4843 for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
4844 {
4845 if (m68hc11_check_z_replacement (insn, info) == 0)
4846 break;
4847 }
4848
4849 /* May be we can use Y or X if they contain the same value as Z.
4850 This happens very often after the reload. */
4851 if (info->z_set_count == 1)
4852 {
4853 rtx p = info->first;
4854 rtx v = 0;
4855
4856 if (info->x_used)
4857 {
4858 v = find_last_value (iy_reg, &p, insn, 1);
4859 }
4860 else if (info->y_used)
4861 {
4862 v = find_last_value (ix_reg, &p, insn, 1);
4863 }
4864 if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
4865 {
4866 if (info->x_used)
4867 info->regno = HARD_Y_REGNUM;
4868 else
4869 info->regno = HARD_X_REGNUM;
4870 info->must_load_z = 0;
4871 info->must_save_reg = 0;
4872 info->must_restore_reg = 0;
4873 info->found_call = 1;
4874 }
4875 }
4876 if (info->z_set_count == 0)
4877 info->need_save_z = 0;
4878
4879 if (insn == 0)
4880 info->need_save_z = 0;
4881
4882 if (info->last == 0)
4883 info->last = insn;
4884
4885 if (info->regno >= 0)
4886 {
4887 reg = info->regno;
4888 info->replace_reg = gen_rtx (REG, HImode, reg);
4889 }
4890 else if (info->can_use_d)
4891 {
4892 reg = HARD_D_REGNUM;
4893 info->replace_reg = d_reg;
4894 }
4895 else if (info->x_used)
4896 {
4897 reg = HARD_Y_REGNUM;
4898 info->replace_reg = iy_reg;
4899 }
4900 else
4901 {
4902 reg = HARD_X_REGNUM;
4903 info->replace_reg = ix_reg;
4904 }
4905 info->regno = reg;
4906
4907 if (info->must_save_reg && info->must_restore_reg)
4908 {
4909 if (insn && dead_register_here (insn, info->replace_reg))
4910 {
4911 info->must_save_reg = 0;
4912 info->must_restore_reg = 0;
4913 }
4914 }
4915 }
4916
4917 /* The insn uses the Z register. Find a replacement register for it
4918 (either X or Y) and replace it in the insn and the next ones until
4919 the flow changes or the replacement register is used. Instructions
4920 are emited before and after the Z-block to preserve the value of
4921 Z and of the replacement register. */
4922
4923 static void
4924 m68hc11_z_replacement (insn)
4925 rtx insn;
4926 {
4927 rtx replace_reg_qi;
4928 rtx replace_reg;
4929 struct replace_info info;
4930
4931 /* Find trivial case where we only need to replace z with the
4932 equivalent soft register. */
4933 if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
4934 {
4935 rtx body = PATTERN (insn);
4936 rtx src = XEXP (body, 1);
4937 rtx dst = XEXP (body, 0);
4938
4939 if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
4940 {
4941 XEXP (body, 0) = gen_rtx (REG, GET_MODE (dst), SOFT_Z_REGNUM);
4942 return;
4943 }
4944 else if (Z_REG_P (src)
4945 && ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
4946 {
4947 XEXP (body, 1) = gen_rtx (REG, GET_MODE (src), SOFT_Z_REGNUM);
4948 return;
4949 }
4950 else if (D_REG_P (dst)
4951 && m68hc11_arith_operator (src, GET_MODE (src))
4952 && D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
4953 {
4954 XEXP (src, 1) = gen_rtx (REG, GET_MODE (src), SOFT_Z_REGNUM);
4955 return;
4956 }
4957 else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
4958 && INTVAL (src) == 0)
4959 {
4960 XEXP (body, 0) = gen_rtx (REG, GET_MODE (dst), SOFT_Z_REGNUM);
4961 /* Force it to be re-recognized. */
4962 INSN_CODE (insn) = -1;
4963 return;
4964 }
4965 }
4966
4967 m68hc11_find_z_replacement (insn, &info);
4968
4969 replace_reg = info.replace_reg;
4970 replace_reg_qi = NULL_RTX;
4971
4972 /* Save the X register in a .page0 location. */
4973 if (info.must_save_reg && !info.must_push_reg)
4974 {
4975 rtx dst;
4976
4977 if (info.must_push_reg && 0)
4978 dst = gen_rtx (MEM, HImode,
4979 gen_rtx (PRE_DEC, HImode,
4980 gen_rtx (REG, HImode, HARD_SP_REGNUM)));
4981 else
4982 dst = gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM);
4983
4984 emit_insn_before (gen_movhi (dst,
4985 gen_rtx (REG, HImode, info.regno)), insn);
4986 }
4987 if (info.must_load_z && !info.must_push_reg)
4988 {
4989 emit_insn_before (gen_movhi (gen_rtx (REG, HImode, info.regno),
4990 gen_rtx (REG, HImode, SOFT_Z_REGNUM)),
4991 insn);
4992 }
4993
4994
4995 /* Replace all occurrence of Z by replace_reg.
4996 Stop when the last instruction to replace is reached.
4997 Also stop when we detect a change in the flow (but it's not
4998 necessary; just safeguard). */
4999
5000 for (; insn && insn != info.last; insn = NEXT_INSN (insn))
5001 {
5002 rtx body;
5003
5004 if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
5005 break;
5006
5007 if (GET_CODE (insn) != INSN
5008 && GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
5009 continue;
5010
5011 body = PATTERN (insn);
5012 if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
5013 || GET_CODE (body) == ASM_OPERANDS
5014 || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
5015 {
5016 rtx note;
5017
5018 if (debug_m6811 && reg_mentioned_p (replace_reg, body))
5019 {
5020 printf ("Reg mentioned here...:\n");
5021 fflush (stdout);
5022 debug_rtx (insn);
5023 }
5024
5025 /* Stack pointer was decremented by 2 due to the push.
5026 Correct that by adding 2 to the destination. */
5027 if (info.must_push_reg
5028 && info.z_loaded_with_sp && GET_CODE (body) == SET)
5029 {
5030 rtx src, dst;
5031
5032 src = SET_SRC (body);
5033 dst = SET_DEST (body);
5034 if (SP_REG_P (src) && Z_REG_P (dst))
5035 emit_insn_after (gen_addhi3 (dst, dst, const2_rtx), insn);
5036 }
5037
5038 /* Replace any (REG:HI Z) occurrence by either X or Y. */
5039 if (!validate_replace_rtx (z_reg, replace_reg, insn))
5040 {
5041 INSN_CODE (insn) = -1;
5042 if (!validate_replace_rtx (z_reg, replace_reg, insn))
5043 fatal_insn ("cannot do z-register replacement", insn);
5044 }
5045
5046 /* Likewise for (REG:QI Z). */
5047 if (reg_mentioned_p (z_reg, insn))
5048 {
5049 if (replace_reg_qi == NULL_RTX)
5050 replace_reg_qi = gen_rtx (REG, QImode, REGNO (replace_reg));
5051 validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
5052 }
5053
5054 /* If there is a REG_INC note on Z, replace it with a
5055 REG_INC note on the replacement register. This is necessary
5056 to make sure that the flow pass will identify the change
5057 and it will not remove a possible insn that saves Z. */
5058 for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
5059 {
5060 if (REG_NOTE_KIND (note) == REG_INC
5061 && GET_CODE (XEXP (note, 0)) == REG
5062 && REGNO (XEXP (note, 0)) == REGNO (z_reg))
5063 {
5064 XEXP (note, 0) = replace_reg;
5065 }
5066 }
5067 }
5068 if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
5069 break;
5070 }
5071
5072 /* Save Z before restoring the old value. */
5073 if (insn && info.need_save_z && !info.must_push_reg)
5074 {
5075 rtx save_pos_insn = insn;
5076
5077 /* If Z is clobber by the last insn, we have to save its value
5078 before the last instruction. */
5079 if (info.save_before_last)
5080 save_pos_insn = PREV_INSN (save_pos_insn);
5081
5082 emit_insn_before (gen_movhi (gen_rtx (REG, HImode, SOFT_Z_REGNUM),
5083 gen_rtx (REG, HImode, info.regno)),
5084 save_pos_insn);
5085 }
5086
5087 if (info.must_push_reg && info.last)
5088 {
5089 rtx new_body, body;
5090
5091 body = PATTERN (info.last);
5092 new_body = gen_rtx (PARALLEL, VOIDmode,
5093 gen_rtvec (3, body,
5094 gen_rtx (USE, VOIDmode,
5095 replace_reg),
5096 gen_rtx (USE, VOIDmode,
5097 gen_rtx (REG, HImode,
5098 SOFT_Z_REGNUM))));
5099 PATTERN (info.last) = new_body;
5100
5101 /* Force recognition on insn since we changed it. */
5102 INSN_CODE (insn) = -1;
5103
5104 if (!validate_replace_rtx (z_reg, replace_reg, info.last))
5105 {
5106 fatal_insn ("invalid Z register replacement for insn", insn);
5107 }
5108 insn = NEXT_INSN (info.last);
5109 }
5110
5111 /* Restore replacement register unless it was died. */
5112 if (insn && info.must_restore_reg && !info.must_push_reg)
5113 {
5114 rtx dst;
5115
5116 if (info.must_push_reg && 0)
5117 dst = gen_rtx (MEM, HImode,
5118 gen_rtx (POST_INC, HImode,
5119 gen_rtx (REG, HImode, HARD_SP_REGNUM)));
5120 else
5121 dst = gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM);
5122
5123 emit_insn_before (gen_movhi (gen_rtx (REG, HImode, info.regno),
5124 dst), insn);
5125 }
5126
5127 }
5128
5129
5130 /* Scan all the insn and re-affects some registers
5131 - The Z register (if it was used), is affected to X or Y depending
5132 on the instruction. */
5133
5134 static void
5135 m68hc11_reassign_regs (first)
5136 rtx first;
5137 {
5138 rtx insn;
5139
5140 ix_reg = gen_rtx (REG, HImode, HARD_X_REGNUM);
5141 iy_reg = gen_rtx (REG, HImode, HARD_Y_REGNUM);
5142 z_reg = gen_rtx (REG, HImode, HARD_Z_REGNUM);
5143 z_reg_qi = gen_rtx (REG, QImode, HARD_Z_REGNUM);
5144
5145 /* Scan all insns to replace Z by X or Y preserving the old value
5146 of X/Y and restoring it afterward. */
5147
5148 for (insn = first; insn; insn = NEXT_INSN (insn))
5149 {
5150 rtx body;
5151
5152 if (GET_CODE (insn) == CODE_LABEL
5153 || GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
5154 continue;
5155
5156 if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
5157 continue;
5158
5159 body = PATTERN (insn);
5160 if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
5161 continue;
5162
5163 if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
5164 || GET_CODE (body) == ASM_OPERANDS
5165 || GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
5166 continue;
5167
5168 if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
5169 || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
5170 {
5171
5172 /* If Z appears in this insn, replace it in the current insn
5173 and the next ones until the flow changes or we have to
5174 restore back the replacement register. */
5175
5176 if (reg_mentioned_p (z_reg, body))
5177 {
5178 m68hc11_z_replacement (insn);
5179 }
5180 }
5181 else
5182 {
5183 printf ("insn not handled by Z replacement:\n");
5184 fflush (stdout);
5185 debug_rtx (insn);
5186 }
5187 }
5188 }
5189
5190
5191 /* Machine-dependent reorg pass.
5192 Specific optimizations are defined here:
5193 - this pass changes the Z register into either X or Y
5194 (it preserves X/Y previous values in a memory slot in page0).
5195
5196 When this pass is finished, the global variable
5197 'z_replacement_completed' is set to 2. */
5198
5199 static void
5200 m68hc11_reorg ()
5201 {
5202 int split_done = 0;
5203 rtx insn, first;
5204
5205 z_replacement_completed = 0;
5206 z_reg = gen_rtx (REG, HImode, HARD_Z_REGNUM);
5207 first = get_insns ();
5208
5209 /* Some RTX are shared at this point. This breaks the Z register
5210 replacement, unshare everything. */
5211 unshare_all_rtl_again (first);
5212
5213 /* Force a split of all splitable insn. This is necessary for the
5214 Z register replacement mechanism because we end up with basic insns. */
5215 split_all_insns_noflow ();
5216 split_done = 1;
5217
5218 z_replacement_completed = 1;
5219 m68hc11_reassign_regs (first);
5220
5221 if (optimize)
5222 compute_bb_for_insn ();
5223
5224 /* After some splitting, there are some oportunities for CSE pass.
5225 This happens quite often when 32-bit or above patterns are split. */
5226 if (optimize > 0 && split_done)
5227 {
5228 reload_cse_regs (first);
5229 }
5230
5231 /* Re-create the REG_DEAD notes. These notes are used in the machine
5232 description to use the best assembly directives. */
5233 if (optimize)
5234 {
5235 /* Before recomputing the REG_DEAD notes, remove all of them.
5236 This is necessary because the reload_cse_regs() pass can
5237 have replaced some (MEM) with a register. In that case,
5238 the REG_DEAD that could exist for that register may become
5239 wrong. */
5240 for (insn = first; insn; insn = NEXT_INSN (insn))
5241 {
5242 if (INSN_P (insn))
5243 {
5244 rtx *pnote;
5245
5246 pnote = &REG_NOTES (insn);
5247 while (*pnote != 0)
5248 {
5249 if (REG_NOTE_KIND (*pnote) == REG_DEAD)
5250 *pnote = XEXP (*pnote, 1);
5251 else
5252 pnote = &XEXP (*pnote, 1);
5253 }
5254 }
5255 }
5256
5257 life_analysis (first, 0, PROP_REG_INFO | PROP_DEATH_NOTES);
5258 }
5259
5260 z_replacement_completed = 2;
5261
5262 /* If optimizing, then go ahead and split insns that must be
5263 split after Z register replacement. This gives more opportunities
5264 for peephole (in particular for consecutives xgdx/xgdy). */
5265 if (optimize > 0)
5266 split_all_insns_noflow ();
5267
5268 /* Once insns are split after the z_replacement_completed == 2,
5269 we must not re-run the life_analysis. The xgdx/xgdy patterns
5270 are not recognized and the life_analysis pass removes some
5271 insns because it thinks some (SETs) are noops or made to dead
5272 stores (which is false due to the swap).
5273
5274 Do a simple pass to eliminate the noop set that the final
5275 split could generate (because it was easier for split definition). */
5276 {
5277 rtx insn;
5278
5279 for (insn = first; insn; insn = NEXT_INSN (insn))
5280 {
5281 rtx body;
5282
5283 if (INSN_DELETED_P (insn))
5284 continue;
5285 if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
5286 continue;
5287
5288 /* Remove the (set (R) (R)) insns generated by some splits. */
5289 body = PATTERN (insn);
5290 if (GET_CODE (body) == SET
5291 && rtx_equal_p (SET_SRC (body), SET_DEST (body)))
5292 {
5293 PUT_CODE (insn, NOTE);
5294 NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
5295 NOTE_SOURCE_FILE (insn) = 0;
5296 continue;
5297 }
5298 }
5299 }
5300 }
5301 \f
5302
5303 /* Cost functions. */
5304
5305 /* Cost of moving memory. */
5306 int
5307 m68hc11_memory_move_cost (mode, class, in)
5308 enum machine_mode mode;
5309 enum reg_class class;
5310 int in ATTRIBUTE_UNUSED;
5311 {
5312 if (class <= H_REGS && class > NO_REGS)
5313 {
5314 if (GET_MODE_SIZE (mode) <= 2)
5315 return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
5316 else
5317 return COSTS_N_INSNS (2) + (reload_completed | reload_in_progress);
5318 }
5319 else
5320 {
5321 if (GET_MODE_SIZE (mode) <= 2)
5322 return COSTS_N_INSNS (3);
5323 else
5324 return COSTS_N_INSNS (4);
5325 }
5326 }
5327
5328
5329 /* Cost of moving data from a register of class 'from' to on in class 'to'.
5330 Reload does not check the constraint of set insns when the two registers
5331 have a move cost of 2. Setting a higher cost will force reload to check
5332 the constraints. */
5333 int
5334 m68hc11_register_move_cost (mode, from, to)
5335 enum machine_mode mode;
5336 enum reg_class from;
5337 enum reg_class to;
5338 {
5339 /* All costs are symmetric, so reduce cases by putting the
5340 lower number class as the destination. */
5341 if (from < to)
5342 {
5343 enum reg_class tmp = to;
5344 to = from, from = tmp;
5345 }
5346 if (to >= S_REGS)
5347 return m68hc11_memory_move_cost (mode, S_REGS, 0);
5348 else if (from <= S_REGS)
5349 return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
5350 else
5351 return COSTS_N_INSNS (2);
5352 }
5353
5354
5355 /* Provide the costs of an addressing mode that contains ADDR.
5356 If ADDR is not a valid address, its cost is irrelevant. */
5357
5358 static int
5359 m68hc11_address_cost (addr)
5360 rtx addr;
5361 {
5362 int cost = 4;
5363
5364 switch (GET_CODE (addr))
5365 {
5366 case REG:
5367 /* Make the cost of hard registers and specially SP, FP small. */
5368 if (REGNO (addr) < FIRST_PSEUDO_REGISTER)
5369 cost = 0;
5370 else
5371 cost = 1;
5372 break;
5373
5374 case SYMBOL_REF:
5375 cost = 8;
5376 break;
5377
5378 case LABEL_REF:
5379 case CONST:
5380 cost = 0;
5381 break;
5382
5383 case PLUS:
5384 {
5385 register rtx plus0 = XEXP (addr, 0);
5386 register rtx plus1 = XEXP (addr, 1);
5387
5388 if (GET_CODE (plus0) != REG)
5389 break;
5390
5391 switch (GET_CODE (plus1))
5392 {
5393 case CONST_INT:
5394 if (INTVAL (plus1) >= 2 * m68hc11_max_offset
5395 || INTVAL (plus1) < m68hc11_min_offset)
5396 cost = 3;
5397 else if (INTVAL (plus1) >= m68hc11_max_offset)
5398 cost = 2;
5399 else
5400 cost = 1;
5401 if (REGNO (plus0) < FIRST_PSEUDO_REGISTER)
5402 cost += 0;
5403 else
5404 cost += 1;
5405 break;
5406
5407 case SYMBOL_REF:
5408 cost = 8;
5409 break;
5410
5411 case CONST:
5412 case LABEL_REF:
5413 cost = 0;
5414 break;
5415
5416 default:
5417 break;
5418 }
5419 break;
5420 }
5421 case PRE_DEC:
5422 case PRE_INC:
5423 if (SP_REG_P (XEXP (addr, 0)))
5424 cost = 1;
5425 break;
5426
5427 default:
5428 break;
5429 }
5430 if (debug_m6811)
5431 {
5432 printf ("Address cost: %d for :", cost);
5433 fflush (stdout);
5434 debug_rtx (addr);
5435 }
5436
5437 return cost;
5438 }
5439
5440 static int
5441 m68hc11_shift_cost (mode, x, shift)
5442 enum machine_mode mode;
5443 rtx x;
5444 int shift;
5445 {
5446 int total;
5447
5448 total = rtx_cost (x, SET);
5449 if (mode == QImode)
5450 total += m68hc11_cost->shiftQI_const[shift % 8];
5451 else if (mode == HImode)
5452 total += m68hc11_cost->shiftHI_const[shift % 16];
5453 else if (shift == 8 || shift == 16 || shift == 32)
5454 total += m68hc11_cost->shiftHI_const[8];
5455 else if (shift != 0 && shift != 16 && shift != 32)
5456 {
5457 total += m68hc11_cost->shiftHI_const[1] * shift;
5458 }
5459
5460 /* For SI and others, the cost is higher. */
5461 if (GET_MODE_SIZE (mode) > 2 && (shift % 16) != 0)
5462 total *= GET_MODE_SIZE (mode) / 2;
5463
5464 /* When optimizing for size, make shift more costly so that
5465 multiplications are preferred. */
5466 if (optimize_size && (shift % 8) != 0)
5467 total *= 2;
5468
5469 return total;
5470 }
5471
5472 static int
5473 m68hc11_rtx_costs_1 (x, code, outer_code)
5474 rtx x;
5475 enum rtx_code code;
5476 enum rtx_code outer_code ATTRIBUTE_UNUSED;
5477 {
5478 enum machine_mode mode = GET_MODE (x);
5479 int extra_cost = 0;
5480 int total;
5481
5482 switch (code)
5483 {
5484 case ROTATE:
5485 case ROTATERT:
5486 case ASHIFT:
5487 case LSHIFTRT:
5488 case ASHIFTRT:
5489 if (GET_CODE (XEXP (x, 1)) == CONST_INT)
5490 {
5491 return m68hc11_shift_cost (mode, XEXP (x, 0), INTVAL (XEXP (x, 1)));
5492 }
5493
5494 total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
5495 total += m68hc11_cost->shift_var;
5496 return total;
5497
5498 case AND:
5499 case XOR:
5500 case IOR:
5501 total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
5502 total += m68hc11_cost->logical;
5503
5504 /* Logical instructions are byte instructions only. */
5505 total *= GET_MODE_SIZE (mode);
5506 return total;
5507
5508 case MINUS:
5509 case PLUS:
5510 total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
5511 total += m68hc11_cost->add;
5512 if (GET_MODE_SIZE (mode) > 2)
5513 {
5514 total *= GET_MODE_SIZE (mode) / 2;
5515 }
5516 return total;
5517
5518 case UDIV:
5519 case DIV:
5520 case MOD:
5521 total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
5522 switch (mode)
5523 {
5524 case QImode:
5525 total += m68hc11_cost->divQI;
5526 break;
5527
5528 case HImode:
5529 total += m68hc11_cost->divHI;
5530 break;
5531
5532 case SImode:
5533 default:
5534 total += m68hc11_cost->divSI;
5535 break;
5536 }
5537 return total;
5538
5539 case MULT:
5540 /* mul instruction produces 16-bit result. */
5541 if (mode == HImode && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
5542 && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
5543 return m68hc11_cost->multQI
5544 + rtx_cost (XEXP (XEXP (x, 0), 0), code)
5545 + rtx_cost (XEXP (XEXP (x, 1), 0), code);
5546
5547 /* emul instruction produces 32-bit result for 68HC12. */
5548 if (TARGET_M6812 && mode == SImode
5549 && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
5550 && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
5551 return m68hc11_cost->multHI
5552 + rtx_cost (XEXP (XEXP (x, 0), 0), code)
5553 + rtx_cost (XEXP (XEXP (x, 1), 0), code);
5554
5555 total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
5556 switch (mode)
5557 {
5558 case QImode:
5559 total += m68hc11_cost->multQI;
5560 break;
5561
5562 case HImode:
5563 total += m68hc11_cost->multHI;
5564 break;
5565
5566 case SImode:
5567 default:
5568 total += m68hc11_cost->multSI;
5569 break;
5570 }
5571 return total;
5572
5573 case NEG:
5574 case SIGN_EXTEND:
5575 extra_cost = COSTS_N_INSNS (2);
5576
5577 /* Fall through */
5578 case NOT:
5579 case COMPARE:
5580 case ABS:
5581 case ZERO_EXTEND:
5582 total = extra_cost + rtx_cost (XEXP (x, 0), code);
5583 if (mode == QImode)
5584 {
5585 return total + COSTS_N_INSNS (1);
5586 }
5587 if (mode == HImode)
5588 {
5589 return total + COSTS_N_INSNS (2);
5590 }
5591 if (mode == SImode)
5592 {
5593 return total + COSTS_N_INSNS (4);
5594 }
5595 return total + COSTS_N_INSNS (8);
5596
5597 case IF_THEN_ELSE:
5598 if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
5599 return COSTS_N_INSNS (1);
5600
5601 return COSTS_N_INSNS (1);
5602
5603 default:
5604 return COSTS_N_INSNS (4);
5605 }
5606 }
5607
5608 static bool
5609 m68hc11_rtx_costs (x, code, outer_code, total)
5610 rtx x;
5611 int code, outer_code;
5612 int *total;
5613 {
5614 switch (code)
5615 {
5616 /* Constants are cheap. Moving them in registers must be avoided
5617 because most instructions do not handle two register operands. */
5618 case CONST_INT:
5619 case CONST:
5620 case LABEL_REF:
5621 case SYMBOL_REF:
5622 case CONST_DOUBLE:
5623 /* Logical and arithmetic operations with a constant operand are
5624 better because they are not supported with two registers. */
5625 /* 'clr' is slow */
5626 if (outer_code == SET && x == const0_rtx)
5627 /* After reload, the reload_cse pass checks the cost to change
5628 a SET into a PLUS. Make const0 cheap then. */
5629 *total = 1 - reload_completed;
5630 else
5631 *total = 0;
5632 return true;
5633
5634 if (outer_code == SET)
5635 *total = 1 - reload_completed;
5636
5637 case ROTATE:
5638 case ROTATERT:
5639 case ASHIFT:
5640 case LSHIFTRT:
5641 case ASHIFTRT:
5642 case MINUS:
5643 case PLUS:
5644 case AND:
5645 case XOR:
5646 case IOR:
5647 case UDIV:
5648 case DIV:
5649 case MOD:
5650 case MULT:
5651 case NEG:
5652 case SIGN_EXTEND:
5653 case NOT:
5654 case COMPARE:
5655 case ZERO_EXTEND:
5656 case IF_THEN_ELSE:
5657 *total = m68hc11_rtx_costs_1 (x, code, outer_code);
5658 return true;
5659
5660 default:
5661 return false;
5662 }
5663 }
5664 \f
5665
5666 /* print_options - called at the start of the code generation for a
5667 module. */
5668
5669 extern char *asm_file_name;
5670
5671 #include <time.h>
5672 #include <sys/types.h>
5673
5674 static void
5675 print_options (out)
5676 FILE *out;
5677 {
5678 const char *a_time;
5679 long c_time;
5680 int i;
5681 extern int save_argc;
5682 extern char **save_argv;
5683
5684 fprintf (out, ";;; Command:\t");
5685 for (i = 0; i < save_argc; i++)
5686 {
5687 fprintf (out, "%s", save_argv[i]);
5688 if (i + 1 < save_argc)
5689 fprintf (out, " ");
5690 }
5691 fprintf (out, "\n");
5692 c_time = time (0);
5693 a_time = ctime (&c_time);
5694 fprintf (out, ";;; Compiled:\t%s", a_time);
5695 #ifdef __GNUC__
5696 #ifndef __VERSION__
5697 #define __VERSION__ "[unknown]"
5698 #endif
5699 fprintf (out, ";;; (META)compiled by GNU C version %s.\n", __VERSION__);
5700 #else
5701 fprintf (out, ";;; (META)compiled by CC.\n");
5702 #endif
5703 }
5704
5705 void
5706 m68hc11_asm_file_start (out, main_file)
5707 FILE *out;
5708 const char *main_file;
5709 {
5710 fprintf (out, ";;;-----------------------------------------\n");
5711 fprintf (out, ";;; Start %s gcc assembly output\n",
5712 TARGET_M6811
5713 ? "MC68HC11"
5714 : TARGET_M68S12 ? "MC68HCS12" : "MC68HC12");
5715 fprintf (out, ";;; gcc compiler %s\n", version_string);
5716 print_options (out);
5717 fprintf (out, ";;;-----------------------------------------\n");
5718 output_file_directive (out, main_file);
5719
5720 if (TARGET_SHORT)
5721 fprintf (out, "\t.mode mshort\n");
5722 else
5723 fprintf (out, "\t.mode mlong\n");
5724 }
5725
5726
5727 static void
5728 m68hc11_asm_out_constructor (symbol, priority)
5729 rtx symbol;
5730 int priority;
5731 {
5732 default_ctor_section_asm_out_constructor (symbol, priority);
5733 fprintf (asm_out_file, "\t.globl\t__do_global_ctors\n");
5734 }
5735
5736 static void
5737 m68hc11_asm_out_destructor (symbol, priority)
5738 rtx symbol;
5739 int priority;
5740 {
5741 default_dtor_section_asm_out_destructor (symbol, priority);
5742 fprintf (asm_out_file, "\t.globl\t__do_global_dtors\n");
5743 }
5744
5745 #include "gt-m68hc11.h"