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