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