OSDN Git Service

* config/m68hc11/m68hc11.c (m68hc11_gen_highpart): Don't use
[pf3gnuchains/gcc-fork.git] / gcc / config / m68hc11 / m68hc11.c
1 /* Subroutines for code generation on Motorola 68HC11 and 68HC12.
2    Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3    Contributed by Stephane Carrez (stcarrez@worldnet.fr)
4
5 This file is part of GNU CC.
6
7 GNU CC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU CC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU CC; see the file COPYING.  If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
21
22 Note:
23    A first 68HC11 port was made by Otto Lind (otto@coactive.com)
24    on gcc 2.6.3.  I have used it as a starting point for this port.
25    However, this new port is a complete re-write.  Its internal
26    design is completely different.  The generated code is not
27    compatible with the gcc 2.6.3 port.
28
29    The gcc 2.6.3 port is available at:
30
31    ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
32
33 */
34
35 #include <stdio.h>
36 #include "config.h"
37 #include "system.h"
38 #include "rtl.h"
39 #include "tree.h"
40 #include "tm_p.h"
41 #include "regs.h"
42 #include "hard-reg-set.h"
43 #include "real.h"
44 #include "insn-config.h"
45 #include "conditions.h"
46 #include "output.h"
47 #include "insn-attr.h"
48 #include "flags.h"
49 #include "recog.h"
50 #include "expr.h"
51 #include "toplev.h"
52 #include "basic-block.h"
53 #include "function.h"
54 #include "ggc.h"
55 #include "target.h"
56 #include "target-def.h"
57
58 static void print_options PARAMS ((FILE *));
59 static void emit_move_after_reload PARAMS ((rtx, rtx, rtx));
60 static rtx simplify_logical PARAMS ((enum machine_mode, int, rtx, rtx *));
61 static void m68hc11_emit_logical PARAMS ((enum machine_mode, int, rtx *));
62 static int go_if_legitimate_address_internal PARAMS((rtx, enum machine_mode,
63                                                      int));
64 static int register_indirect_p PARAMS((rtx, enum machine_mode, int));
65 static rtx m68hc11_expand_compare PARAMS((enum rtx_code, rtx, rtx));
66 static int must_parenthesize PARAMS ((rtx));
67 static int m68hc11_shift_cost PARAMS ((enum machine_mode, rtx, int));
68 static int m68hc11_auto_inc_p PARAMS ((rtx));
69 static int m68hc11_valid_type_attribute_p PARAMS((tree, tree,
70                                                   tree, tree));
71
72 void create_regs_rtx PARAMS ((void));
73 static void m68hc11_add_gc_roots PARAMS ((void));
74
75 static void asm_print_register PARAMS ((FILE *, int));
76 static void m68hc11_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
77
78 rtx m68hc11_soft_tmp_reg;
79
80 /* Must be set to 1 to produce debug messages. */
81 int debug_m6811 = 0;
82
83 extern FILE *asm_out_file;
84
85 rtx ix_reg;
86 rtx iy_reg;
87 rtx d_reg;
88 rtx da_reg;
89 rtx stack_push_word;
90 rtx stack_pop_word;
91 static int regs_inited = 0;
92 static rtx z_reg;
93
94 /* Set to 1 by expand_prologue() when the function is an interrupt handler.  */
95 int current_function_interrupt;
96
97 /* Set to 1 by expand_prologue() when the function is a trap handler.  */
98 int current_function_trap;
99
100 /* Min offset that is valid for the indirect addressing mode.  */
101 HOST_WIDE_INT m68hc11_min_offset = 0;
102
103 /* Max offset that is valid for the indirect addressing mode.  */
104 HOST_WIDE_INT m68hc11_max_offset = 256;
105
106 /* The class value for base registers.  */
107 enum reg_class m68hc11_base_reg_class = A_REGS;
108
109 /* The class value for index registers.  This is NO_REGS for 68HC11.  */
110 enum reg_class m68hc11_index_reg_class = NO_REGS;
111
112 enum reg_class m68hc11_tmp_regs_class = NO_REGS;
113
114 /* Tables that tell whether a given hard register is valid for
115    a base or an index register.  It is filled at init time depending
116    on the target processor.  */
117 unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
118 unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
119
120 /* A correction offset which is applied to the stack pointer.
121    This is 1 for 68HC11 and 0 for 68HC12.  */
122 int m68hc11_sp_correction;
123
124 /* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns.  */
125 rtx m68hc11_compare_op0;
126 rtx m68hc11_compare_op1;
127 \f
128
129 struct processor_costs *m68hc11_cost;
130
131 /* Costs for a 68HC11.  */
132 struct processor_costs m6811_cost = {
133   /* add */
134   COSTS_N_INSNS (2),
135   /* logical */
136   COSTS_N_INSNS (2),
137   /* non-constant shift */
138   COSTS_N_INSNS (20),
139   /* shiftQI const */
140   { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
141     COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
142     COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
143
144   /* shiftHI const */
145   { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
146     COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
147     COSTS_N_INSNS (4), COSTS_N_INSNS (2),
148     COSTS_N_INSNS (2), COSTS_N_INSNS (4),
149     COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
150     COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
151   },
152   /* mulQI */
153   COSTS_N_INSNS (20),
154   /* mulHI */
155   COSTS_N_INSNS (20 * 4),
156   /* mulSI */
157   COSTS_N_INSNS (20 * 16),
158   /* divQI */
159   COSTS_N_INSNS (20),
160   /* divHI */
161   COSTS_N_INSNS (80),
162   /* divSI */
163   COSTS_N_INSNS (100)
164 };
165
166 /* Costs for a 68HC12.  */
167 struct processor_costs m6812_cost = {
168   /* add */
169   COSTS_N_INSNS (1),
170   /* logical */
171   COSTS_N_INSNS (1),
172   /* non-constant shift */
173   COSTS_N_INSNS (20),
174   /* shiftQI const */
175   { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
176     COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
177     COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
178
179   /* shiftHI const */
180   { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
181     COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
182     COSTS_N_INSNS (4), COSTS_N_INSNS (2),
183     COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
184     COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
185     COSTS_N_INSNS (6), COSTS_N_INSNS (4)
186   },
187   /* mulQI */
188   COSTS_N_INSNS (3),
189   /* mulHI */
190   COSTS_N_INSNS (3),
191   /* mulSI */
192   COSTS_N_INSNS (3 * 4),
193   /* divQI */
194   COSTS_N_INSNS (12),
195   /* divHI */
196   COSTS_N_INSNS (12),
197   /* divSI */
198   COSTS_N_INSNS (100)
199 };
200
201 /* Machine specific options */
202
203 const char *m68hc11_regparm_string;
204 const char *m68hc11_reg_alloc_order;
205 const char *m68hc11_soft_reg_count;
206
207 static int nb_soft_regs;
208 \f
209 /* Initialize the GCC target structure.  */
210 #undef TARGET_VALID_TYPE_ATTRIBUTE
211 #define TARGET_VALID_TYPE_ATTRIBUTE m68hc11_valid_type_attribute_p
212
213 #undef TARGET_ASM_FUNCTION_EPILOGUE
214 #define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
215
216 struct gcc_target targetm = TARGET_INITIALIZER;
217 \f
218 int
219 m68hc11_override_options ()
220 {
221   m68hc11_add_gc_roots ();
222
223   memset (m68hc11_reg_valid_for_index, 0,
224           sizeof (m68hc11_reg_valid_for_index));
225   memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
226
227   /* Compilation with -fpic generates a wrong code.  */
228   if (flag_pic)
229     {
230       warning ("-f%s ignored for 68HC11/68HC12 (not supported)",
231                (flag_pic > 1) ? "PIC" : "pic");
232       flag_pic = 0;
233     }
234
235   /* Configure for a 68hc11 processor.  */
236   if (TARGET_M6811)
237     {
238       /* If gcc was built for a 68hc12, invalidate that because
239          a -m68hc11 option was specified on the command line.  */
240       if (TARGET_DEFAULT != MASK_M6811)
241         target_flags &= ~TARGET_DEFAULT;
242
243       m68hc11_cost = &m6811_cost;
244       m68hc11_min_offset = 0;
245       m68hc11_max_offset = 256;
246       m68hc11_index_reg_class = NO_REGS;
247       m68hc11_base_reg_class = A_REGS;
248       m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
249       m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
250       m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
251       m68hc11_sp_correction = 1;
252       m68hc11_tmp_regs_class = D_REGS;
253       if (m68hc11_soft_reg_count == 0 && !TARGET_M6812)
254         m68hc11_soft_reg_count = "4";
255     }
256
257   /* Configure for a 68hc12 processor.  */
258   if (TARGET_M6812)
259     {
260       m68hc11_cost = &m6812_cost;
261       m68hc11_min_offset = -65536;
262       m68hc11_max_offset = 65536;
263       m68hc11_index_reg_class = D_REGS;
264       m68hc11_base_reg_class = A_OR_SP_REGS;
265       m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
266       m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
267       m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
268       m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
269       m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
270       m68hc11_sp_correction = 0;
271       m68hc11_tmp_regs_class = TMP_REGS;
272       target_flags &= ~MASK_M6811;
273       if (m68hc11_soft_reg_count == 0)
274         m68hc11_soft_reg_count = "2";
275     }
276   return 0;
277 }
278
279
280 void
281 m68hc11_conditional_register_usage ()
282 {
283   int i;
284   int cnt = atoi (m68hc11_soft_reg_count);
285
286   if (cnt < 0)
287     cnt = 0;
288   if (cnt > SOFT_REG_LAST - SOFT_REG_FIRST)
289     cnt = SOFT_REG_LAST - SOFT_REG_FIRST;
290
291   nb_soft_regs = cnt;
292   for (i = SOFT_REG_FIRST + cnt; i < SOFT_REG_LAST; i++)
293     {
294       fixed_regs[i] = 1;
295       call_used_regs[i] = 1;
296     }
297 }
298 \f
299
300 /* Reload and register operations. */
301
302 static const char *reg_class_names[] = REG_CLASS_NAMES;
303
304
305 void
306 create_regs_rtx ()
307 {
308   /*  regs_inited = 1; */
309   ix_reg = gen_rtx (REG, HImode, HARD_X_REGNUM);
310   iy_reg = gen_rtx (REG, HImode, HARD_Y_REGNUM);
311   d_reg = gen_rtx (REG, HImode, HARD_D_REGNUM);
312   da_reg = gen_rtx (REG, QImode, HARD_A_REGNUM);
313   m68hc11_soft_tmp_reg = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
314
315   stack_push_word = gen_rtx (MEM, HImode,
316                              gen_rtx (PRE_DEC, HImode,
317                                       gen_rtx (REG, HImode, HARD_SP_REGNUM)));
318   stack_pop_word = gen_rtx (MEM, HImode,
319                             gen_rtx (POST_INC, HImode,
320                                      gen_rtx (REG, HImode, HARD_SP_REGNUM)));
321
322 }
323
324 /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
325     - 8 bit values are stored anywhere (except the SP register).
326     - 16 bit values can be stored in any register whose mode is 16
327     - 32 bit values can be stored in D, X registers or in a soft register
328       (except the last one because we need 2 soft registers)
329     - Values whose size is > 32 bit are not stored in real hard
330       registers.  They may be stored in soft registers if there are
331       enough of them.  */
332 int
333 hard_regno_mode_ok (regno, mode)
334      int regno;
335      enum machine_mode mode;
336 {
337   switch (GET_MODE_SIZE (mode))
338     {
339     case 8:
340       return S_REGNO_P (regno) && nb_soft_regs >= 4;
341
342     case 4:
343       return X_REGNO_P (regno) || (S_REGNO_P (regno) && nb_soft_regs >= 2);
344
345     case 2:
346       return G_REGNO_P (regno);
347
348     case 1:
349       /* We have to accept a QImode in X or Y registers.  Otherwise, the
350          reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
351          in the insns.  Reload fails if the insn rejects the register class 'a'
352          as well as if it accepts it.  Patterns that failed were
353          zero_extend_qihi2 and iorqi3.  */
354
355       return G_REGNO_P (regno) && !SP_REGNO_P (regno);
356
357     default:
358       return 0;
359     }
360 }
361
362 enum reg_class
363 preferred_reload_class (operand, class)
364      rtx operand;
365      enum reg_class class;
366 {
367   enum machine_mode mode;
368
369   mode = GET_MODE (operand);
370
371   if (debug_m6811)
372     {
373       printf ("Preferred reload: (class=%s): ", reg_class_names[class]);
374     }
375
376   if (class == D_OR_A_OR_S_REGS && SP_REG_P (operand))
377     return m68hc11_base_reg_class;
378
379   if (class >= S_REGS && (GET_CODE (operand) == MEM
380                           || GET_CODE (operand) == CONST_INT))
381     {
382       /* S_REGS class must not be used.  The movhi template does not
383          work to move a memory to a soft register.
384          Restrict to a hard reg.  */
385       switch (class)
386         {
387         default:
388         case G_REGS:
389         case D_OR_A_OR_S_REGS:
390           class = A_OR_D_REGS;
391           break;
392         case A_OR_S_REGS:
393           class = A_REGS;
394           break;
395         case D_OR_SP_OR_S_REGS:
396           class = D_OR_SP_REGS;
397           break;
398         case D_OR_Y_OR_S_REGS:
399           class = D_OR_Y_REGS;
400           break;
401         case D_OR_X_OR_S_REGS:
402           class = D_OR_X_REGS;
403           break;
404         case SP_OR_S_REGS:
405           class = SP_REGS;
406           break;
407         case Y_OR_S_REGS:
408           class = Y_REGS;
409           break;
410         case X_OR_S_REGS:
411           class = X_REGS;
412           break;
413         case D_OR_S_REGS:
414           class = D_REGS;
415         }
416     }
417   else if (class == Y_REGS && GET_CODE (operand) == MEM)
418     {
419       class = Y_REGS;
420     }
421   else if (class == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
422     {
423       class = D_OR_X_REGS;
424     }
425   else if (class >= S_REGS && S_REG_P (operand))
426     {
427       switch (class)
428         {
429         default:
430         case G_REGS:
431         case D_OR_A_OR_S_REGS:
432           class = A_OR_D_REGS;
433           break;
434         case A_OR_S_REGS:
435           class = A_REGS;
436           break;
437         case D_OR_SP_OR_S_REGS:
438           class = D_OR_SP_REGS;
439           break;
440         case D_OR_Y_OR_S_REGS:
441           class = D_OR_Y_REGS;
442           break;
443         case D_OR_X_OR_S_REGS:
444           class = D_OR_X_REGS;
445           break;
446         case SP_OR_S_REGS:
447           class = SP_REGS;
448           break;
449         case Y_OR_S_REGS:
450           class = Y_REGS;
451           break;
452         case X_OR_S_REGS:
453           class = X_REGS;
454           break;
455         case D_OR_S_REGS:
456           class = D_REGS;
457         }
458     }
459   else if (class >= S_REGS)
460     {
461       if (debug_m6811)
462         {
463           printf ("Class = %s for: ", reg_class_names[class]);
464           fflush (stdout);
465           debug_rtx (operand);
466         }
467     }
468
469   if (debug_m6811)
470     {
471       printf (" => class=%s\n", reg_class_names[class]);
472       fflush (stdout);
473       debug_rtx (operand);
474     }
475
476   return class;
477 }
478
479 /* Return 1 if the operand is a valid indexed addressing mode.
480    For 68hc11:  n,r    with n in [0..255] and r in A_REGS class
481    For 68hc12:  n,r    no constraint on the constant, r in A_REGS class.  */
482 static int
483 register_indirect_p (operand, mode, strict)
484      rtx operand;
485      enum machine_mode mode;
486      int strict;
487 {
488   rtx base, offset;
489
490   switch (GET_CODE (operand))
491     {
492     case POST_INC:
493     case PRE_INC:
494     case POST_DEC:
495     case PRE_DEC:
496       if (TARGET_M6812 && TARGET_AUTO_INC_DEC)
497         return register_indirect_p (XEXP (operand, 0), mode, strict);
498       return 0;
499
500     case PLUS:
501       base = XEXP (operand, 0);
502       if (GET_CODE (base) == MEM)
503         return 0;
504
505       offset = XEXP (operand, 1);
506       if (GET_CODE (offset) == MEM)
507         return 0;
508
509       if (GET_CODE (base) == REG)
510         {
511           if (!VALID_CONSTANT_OFFSET_P (offset, mode))
512             return 0;
513
514           if (strict == 0)
515             return 1;
516
517           return REGNO_OK_FOR_BASE_P2 (REGNO (base), strict);
518         }
519       if (GET_CODE (offset) == REG)
520         {
521           if (!VALID_CONSTANT_OFFSET_P (base, mode))
522             return 0;
523
524           if (strict == 0)
525             return 1;
526
527           return REGNO_OK_FOR_BASE_P2 (REGNO (offset), strict);
528         }
529       return 0;
530
531     case REG:
532       return REGNO_OK_FOR_BASE_P2 (REGNO (operand), strict);
533
534     default:
535       return 0;
536     }
537 }
538
539 /* Returns 1 if the operand fits in a 68HC11 indirect mode or in
540    a 68HC12 1-byte index addressing mode.  */
541 int
542 m68hc11_small_indexed_indirect_p (operand, mode)
543      rtx operand;
544      enum machine_mode mode;
545 {
546   rtx base, offset;
547
548   if (GET_CODE (operand) != MEM)
549     return 0;
550
551   operand = XEXP (operand, 0);
552   if (CONSTANT_ADDRESS_P (operand))
553     return 1;
554
555   if (PUSH_POP_ADDRESS_P (operand))
556     return 1;
557
558   if (!register_indirect_p (operand, mode,
559                             (reload_completed | reload_in_progress)))
560     return 0;
561
562   if (TARGET_M6812 && GET_CODE (operand) == PLUS
563       && (reload_completed | reload_in_progress))
564     {
565       base = XEXP (operand, 0);
566       offset = XEXP (operand, 1);
567       if (GET_CODE (base) == CONST_INT)
568         offset = base;
569
570       switch (GET_MODE_SIZE (mode))
571         {
572         case 8:
573           if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
574             return 0;
575           break;
576
577         case 4:
578           if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
579             return 0;
580           break;
581
582         default:
583           if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
584             return 0;
585           break;
586         }
587     }
588   return 1;
589 }
590
591 int
592 m68hc11_register_indirect_p (operand, mode)
593      rtx operand;
594      enum machine_mode mode;
595 {
596   if (GET_CODE (operand) != MEM)
597     return 0;
598
599   operand = XEXP (operand, 0);
600   return register_indirect_p (operand, mode,
601                               (reload_completed | reload_in_progress));
602 }
603
604 static int
605 go_if_legitimate_address_internal (operand, mode, strict)
606      rtx operand;
607      enum machine_mode mode;
608      int strict;
609 {
610   if (CONSTANT_ADDRESS_P (operand))
611     {
612       /* Reject the global variables if they are too wide.  This forces
613          a load of their address in a register and generates smaller code.  */
614       if (GET_MODE_SIZE (mode) == 8)
615         return 0;
616
617       return 1;
618     }
619   if (register_indirect_p (operand, mode, strict))
620     {
621       return 1;
622     }
623   if (PUSH_POP_ADDRESS_P (operand))
624     {
625       return 1;
626     }
627   if (symbolic_memory_operand (operand, mode))
628     {
629       return 1;
630     }
631   return 0;
632 }
633
634 int
635 m68hc11_go_if_legitimate_address (operand, mode, strict)
636      rtx operand;
637      enum machine_mode mode;
638      int strict;
639 {
640   int result;
641
642   if (debug_m6811)
643     {
644       printf ("Checking: ");
645       fflush (stdout);
646       debug_rtx (operand);
647     }
648
649   result = go_if_legitimate_address_internal (operand, mode, strict);
650
651   if (debug_m6811)
652     {
653       printf (" -> %s\n", result == 0 ? "NO" : "YES");
654     }
655
656   if (result == 0)
657     {
658       if (debug_m6811)
659         {
660           printf ("go_if_legitimate%s, ret 0: %d:",
661                   (strict ? "_strict" : ""), mode);
662           fflush (stdout);
663           debug_rtx (operand);
664         }
665     }
666   return result;
667 }
668
669 int
670 m68hc11_legitimize_address (operand, old_operand, mode)
671      rtx *operand ATTRIBUTE_UNUSED;
672      rtx old_operand ATTRIBUTE_UNUSED;
673      enum machine_mode mode ATTRIBUTE_UNUSED;
674 {
675   return 0;
676 }
677
678
679 int
680 m68hc11_reload_operands (operands)
681      rtx operands[];
682 {
683   enum machine_mode mode;
684
685   if (regs_inited == 0)
686     create_regs_rtx ();
687
688   mode = GET_MODE (operands[1]);
689
690   /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
691   if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
692     {
693       rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
694       rtx base = XEXP (XEXP (operands[1], 0), 0);
695
696       if (GET_CODE (base) != REG)
697         {
698           rtx tmp = base;
699           base = big_offset;
700           big_offset = tmp;
701         }
702
703       /* If the offset is out of range, we have to compute the address
704          with a separate add instruction.  We try to do with with an 8-bit
705          add on the A register.  This is possible only if the lowest part
706          of the offset (ie, big_offset % 256) is a valid constant offset
707          with respect to the mode.  If it's not, we have to generate a
708          16-bit add on the D register.  From:
709        
710          (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
711        
712          we generate:
713         
714          [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
715          (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
716          [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
717          (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
718        
719          (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
720          (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))) 
721
722       */
723       if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
724         {
725           int vh, vl;
726           rtx reg = operands[0];
727           rtx offset;
728           int val = INTVAL (big_offset);
729
730
731           /* We use the 'operands[0]' as a scratch register to compute the
732              address. Make sure 'base' is in that register.  */
733           if (!rtx_equal_p (base, operands[0]))
734             {
735               emit_move_insn (reg, base);
736             }
737
738           if (val > 0)
739             {
740               vh = val >> 8;
741               vl = val & 0x0FF;
742             }
743           else
744             {
745               vh = (val >> 8) & 0x0FF;
746               vl = val & 0x0FF;
747             }
748
749           /* Create the lowest part offset that still remains to be added.
750              If it's not a valid offset, do a 16-bit add.  */
751           offset = gen_rtx (CONST_INT, VOIDmode, vl);
752           if (!VALID_CONSTANT_OFFSET_P (offset, mode))
753             {
754               emit_insn (gen_rtx (SET, VOIDmode, reg,
755                                   gen_rtx (PLUS, HImode, reg, big_offset)));
756               offset = const0_rtx;
757             }
758           else
759             {
760               emit_insn (gen_rtx (SET, VOIDmode, reg,
761                                   gen_rtx (PLUS, HImode, reg,
762                                            gen_rtx (CONST_INT,
763                                                     VOIDmode, vh << 8))));
764             }
765           emit_move_insn (operands[0],
766                           gen_rtx (MEM, GET_MODE (operands[1]),
767                                    gen_rtx (PLUS, Pmode, reg, offset)));
768           return 1;
769         }
770     }
771
772   /* Use the normal gen_movhi pattern. */
773   return 0;
774 }
775
776 void
777 m68hc11_emit_libcall (name, code, dmode, smode, noperands, operands)
778      const char *name;
779      enum rtx_code code;
780      enum machine_mode dmode;
781      enum machine_mode smode;
782      int noperands;
783      rtx *operands;
784 {
785   rtx ret;
786   rtx insns;
787   rtx libcall;
788   rtx equiv;
789
790   start_sequence ();
791   libcall = gen_rtx_SYMBOL_REF (Pmode, name);
792   switch (noperands)
793     {
794     case 2:
795       ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
796                                      dmode, 1, operands[1], smode);
797       equiv = gen_rtx (code, dmode, operands[1]);
798       break;
799
800     case 3:
801       ret = emit_library_call_value (libcall, NULL_RTX,
802                                      LCT_CONST, dmode, 2,
803                                      operands[1], smode, operands[2],
804                                      smode);
805       equiv = gen_rtx (code, dmode, operands[1], operands[2]);
806       break;
807
808     default:
809       abort ();
810     }
811
812   insns = get_insns ();
813   end_sequence ();
814   emit_libcall_block (insns, operands[0], ret, equiv);
815 }
816
817 /* Returns true if X is a PRE/POST increment decrement
818    (same as auto_inc_p() in rtlanal.c but do not take into
819    account the stack).  */
820 static int
821 m68hc11_auto_inc_p (x)
822      rtx x;
823 {
824   return GET_CODE (x) == PRE_DEC
825     || GET_CODE (x) == POST_INC
826     || GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
827 }
828 \f
829
830 /* Predicates for machine description.  */
831
832 int
833 memory_reload_operand (operand, mode)
834      rtx operand;
835      enum machine_mode mode ATTRIBUTE_UNUSED;
836 {
837   return GET_CODE (operand) == MEM
838     && GET_CODE (XEXP (operand, 0)) == PLUS
839     && ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
840          && GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
841         || (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
842             && GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
843 }
844
845 int
846 tst_operand (operand, mode)
847      rtx operand;
848      enum machine_mode mode;
849 {
850   if (GET_CODE (operand) == MEM)
851     {
852       rtx addr = XEXP (operand, 0);
853       if (m68hc11_auto_inc_p (addr))
854         return 0;
855     }
856   return nonimmediate_operand (operand, mode);
857 }
858
859 int
860 cmp_operand (operand, mode)
861      rtx operand;
862      enum machine_mode mode;
863 {
864   if (GET_CODE (operand) == MEM)
865     {
866       rtx addr = XEXP (operand, 0);
867       if (m68hc11_auto_inc_p (addr))
868         return 0;
869     }
870   return general_operand (operand, mode);
871 }
872
873 int
874 non_push_operand (operand, mode)
875      rtx operand;
876      enum machine_mode mode;
877 {
878   if (general_operand (operand, mode) == 0)
879     return 0;
880
881   if (push_operand (operand, mode) == 1)
882     return 0;
883   return 1;
884 }
885
886 int
887 reg_or_some_mem_operand (operand, mode)
888      rtx operand;
889      enum machine_mode mode;
890 {
891   if (GET_CODE (operand) == MEM)
892     {
893       rtx op = XEXP (operand, 0);
894
895       if (symbolic_memory_operand (op, mode))
896         return 1;
897
898       if (IS_STACK_PUSH (operand))
899         return 1;
900
901       if (m68hc11_register_indirect_p (operand, mode))
902         return 1;
903
904       return 0;
905     }
906
907   return register_operand (operand, mode);
908 }
909
910 int
911 stack_register_operand (operand, mode)
912      rtx operand;
913      enum machine_mode mode ATTRIBUTE_UNUSED;
914 {
915   return SP_REG_P (operand);
916 }
917
918 int
919 d_register_operand (operand, mode)
920      rtx operand;
921      enum machine_mode mode ATTRIBUTE_UNUSED;
922 {
923   if (GET_CODE (operand) == SUBREG)
924     operand = XEXP (operand, 0);
925
926   return GET_CODE (operand) == REG
927     && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
928         || REGNO (operand) == HARD_D_REGNUM);
929 }
930
931 int
932 hard_addr_reg_operand (operand, mode)
933      rtx operand;
934      enum machine_mode mode ATTRIBUTE_UNUSED;
935 {
936   if (GET_CODE (operand) == SUBREG)
937     operand = XEXP (operand, 0);
938
939   return GET_CODE (operand) == REG
940     && (REGNO (operand) == HARD_X_REGNUM
941         || REGNO (operand) == HARD_Y_REGNUM
942         || REGNO (operand) == HARD_Z_REGNUM);
943 }
944
945 int
946 hard_reg_operand (operand, mode)
947      rtx operand;
948      enum machine_mode mode ATTRIBUTE_UNUSED;
949 {
950   if (GET_CODE (operand) == SUBREG)
951     operand = XEXP (operand, 0);
952
953   return GET_CODE (operand) == REG
954     && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
955         || H_REGNO_P (REGNO (operand)));
956 }
957
958 int
959 memory_indexed_operand (operand, mode)
960      rtx operand;
961      enum machine_mode mode ATTRIBUTE_UNUSED;
962 {
963   if (GET_CODE (operand) != MEM)
964     return 0;
965
966   operand = XEXP (operand, 0);
967   if (GET_CODE (operand) == PLUS)
968     {
969       if (GET_CODE (XEXP (operand, 0)) == REG)
970         operand = XEXP (operand, 0);
971       else if (GET_CODE (XEXP (operand, 1)) == REG)
972         operand = XEXP (operand, 1);
973     }
974   return GET_CODE (operand) == REG
975     && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
976         || A_REGNO_P (REGNO (operand)));
977 }
978
979 int
980 push_pop_operand_p (operand)
981      rtx operand;
982 {
983   if (GET_CODE (operand) != MEM)
984     {
985       return 0;
986     }
987   operand = XEXP (operand, 0);
988   return PUSH_POP_ADDRESS_P (operand);
989 }
990
991 /* Returns 1 if OP is either a symbol reference or a sum of a symbol
992    reference and a constant.  */
993
994 int
995 symbolic_memory_operand (op, mode)
996      register rtx op;
997      enum machine_mode mode;
998 {
999   switch (GET_CODE (op))
1000     {
1001     case SYMBOL_REF:
1002     case LABEL_REF:
1003       return 1;
1004
1005     case CONST:
1006       return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
1007                || GET_CODE (XEXP (op, 0)) == LABEL_REF)
1008               && GET_CODE (XEXP (op, 1)) == CONST_INT);
1009
1010       /* ??? This clause seems to be irrelevant.  */
1011     case CONST_DOUBLE:
1012       return GET_MODE (op) == mode;
1013
1014     case PLUS:
1015       return symbolic_memory_operand (XEXP (op, 0), mode)
1016         && symbolic_memory_operand (XEXP (op, 1), mode);
1017
1018     default:
1019       return 0;
1020     }
1021 }
1022
1023 int
1024 m68hc11_logical_operator (op, mode)
1025      register rtx op;
1026      enum machine_mode mode ATTRIBUTE_UNUSED;
1027 {
1028   return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
1029 }
1030
1031 int
1032 m68hc11_arith_operator (op, mode)
1033      register rtx op;
1034      enum machine_mode mode ATTRIBUTE_UNUSED;
1035 {
1036   return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
1037     || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS
1038     || GET_CODE (op) == ASHIFT || GET_CODE (op) == ASHIFTRT
1039     || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATE
1040     || GET_CODE (op) == ROTATERT;
1041 }
1042
1043 int
1044 m68hc11_non_shift_operator (op, mode)
1045      register rtx op;
1046      enum machine_mode mode ATTRIBUTE_UNUSED;
1047 {
1048   return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
1049     || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS;
1050 }
1051
1052
1053 int
1054 m68hc11_unary_operator (op, mode)
1055      register rtx op;
1056      enum machine_mode mode ATTRIBUTE_UNUSED;
1057 {
1058   return GET_CODE (op) == NEG || GET_CODE (op) == NOT
1059     || GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
1060 }
1061 \f
1062
1063 /* Profiling.  */
1064
1065 int
1066 m68hc11_block_profiler (out, blockno)
1067      FILE *out ATTRIBUTE_UNUSED;
1068      int blockno ATTRIBUTE_UNUSED;
1069 {
1070   return 0;
1071 }
1072
1073 int
1074 m68hc11_function_block_profiler (out, block_or_label)
1075      FILE *out ATTRIBUTE_UNUSED;
1076      int block_or_label ATTRIBUTE_UNUSED;
1077 {
1078   return 0;
1079 }
1080
1081 /* Emit the code to build the trampoline used to call a nested function.
1082    
1083    68HC11               68HC12
1084
1085    ldy #&CXT            movw #&CXT,*_.d1
1086    sty *_.d1            jmp FNADDR
1087    jmp FNADDR
1088
1089 */
1090 void
1091 m68hc11_initialize_trampoline (tramp, fnaddr, cxt)
1092      rtx tramp;
1093      rtx fnaddr;
1094      rtx cxt;
1095 {
1096   char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
1097
1098   /* Skip the '*'.  */
1099   if (*static_chain_reg == '*')
1100     static_chain_reg++;
1101   if (TARGET_M6811)
1102     {
1103       emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
1104       emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
1105       emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
1106                       GEN_INT (0x18df));
1107       emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
1108                       gen_rtx_CONST (QImode,
1109                                      gen_rtx_SYMBOL_REF (Pmode,
1110                                                          static_chain_reg)));
1111       emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
1112                       GEN_INT (0x7e));
1113       emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
1114     }
1115   else
1116     {
1117       emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
1118       emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
1119       emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
1120                       gen_rtx_CONST (HImode,
1121                                      gen_rtx_SYMBOL_REF (Pmode,
1122                                                          static_chain_reg)));
1123       emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
1124                       GEN_INT (0x06));
1125       emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
1126     }
1127 }
1128 \f
1129 /* Declaration of types.  */
1130
1131 /* If defined, a C expression whose value is nonzero if IDENTIFIER
1132    with arguments ARGS is a valid machine specific attribute for TYPE.
1133    The attributes in ATTRIBUTES have previously been assigned to TYPE.  */
1134
1135 static int
1136 m68hc11_valid_type_attribute_p (type, attributes, identifier, args)
1137      tree type;
1138      tree attributes ATTRIBUTE_UNUSED;
1139      tree identifier;
1140      tree args;
1141 {
1142   if (TREE_CODE (type) != FUNCTION_TYPE
1143       && TREE_CODE (type) != FIELD_DECL && TREE_CODE (type) != TYPE_DECL)
1144     return 0;
1145
1146   if (TREE_CODE (type) == FUNCTION_TYPE)
1147     {
1148       if (is_attribute_p ("interrupt", identifier))
1149         return (args == NULL_TREE);
1150       if (is_attribute_p ("trap", identifier))
1151         return (args == NULL_TREE);
1152     }
1153
1154   return 0;
1155 }
1156
1157 /* Define this macro if references to a symbol must be treated
1158    differently depending on something about the variable or function
1159    named by the symbol (such as what section it is in).
1160
1161    For the 68HC11, we want to recognize trap handlers so that we
1162    handle calls to traps in a special manner (by issuing the trap).
1163    This information is stored in SYMBOL_REF_FLAG.  */
1164 void
1165 m68hc11_encode_section_info (decl)
1166      tree decl;
1167 {
1168   tree func_attr;
1169   int trap_handler;
1170   rtx rtl;
1171
1172   if (TREE_CODE (decl) != FUNCTION_DECL)
1173     return;
1174
1175   rtl = DECL_RTL (decl);
1176
1177   func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
1178   trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
1179   SYMBOL_REF_FLAG (XEXP (rtl, 0)) = trap_handler;
1180 }
1181 \f
1182
1183 /* Argument support functions.  */
1184
1185 /* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
1186    Arrays are passed by references and other types by value.
1187
1188    SCz: I tried to pass DImode by reference but it seems that this
1189    does not work very well.  */
1190 int
1191 m68hc11_function_arg_pass_by_reference (cum, mode, type, named)
1192      const CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
1193      enum machine_mode mode ATTRIBUTE_UNUSED;
1194      tree type;
1195      int named ATTRIBUTE_UNUSED;
1196 {
1197   return ((type && TREE_CODE (type) == ARRAY_TYPE)
1198           /* Consider complex values as aggregates, so care for TCmode. */
1199           /*|| GET_MODE_SIZE (mode) > 4 SCz, temporary */
1200           /*|| (type && AGGREGATE_TYPE_P (type))) */ );
1201 }
1202
1203
1204 /* Define the offset between two registers, one to be eliminated, and the
1205    other its replacement, at the start of a routine.  */
1206 int
1207 m68hc11_initial_elimination_offset (from, to)
1208      int from;
1209      int to;
1210 {
1211   int trap_handler;
1212   tree func_attr;
1213   int size;
1214   int regno;
1215
1216   /* For a trap handler, we must take into account the registers which
1217      are pushed on the stack during the trap (except the PC).  */
1218   func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1219   trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
1220   if (trap_handler && from == ARG_POINTER_REGNUM)
1221     size = 7;
1222   else
1223     size = 0;
1224
1225   if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1226     {
1227       /* 2 is for the saved frame.
1228          1 is for the 'sts' correction when creating the frame.  */
1229       return get_frame_size () + 2 + m68hc11_sp_correction + size;
1230     }
1231
1232   if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1233     {
1234       return m68hc11_sp_correction;
1235     }
1236
1237   /* Push any 2 byte pseudo hard registers that we need to save.  */
1238   for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
1239     {
1240       if (regs_ever_live[regno] && !call_used_regs[regno])
1241         {
1242           size += 2;
1243         }
1244     }
1245
1246   if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
1247     {
1248       return get_frame_size () + size;
1249     }
1250
1251   if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
1252     {
1253       return size;
1254     }
1255   return 0;
1256 }
1257
1258 /* Initialize a variable CUM of type CUMULATIVE_ARGS
1259    for a call to a function whose data type is FNTYPE.
1260    For a library call, FNTYPE is 0.  */
1261
1262 void
1263 m68hc11_init_cumulative_args (cum, fntype, libname)
1264      CUMULATIVE_ARGS *cum;
1265      tree fntype;
1266      rtx libname;
1267 {
1268   tree ret_type;
1269
1270   z_replacement_completed = 0;
1271   cum->words = 0;
1272   cum->nregs = 0;
1273
1274   /* For a library call, we must find out the type of the return value.
1275      When the return value is bigger than 4 bytes, it is returned in
1276      memory.  In that case, the first argument of the library call is a
1277      pointer to the memory location.  Because the first argument is passed in
1278      register D, we have to identify this, so that the first function
1279      parameter is not passed in D either.  */
1280   if (fntype == 0)
1281     {
1282       const char *name;
1283       size_t len;
1284
1285       if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
1286         return;
1287
1288       /* If the library ends in 'di' or in 'df', we assume it's
1289          returning some DImode or some DFmode which are 64-bit wide.  */
1290       name = XSTR (libname, 0);
1291       len = strlen (name);
1292       if (len > 3
1293           && ((name[len - 2] == 'd'
1294                && (name[len - 1] == 'f' || name[len - 1] == 'i'))
1295               || (name[len - 3] == 'd'
1296                   && (name[len - 2] == 'i' || name[len - 2] == 'f'))))
1297         {
1298           /* We are in.  Mark the first parameter register as already used.  */
1299           cum->words = 1;
1300           cum->nregs = 1;
1301         }
1302       return;
1303     }
1304
1305   ret_type = TREE_TYPE (fntype);
1306
1307   if (ret_type && aggregate_value_p (ret_type))
1308     {
1309       cum->words = 1;
1310       cum->nregs = 1;
1311     }
1312 }
1313
1314 /* Update the data in CUM to advance over an argument
1315    of mode MODE and data type TYPE.
1316    (TYPE is null for libcalls where that information may not be available.)  */
1317
1318 void
1319 m68hc11_function_arg_advance (cum, mode, type, named)
1320      CUMULATIVE_ARGS *cum;
1321      enum machine_mode mode;
1322      tree type;
1323      int named ATTRIBUTE_UNUSED;
1324 {
1325   if (mode != BLKmode)
1326     {
1327       if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
1328         {
1329           cum->nregs = 2;
1330           cum->words = GET_MODE_SIZE (mode);
1331         }
1332       else
1333         {
1334           cum->words += GET_MODE_SIZE (mode);
1335           if (cum->words <= HARD_REG_SIZE)
1336             cum->nregs = 1;
1337         }
1338     }
1339   else
1340     {
1341       cum->words += int_size_in_bytes (type);
1342     }
1343   return;
1344 }
1345
1346 /* Define where to put the arguments to a function.
1347    Value is zero to push the argument on the stack,
1348    or a hard register in which to store the argument.
1349
1350    MODE is the argument's machine mode.
1351    TYPE is the data type of the argument (as a tree).
1352     This is null for libcalls where that information may
1353     not be available.
1354    CUM is a variable of type CUMULATIVE_ARGS which gives info about
1355     the preceding args and about the function being called.
1356    NAMED is nonzero if this argument is a named parameter
1357     (otherwise it is an extra parameter matching an ellipsis).  */
1358
1359 struct rtx_def *
1360 m68hc11_function_arg (cum, mode, type, named)
1361      const CUMULATIVE_ARGS *cum;
1362      enum machine_mode mode;
1363      tree type ATTRIBUTE_UNUSED;
1364      int named ATTRIBUTE_UNUSED;
1365 {
1366   if (cum->words != 0)
1367     {
1368       return NULL_RTX;
1369     }
1370
1371   if (mode != BLKmode)
1372     {
1373       if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
1374         return gen_rtx (REG, mode, HARD_X_REGNUM);
1375
1376       if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
1377         {
1378           return NULL_RTX;
1379         }
1380       return gen_rtx (REG, mode, HARD_D_REGNUM);
1381     }
1382   return NULL_RTX;
1383 }
1384
1385 /* The "standard" implementation of va_start: just assign `nextarg' to
1386    the variable.  */
1387 void
1388 m68hc11_expand_builtin_va_start (stdarg_p, valist, nextarg)
1389      int stdarg_p ATTRIBUTE_UNUSED;
1390      tree valist;
1391      rtx nextarg;
1392 {
1393   tree t;
1394
1395   /* SCz: the default implementation in builtins.c adjust the
1396      nextarg using UNITS_PER_WORD.  This works only with -mshort
1397      and fails when integers are 32-bit.  Here is the correct way.  */
1398   if (!stdarg_p)
1399     nextarg = plus_constant (nextarg, -INT_TYPE_SIZE / 8);
1400
1401   t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
1402              make_tree (ptr_type_node, nextarg));
1403   TREE_SIDE_EFFECTS (t) = 1;
1404
1405   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1406 }
1407
1408 rtx
1409 m68hc11_va_arg (valist, type)
1410      tree valist;
1411      tree type;
1412 {
1413   tree addr_tree, t;
1414   HOST_WIDE_INT align;
1415   HOST_WIDE_INT rounded_size;
1416   rtx addr;
1417   int pad_direction;
1418
1419   /* Compute the rounded size of the type.  */
1420   align = PARM_BOUNDARY / BITS_PER_UNIT;
1421   rounded_size = (((int_size_in_bytes (type) + align - 1) / align) * align);
1422
1423   /* Get AP.  */
1424   addr_tree = valist;
1425   pad_direction = m68hc11_function_arg_padding (TYPE_MODE (type), type);
1426
1427   if (pad_direction == downward)
1428     {
1429       /* Small args are padded downward.  */
1430
1431       HOST_WIDE_INT adj;
1432       adj = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT;
1433       if (rounded_size > align)
1434         adj = rounded_size;
1435
1436       addr_tree = build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree,
1437                          build_int_2 (rounded_size - adj, 0));
1438     }
1439
1440   addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
1441   addr = copy_to_reg (addr);
1442
1443   /* Compute new value for AP.  */
1444   t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
1445              build (PLUS_EXPR, TREE_TYPE (valist), valist,
1446                     build_int_2 (rounded_size, 0)));
1447   TREE_SIDE_EFFECTS (t) = 1;
1448   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1449
1450   return addr;
1451 }
1452
1453 /* If defined, a C expression which determines whether, and in which direction,
1454    to pad out an argument with extra space.  The value should be of type
1455    `enum direction': either `upward' to pad above the argument,
1456    `downward' to pad below, or `none' to inhibit padding.
1457
1458    Structures are stored left shifted in their argument slot.  */
1459 int
1460 m68hc11_function_arg_padding (mode, type)
1461      enum machine_mode mode;
1462      tree type;
1463 {
1464   if (type != 0 && AGGREGATE_TYPE_P (type))
1465     return upward;
1466
1467   /* This is the default definition.  */
1468   return (!BYTES_BIG_ENDIAN
1469           ? upward
1470           : ((mode == BLKmode
1471               ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
1472                  && int_size_in_bytes (type) <
1473                  (PARM_BOUNDARY / BITS_PER_UNIT)) : GET_MODE_BITSIZE (mode) <
1474               PARM_BOUNDARY) ? downward : upward));
1475 }
1476 \f
1477
1478 /* Function prologue and epilogue.  */
1479
1480 /* Emit a move after the reload pass has completed.  This is used to
1481    emit the prologue and epilogue.  */
1482 static void
1483 emit_move_after_reload (to, from, scratch)
1484      rtx to, from, scratch;
1485 {
1486   rtx insn;
1487
1488   if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
1489     {
1490       insn = emit_move_insn (to, from);
1491     }
1492   else
1493     {
1494       emit_move_insn (scratch, from);
1495       insn = emit_move_insn (to, scratch);
1496     }
1497
1498   /* Put a REG_INC note to tell the flow analysis that the instruction
1499      is necessary.  */
1500   if (IS_STACK_PUSH (to))
1501     {
1502       REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
1503                                             XEXP (XEXP (to, 0), 0),
1504                                             REG_NOTES (insn));
1505     }
1506   else if (IS_STACK_POP (from))
1507     {
1508       REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
1509                                             XEXP (XEXP (from, 0), 0),
1510                                             REG_NOTES (insn));
1511     }
1512 }
1513
1514 int
1515 m68hc11_total_frame_size ()
1516 {
1517   int size;
1518   int regno;
1519
1520   size = get_frame_size ();
1521   if (current_function_interrupt)
1522     {
1523       size += 3 * HARD_REG_SIZE;
1524     }
1525   if (frame_pointer_needed)
1526     size += HARD_REG_SIZE;
1527
1528   for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
1529     if (regs_ever_live[regno] && !call_used_regs[regno])
1530       size += HARD_REG_SIZE;
1531
1532   return size;
1533 }
1534
1535 static void
1536 m68hc11_output_function_epilogue (out, size)
1537      FILE *out ATTRIBUTE_UNUSED;
1538      HOST_WIDE_INT size ATTRIBUTE_UNUSED;
1539 {
1540   /* We catch the function epilogue generation to have a chance
1541      to clear the z_replacement_completed flag.  */
1542   z_replacement_completed = 0;
1543 }
1544
1545 void
1546 expand_prologue ()
1547 {
1548   tree func_attr;
1549   int size;
1550   int regno;
1551   rtx scratch;
1552
1553   if (reload_completed != 1)
1554     abort ();
1555
1556   size = get_frame_size ();
1557
1558   create_regs_rtx ();
1559
1560   /* Generate specific prologue for interrupt handlers.  */
1561   func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1562   current_function_interrupt = lookup_attribute ("interrupt",
1563                                                  func_attr) != NULL_TREE;
1564   current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
1565
1566   /* Get the scratch register to build the frame and push registers.
1567      If the first argument is a 32-bit quantity, the D+X registers
1568      are used.  Use Y to compute the frame.  Otherwise, X is cheaper.
1569      For 68HC12, this scratch register is not used.  */
1570   if (current_function_args_info.nregs == 2)
1571     scratch = iy_reg;
1572   else
1573     scratch = ix_reg;
1574
1575   /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
1576      Other soft registers in page0 need not to be saved because they
1577      will be restored by C functions.  For a trap handler, we don't
1578      need to preserve these registers because this is a synchronous call.  */
1579   if (current_function_interrupt)
1580     {
1581       emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
1582       emit_move_after_reload (stack_push_word,
1583                               gen_rtx (REG, HImode, SOFT_Z_REGNUM), scratch);
1584       emit_move_after_reload (stack_push_word,
1585                               gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM),
1586                               scratch);
1587     }
1588
1589   /* Save current stack frame.  */
1590   if (frame_pointer_needed)
1591     emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
1592
1593   /* Allocate local variables.  */
1594   if (TARGET_M6812 && size >= 2)
1595     {
1596       emit_insn (gen_addhi3 (stack_pointer_rtx,
1597                              stack_pointer_rtx, GEN_INT (-size)));
1598     }
1599   else if (size > 8)
1600     {
1601       rtx insn;
1602
1603       insn = gen_rtx_PARALLEL
1604         (VOIDmode,
1605          gen_rtvec (2,
1606                     gen_rtx_SET (VOIDmode,
1607                                  stack_pointer_rtx,
1608                                  gen_rtx_PLUS (HImode,
1609                                                stack_pointer_rtx,
1610                                                GEN_INT (-size))),
1611                     gen_rtx_CLOBBER (VOIDmode, scratch)));
1612       emit_insn (insn);
1613     }
1614   else
1615     {
1616       int i;
1617
1618       /* Allocate by pushing scratch values.  */
1619       for (i = 2; i <= size; i += 2)
1620         emit_move_after_reload (stack_push_word, ix_reg, 0);
1621
1622       if (size & 1)
1623         emit_insn (gen_addhi3 (stack_pointer_rtx,
1624                                stack_pointer_rtx, GEN_INT (-1)));
1625     }
1626
1627   /* Create the frame pointer.  */
1628   if (frame_pointer_needed)
1629     emit_move_after_reload (hard_frame_pointer_rtx,
1630                             stack_pointer_rtx, scratch);
1631
1632   /* Push any 2 byte pseudo hard registers that we need to save.  */
1633   for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
1634     {
1635       if (regs_ever_live[regno] && !call_used_regs[regno])
1636         {
1637           emit_move_after_reload (stack_push_word,
1638                                   gen_rtx (REG, HImode, regno), scratch);
1639         }
1640     }
1641 }
1642
1643 void
1644 expand_epilogue ()
1645 {
1646   int size;
1647   register int regno;
1648   int return_size;
1649   rtx scratch;
1650
1651   if (reload_completed != 1)
1652     abort ();
1653
1654   size = get_frame_size ();
1655
1656   /* If we are returning a value in two registers, we have to preserve the
1657      X register and use the Y register to restore the stack and the saved
1658      registers.  Otherwise, use X because it's faster (and smaller).  */
1659   if (current_function_return_rtx == 0)
1660     return_size = 0;
1661   else if (GET_CODE (current_function_return_rtx) == MEM)
1662     return_size = HARD_REG_SIZE;
1663   else
1664     return_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));
1665
1666   if (return_size > HARD_REG_SIZE)
1667     scratch = iy_reg;
1668   else
1669     scratch = ix_reg;
1670
1671   /* Pop any 2 byte pseudo hard registers that we saved.  */
1672   for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
1673     {
1674       if (regs_ever_live[regno] && !call_used_regs[regno])
1675         {
1676           emit_move_after_reload (gen_rtx (REG, HImode, regno),
1677                                   stack_pop_word, scratch);
1678         }
1679     }
1680
1681   /* de-allocate auto variables */
1682   if (TARGET_M6812 && size >= 2)
1683     {
1684       emit_insn (gen_addhi3 (stack_pointer_rtx,
1685                              stack_pointer_rtx, GEN_INT (size)));
1686     }
1687   else if (size > 8)
1688     {
1689       rtx insn;
1690
1691       insn = gen_rtx_PARALLEL
1692         (VOIDmode,
1693          gen_rtvec (2,
1694                     gen_rtx_SET (VOIDmode,
1695                                  stack_pointer_rtx,
1696                                  gen_rtx_PLUS (HImode,
1697                                                stack_pointer_rtx,
1698                                                GEN_INT (size))),
1699                     gen_rtx_CLOBBER (VOIDmode, scratch)));
1700       emit_insn (insn);
1701     }
1702   else
1703     {
1704       int i;
1705
1706       for (i = 2; i <= size; i += 2)
1707         emit_move_after_reload (scratch, stack_pop_word, scratch);
1708       if (size & 1)
1709         emit_insn (gen_addhi3 (stack_pointer_rtx,
1710                                stack_pointer_rtx, GEN_INT (1)));
1711     }
1712
1713   /* Restore previous frame pointer.  */
1714   if (frame_pointer_needed)
1715     emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
1716
1717   /* For an interrupt handler, restore ZTMP, ZREG and XYREG.  */
1718   if (current_function_interrupt)
1719     {
1720       emit_move_after_reload (gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM),
1721                               stack_pop_word, scratch);
1722       emit_move_after_reload (gen_rtx (REG, HImode, SOFT_Z_REGNUM),
1723                               stack_pop_word, scratch);
1724       emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
1725     }
1726
1727   /* If the trap handler returns some value, copy the value
1728      in D, X onto the stack so that the rti will pop the return value
1729      correctly.  */
1730   else if (current_function_trap && return_size != 0)
1731     {
1732       rtx addr_reg = stack_pointer_rtx;
1733
1734       if (!TARGET_M6812)
1735         {
1736           emit_move_after_reload (scratch, stack_pointer_rtx, 0);
1737           addr_reg = scratch;
1738         }
1739       emit_move_after_reload (gen_rtx (MEM, HImode,
1740                                        gen_rtx (PLUS, HImode, addr_reg,
1741                                                 GEN_INT (1))), d_reg, 0);
1742       if (return_size > HARD_REG_SIZE)
1743         emit_move_after_reload (gen_rtx (MEM, HImode,
1744                                          gen_rtx (PLUS, HImode, addr_reg,
1745                                                   GEN_INT (3))), ix_reg, 0);
1746     }
1747
1748   emit_jump_insn (gen_return ());
1749 }
1750 \f
1751
1752 /* Low and High part extraction for 68HC11.  These routines are
1753    similar to gen_lowpart and gen_highpart but they have been
1754    fixed to work for constants and 68HC11 specific registers.  */
1755
1756 rtx
1757 m68hc11_gen_lowpart (mode, x)
1758      enum machine_mode mode;
1759      rtx x;
1760 {
1761   /* We assume that the low part of an auto-inc mode is the same with
1762      the mode changed and that the caller split the larger mode in the
1763      correct order.  */
1764   if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
1765     {
1766       return gen_rtx (MEM, mode, XEXP (x, 0));
1767     }
1768
1769   /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1770      floating-point constant.  A CONST_DOUBLE is used whenever the
1771      constant requires more than one word in order to be adequately
1772      represented.  */
1773   if (GET_CODE (x) == CONST_DOUBLE)
1774     {
1775       long l[2];
1776
1777       if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1778         {
1779           REAL_VALUE_TYPE r;
1780
1781           if (GET_MODE (x) == SFmode)
1782             {
1783               REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1784               REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
1785             }
1786           else
1787             {
1788               rtx first, second;
1789
1790               split_double (x, &first, &second);
1791               return second;
1792             }
1793           if (mode == SImode)
1794             return gen_rtx (CONST_INT, VOIDmode, l[0]);
1795
1796           return gen_rtx (CONST_INT, VOIDmode,
1797                           trunc_int_for_mode (l[0], HImode));
1798         }
1799       else
1800         {
1801           l[0] = CONST_DOUBLE_LOW (x);
1802         }
1803       if (mode == SImode)
1804         return gen_rtx (CONST_INT, VOIDmode, l[0]);
1805       else if (mode == HImode && GET_MODE (x) == SFmode)
1806         return gen_rtx (CONST_INT, VOIDmode,
1807                         trunc_int_for_mode (l[0], HImode));
1808       else
1809         abort ();
1810     }
1811
1812   if (mode == QImode && D_REG_P (x))
1813     return gen_rtx (REG, mode, HARD_B_REGNUM);
1814
1815   /* gen_lowpart crashes when it is called with a SUBREG.  */
1816   if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
1817     {
1818       if (mode == SImode)
1819         return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
1820       else if (mode == HImode)
1821         return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
1822       else
1823         abort ();
1824     }
1825   x = gen_lowpart (mode, x);
1826
1827   /* Return a different rtx to avoid to share it in several insns
1828      (when used by a split pattern).  Sharing addresses within
1829      a MEM breaks the Z register replacement (and reloading).  */
1830   if (GET_CODE (x) == MEM)
1831     x = copy_rtx (x);
1832   return x;
1833 }
1834
1835 rtx
1836 m68hc11_gen_highpart (mode, x)
1837      enum machine_mode mode;
1838      rtx x;
1839 {
1840   /* We assume that the high part of an auto-inc mode is the same with
1841      the mode changed and that the caller split the larger mode in the
1842      correct order.  */
1843   if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
1844     {
1845       return gen_rtx (MEM, mode, XEXP (x, 0));
1846     }
1847
1848   /* Note that a CONST_DOUBLE rtx could represent either an integer or a
1849      floating-point constant.  A CONST_DOUBLE is used whenever the
1850      constant requires more than one word in order to be adequately
1851      represented.  */
1852   if (GET_CODE (x) == CONST_DOUBLE)
1853     {
1854       long l[2];
1855
1856       if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1857         {
1858           REAL_VALUE_TYPE r;
1859
1860           if (GET_MODE (x) == SFmode)
1861             {
1862               REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1863               REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
1864             }
1865           else
1866             {
1867               rtx first, second;
1868
1869               split_double (x, &first, &second);
1870               return first;
1871             }
1872           if (mode == SImode)
1873             return gen_rtx (CONST_INT, VOIDmode, l[1]);
1874
1875           return gen_rtx (CONST_INT, VOIDmode,
1876                           trunc_int_for_mode ((l[1] >> 16), HImode));
1877         }
1878       else
1879         {
1880           l[1] = CONST_DOUBLE_HIGH (x);
1881         }
1882
1883       if (mode == SImode)
1884         return gen_rtx (CONST_INT, VOIDmode, l[1]);
1885       else if (mode == HImode && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
1886         return gen_rtx (CONST_INT, VOIDmode,
1887                         trunc_int_for_mode ((l[0] >> 16), HImode));
1888       else
1889         abort ();
1890     }
1891   if (GET_CODE (x) == CONST_INT)
1892     {
1893       HOST_WIDE_INT val = INTVAL (x);
1894
1895       if (mode == QImode)
1896         {
1897           return gen_rtx (CONST_INT, VOIDmode,
1898                           trunc_int_for_mode (val >> 8, QImode));
1899         }
1900       else if (mode == HImode)
1901         {
1902           return gen_rtx (CONST_INT, VOIDmode,
1903                           trunc_int_for_mode (val >> 16, HImode));
1904         }
1905     }
1906   if (mode == QImode && D_REG_P (x))
1907     return gen_rtx (REG, mode, HARD_A_REGNUM);
1908
1909   /* There is no way in GCC to represent the upper part of a word register.
1910      To obtain the 8-bit upper part of a soft register, we change the
1911      reg into a mem rtx.  This is possible because they are physically
1912      located in memory.  There is no offset because we are big-endian.  */
1913   if (mode == QImode && S_REG_P (x))
1914     {
1915       int pos;
1916
1917       /* For 68HC12, avoid the '*' for direct addressing mode.  */
1918       pos = TARGET_M6812 ? 1 : 0;
1919       return gen_rtx (MEM, QImode,
1920                       gen_rtx (SYMBOL_REF, Pmode,
1921                                &reg_names[REGNO (x)][pos]));
1922     }
1923
1924   /* gen_highpart crashes when it is called with a SUBREG.  */
1925   if (GET_CODE (x) == SUBREG)
1926     {
1927       return gen_rtx (SUBREG, mode, XEXP (x, 0), XEXP (x, 1));
1928     }
1929   if (GET_CODE (x) == REG)
1930     {
1931       if (REGNO (x) < FIRST_PSEUDO_REGISTER)
1932         return gen_rtx (REG, mode, REGNO (x));
1933       else
1934         return gen_rtx_SUBREG (mode, x, 0);
1935     }
1936
1937   if (GET_CODE (x) == MEM)
1938     {
1939       x = change_address (x, mode, 0);
1940
1941       /* Return a different rtx to avoid to share it in several insns
1942          (when used by a split pattern).  Sharing addresses within
1943          a MEM breaks the Z register replacement (and reloading).  */
1944       if (GET_CODE (x) == MEM)
1945         x = copy_rtx (x);
1946       return x;
1947     }
1948   abort ();
1949 }
1950 \f
1951
1952 /* Obscure register manipulation.  */
1953
1954 /* Finds backward in the instructions to see if register 'reg' is
1955    dead.  This is used when generating code to see if we can use 'reg'
1956    as a scratch register.  This allows us to choose a better generation
1957    of code when we know that some register dies or can be clobbered.  */
1958
1959 int
1960 dead_register_here (x, reg)
1961      rtx x;
1962      rtx reg;
1963 {
1964   rtx x_reg;
1965   rtx p;
1966
1967   if (D_REG_P (reg))
1968     x_reg = gen_rtx (REG, SImode, HARD_X_REGNUM);
1969   else
1970     x_reg = 0;
1971
1972   for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
1973     if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
1974       {
1975         rtx body;
1976
1977         body = PATTERN (p);
1978
1979         if (GET_CODE (body) == CALL_INSN)
1980           break;
1981         if (GET_CODE (body) == JUMP_INSN)
1982           break;
1983
1984         if (GET_CODE (body) == SET)
1985           {
1986             rtx dst = XEXP (body, 0);
1987
1988             if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
1989               break;
1990             if (x_reg && rtx_equal_p (dst, x_reg))
1991               break;
1992
1993             if (find_regno_note (p, REG_DEAD, REGNO (reg)))
1994               return 1;
1995           }
1996         else if (reg_mentioned_p (reg, p)
1997                  || (x_reg && reg_mentioned_p (x_reg, p)))
1998           break;
1999       }
2000
2001   /* Scan forward to see if the register is set in some insns and never
2002      used since then. */
2003   for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
2004     {
2005       rtx body;
2006
2007       if (GET_CODE (p) == CODE_LABEL
2008           || GET_CODE (p) == JUMP_INSN
2009           || GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
2010         break;
2011
2012       if (GET_CODE (p) != INSN)
2013         continue;
2014
2015       body = PATTERN (p);
2016       if (GET_CODE (body) == SET)
2017         {
2018           rtx src = XEXP (body, 1);
2019           rtx dst = XEXP (body, 0);
2020
2021           if (GET_CODE (dst) == REG
2022               && REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
2023             return 1;
2024         }
2025
2026       /* Register is used (may be in source or in dest). */
2027       if (reg_mentioned_p (reg, p)
2028           || (x_reg != 0 && GET_MODE (p) == SImode
2029               && reg_mentioned_p (x_reg, p)))
2030         break;
2031     }
2032   return p == 0 ? 1 : 0;
2033 }
2034 \f
2035
2036 /* Code generation operations called from machine description file.  */
2037
2038 /* Print the name of register 'regno' in the assembly file.  */
2039 static void
2040 asm_print_register (file, regno)
2041      FILE *file;
2042      int regno;
2043 {
2044   const char *name = reg_names[regno];
2045
2046   if (TARGET_M6812 && name[0] == '*')
2047     name++;
2048
2049   asm_fprintf (file, "%s", name);
2050 }
2051
2052 /* A C compound statement to output to stdio stream STREAM the
2053    assembler syntax for an instruction operand X.  X is an RTL
2054    expression.
2055
2056    CODE is a value that can be used to specify one of several ways
2057    of printing the operand.  It is used when identical operands
2058    must be printed differently depending on the context.  CODE
2059    comes from the `%' specification that was used to request
2060    printing of the operand.  If the specification was just `%DIGIT'
2061    then CODE is 0; if the specification was `%LTR DIGIT' then CODE
2062    is the ASCII code for LTR.
2063
2064    If X is a register, this macro should print the register's name.
2065    The names can be found in an array `reg_names' whose type is
2066    `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
2067
2068    When the machine description has a specification `%PUNCT' (a `%'
2069    followed by a punctuation character), this macro is called with
2070    a null pointer for X and the punctuation character for CODE.
2071
2072    The M68HC11 specific codes are:
2073
2074    'b' for the low part of the operand.
2075    'h' for the high part of the operand
2076        The 'b' or 'h' modifiers have no effect if the operand has
2077        the QImode and is not a S_REG_P (soft register).  If the
2078        operand is a hard register, these two modifiers have no effect.
2079    't' generate the temporary scratch register.  The operand is
2080        ignored.
2081    'T' generate the low-part temporary scratch register.  The operand is
2082        ignored.   */
2083
2084 void
2085 print_operand (file, op, letter)
2086      FILE *file;
2087      rtx op;
2088      int letter;
2089 {
2090   if (letter == 't')
2091     {
2092       asm_print_register (file, SOFT_TMP_REGNUM);
2093       return;
2094     }
2095   else if (letter == 'T')
2096     {
2097       asm_print_register (file, SOFT_TMP_REGNUM);
2098       asm_fprintf (file, "+1");
2099       return;
2100     }
2101   else if (letter == '#')
2102     {
2103       asm_fprintf (file, "%0I");
2104     }
2105
2106   if (GET_CODE (op) == REG)
2107     {
2108       if (letter == 'b' && S_REG_P (op))
2109         {
2110           asm_print_register (file, REGNO (op));
2111           asm_fprintf (file, "+1");
2112         }
2113       else
2114         {
2115           asm_print_register (file, REGNO (op));
2116         }
2117       return;
2118     }
2119
2120   if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
2121     {
2122       if (letter == 'b')
2123         asm_fprintf (file, "%0I%%lo(");
2124       else
2125         asm_fprintf (file, "%0I%%hi(");
2126
2127       output_addr_const (file, op);
2128       asm_fprintf (file, ")");
2129       return;
2130     }
2131
2132   /* Get the low or high part of the operand when 'b' or 'h' modifiers
2133      are specified.  If we already have a QImode, there is nothing to do.  */
2134   if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
2135     {
2136       if (letter == 'b')
2137         {
2138           op = m68hc11_gen_lowpart (QImode, op);
2139         }
2140       else if (letter == 'h')
2141         {
2142           op = m68hc11_gen_highpart (QImode, op);
2143         }
2144     }
2145
2146   if (GET_CODE (op) == MEM)
2147     {
2148       rtx base = XEXP (op, 0);
2149       switch (GET_CODE (base))
2150         {
2151         case PRE_DEC:
2152           if (TARGET_M6812)
2153             {
2154               asm_fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
2155               asm_print_register (file, REGNO (XEXP (base, 0)));
2156             }
2157           else
2158             abort ();
2159           break;
2160
2161         case POST_DEC:
2162           if (TARGET_M6812)
2163             {
2164               asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
2165               asm_print_register (file, REGNO (XEXP (base, 0)));
2166               asm_fprintf (file, "-");
2167             }
2168           else
2169             abort ();
2170           break;
2171
2172         case POST_INC:
2173           if (TARGET_M6812)
2174             {
2175               asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
2176               asm_print_register (file, REGNO (XEXP (base, 0)));
2177               asm_fprintf (file, "+");
2178             }
2179           else
2180             abort ();
2181           break;
2182
2183         case PRE_INC:
2184           if (TARGET_M6812)
2185             {
2186               asm_fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
2187               asm_print_register (file, REGNO (XEXP (base, 0)));
2188             }
2189           else
2190             abort ();
2191           break;
2192
2193         default:
2194           output_address (base);
2195           break;
2196         }
2197     }
2198   else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
2199     {
2200       REAL_VALUE_TYPE r;
2201       REAL_VALUE_FROM_CONST_DOUBLE (r, op);
2202       ASM_OUTPUT_FLOAT_OPERAND (letter, file, r);
2203     }
2204   else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == XFmode)
2205     {
2206       REAL_VALUE_TYPE r;
2207       REAL_VALUE_FROM_CONST_DOUBLE (r, op);
2208       ASM_OUTPUT_LONG_DOUBLE_OPERAND (file, r);
2209     }
2210   else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
2211     {
2212       REAL_VALUE_TYPE r;
2213       REAL_VALUE_FROM_CONST_DOUBLE (r, op);
2214       ASM_OUTPUT_DOUBLE_OPERAND (file, r);
2215     }
2216   else
2217     {
2218       int need_parenthesize = 0;
2219
2220       if (letter != 'i')
2221         asm_fprintf (file, "%0I");
2222       else
2223         need_parenthesize = must_parenthesize (op);
2224
2225       if (need_parenthesize)
2226         asm_fprintf (file, "(");
2227
2228       output_addr_const (file, op);
2229       if (need_parenthesize)
2230         asm_fprintf (file, ")");
2231     }
2232 }
2233
2234 /* Returns true if the operand 'op' must be printed with parenthesis
2235    arround it.  This must be done only if there is a symbol whose name
2236    is a processor register.  */
2237 static int
2238 must_parenthesize (op)
2239      rtx op;
2240 {
2241   const char *name;
2242
2243   switch (GET_CODE (op))
2244     {
2245     case SYMBOL_REF:
2246       name = XSTR (op, 0);
2247       /* Avoid a conflict between symbol name and a possible
2248          register.  */
2249       return (strcasecmp (name, "a") == 0
2250               || strcasecmp (name, "b") == 0
2251               || strcasecmp (name, "d") == 0
2252               || strcasecmp (name, "x") == 0
2253               || strcasecmp (name, "y") == 0
2254               || strcasecmp (name, "pc") == 0
2255               || strcasecmp (name, "sp") == 0
2256               || strcasecmp (name, "ccr") == 0) ? 1 : 0;
2257
2258     case PLUS:
2259     case MINUS:
2260       return must_parenthesize (XEXP (op, 0))
2261         || must_parenthesize (XEXP (op, 1));
2262
2263     case MEM:
2264     case CONST:
2265     case ZERO_EXTEND:
2266     case SIGN_EXTEND:
2267       return must_parenthesize (XEXP (op, 0));
2268
2269     case CONST_DOUBLE:
2270     case CONST_INT:
2271     case LABEL_REF:
2272     case CODE_LABEL:
2273     default:
2274       return 0;
2275     }
2276 }
2277
2278 /* A C compound statement to output to stdio stream STREAM the
2279    assembler syntax for an instruction operand that is a memory
2280    reference whose address is ADDR.  ADDR is an RTL expression.  */
2281
2282 void
2283 print_operand_address (file, addr)
2284      FILE *file;
2285      rtx addr;
2286 {
2287   rtx base;
2288   rtx offset;
2289   int need_parenthesis = 0;
2290
2291   switch (GET_CODE (addr))
2292     {
2293     case REG:
2294       if (!REG_P (addr) || !REG_OK_FOR_BASE_STRICT_P (addr))
2295         abort ();
2296
2297       asm_fprintf (file, "0,");
2298       asm_print_register (file, REGNO (addr));
2299       break;
2300
2301     case MEM:
2302       base = XEXP (addr, 0);
2303       switch (GET_CODE (base))
2304         {
2305         case PRE_DEC:
2306           if (TARGET_M6812)
2307             {
2308               asm_fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
2309               asm_print_register (file, REGNO (XEXP (base, 0)));
2310             }
2311           else
2312             abort ();
2313           break;
2314
2315         case POST_DEC:
2316           if (TARGET_M6812)
2317             {
2318               asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
2319               asm_print_register (file, REGNO (XEXP (base, 0)));
2320               asm_fprintf (file, "-");
2321             }
2322           else
2323             abort ();
2324           break;
2325
2326         case POST_INC:
2327           if (TARGET_M6812)
2328             {
2329               asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
2330               asm_print_register (file, REGNO (XEXP (base, 0)));
2331               asm_fprintf (file, "+");
2332             }
2333           else
2334             abort ();
2335           break;
2336
2337         case PRE_INC:
2338           if (TARGET_M6812)
2339             {
2340               asm_fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
2341               asm_print_register (file, REGNO (XEXP (base, 0)));
2342             }
2343           else
2344             abort ();
2345           break;
2346
2347         default:
2348           need_parenthesis = must_parenthesize (base);
2349           if (need_parenthesis)
2350             asm_fprintf (file, "(");
2351
2352           output_addr_const (file, base);
2353           if (need_parenthesis)
2354             asm_fprintf (file, ")");
2355           break;
2356         }
2357       break;
2358
2359     case PLUS:
2360       base = XEXP (addr, 0);
2361       offset = XEXP (addr, 1);
2362       if (!G_REG_P (base) && G_REG_P (offset))
2363         {
2364           base = XEXP (addr, 1);
2365           offset = XEXP (addr, 0);
2366         }
2367       if ((CONSTANT_ADDRESS_P (base)) && (CONSTANT_ADDRESS_P (offset)))
2368         {
2369           need_parenthesis = must_parenthesize (addr);
2370
2371           if (need_parenthesis)
2372             asm_fprintf (file, "(");
2373
2374           output_addr_const (file, base);
2375           asm_fprintf (file, "+");
2376           output_addr_const (file, offset);
2377           if (need_parenthesis)
2378             asm_fprintf (file, ")");
2379         }
2380       else if (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base))
2381         {
2382           if (REG_P (offset))
2383             {
2384               if (TARGET_M6812)
2385                 {
2386                   asm_print_register (file, REGNO (offset));
2387                   asm_fprintf (file, ",");
2388                   asm_print_register (file, REGNO (base));
2389                 }
2390               else
2391                 abort ();
2392             }
2393           else
2394             {
2395               output_addr_const (file, offset);
2396               asm_fprintf (file, ",");
2397               asm_print_register (file, REGNO (base));
2398             }
2399         }
2400       else
2401         {
2402           abort ();
2403         }
2404       break;
2405
2406     default:
2407       if (GET_CODE (addr) == CONST_INT
2408           && INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
2409         {
2410           asm_fprintf (file, "%d", INTVAL (addr));
2411         }
2412       else
2413         {
2414           need_parenthesis = must_parenthesize (addr);
2415           if (need_parenthesis)
2416             asm_fprintf (file, "(");
2417
2418           output_addr_const (file, addr);
2419           if (need_parenthesis)
2420             asm_fprintf (file, ")");
2421         }
2422       break;
2423     }
2424 }
2425 \f
2426
2427 /* Splitting of some instructions.  */
2428
2429 static rtx
2430 m68hc11_expand_compare (code, op0, op1)
2431      enum rtx_code code;
2432      rtx op0, op1;
2433 {
2434   rtx ret = 0;
2435
2436   if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
2437     abort ();
2438   else
2439     {
2440       emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
2441                               gen_rtx_COMPARE (VOIDmode, op0, op1)));
2442       ret = gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx);
2443     }
2444
2445   return ret;
2446 }
2447
2448 rtx
2449 m68hc11_expand_compare_and_branch (code, op0, op1, label)
2450      enum rtx_code code;
2451      rtx op0, op1, label;
2452 {
2453   rtx tmp;
2454
2455   switch (GET_MODE (op0))
2456     {
2457     case QImode:
2458     case HImode:
2459       tmp = m68hc11_expand_compare (code, op0, op1);
2460       tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
2461                                   gen_rtx_LABEL_REF (VOIDmode, label),
2462                                   pc_rtx);
2463       emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
2464       return 0;
2465 #if 0
2466
2467       /* SCz: from i386.c  */
2468     case SFmode:
2469     case DFmode:
2470       /* Don't expand the comparison early, so that we get better code
2471          when jump or whoever decides to reverse the comparison.  */
2472       {
2473         rtvec vec;
2474         int use_fcomi;
2475
2476         code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
2477                                                 &m68hc11_compare_op1);
2478
2479         tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
2480                               m68hc11_compare_op0, m68hc11_compare_op1);
2481         tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
2482                                     gen_rtx_LABEL_REF (VOIDmode, label),
2483                                     pc_rtx);
2484         tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
2485
2486         use_fcomi = ix86_use_fcomi_compare (code);
2487         vec = rtvec_alloc (3 + !use_fcomi);
2488         RTVEC_ELT (vec, 0) = tmp;
2489         RTVEC_ELT (vec, 1)
2490           = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
2491         RTVEC_ELT (vec, 2)
2492           = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
2493         if (!use_fcomi)
2494           RTVEC_ELT (vec, 3)
2495             = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
2496
2497         emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
2498         return;
2499       }
2500 #endif
2501
2502     case SImode:
2503       /* Expand SImode branch into multiple compare+branch.  */
2504       {
2505         rtx lo[2], hi[2], label2;
2506         enum rtx_code code1, code2, code3;
2507
2508         if (CONSTANT_P (op0) && !CONSTANT_P (op1))
2509           {
2510             tmp = op0;
2511             op0 = op1;
2512             op1 = tmp;
2513             code = swap_condition (code);
2514           }
2515         lo[0] = m68hc11_gen_lowpart (HImode, op0);
2516         lo[1] = m68hc11_gen_lowpart (HImode, op1);
2517         hi[0] = m68hc11_gen_highpart (HImode, op0);
2518         hi[1] = m68hc11_gen_highpart (HImode, op1);
2519
2520         /* Otherwise, if we are doing less-than, op1 is a constant and the
2521            low word is zero, then we can just examine the high word.  */
2522
2523         if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
2524             && (code == LT || code == LTU))
2525           {
2526             return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
2527                                                       label);
2528           }
2529
2530         /* Otherwise, we need two or three jumps.  */
2531
2532         label2 = gen_label_rtx ();
2533
2534         code1 = code;
2535         code2 = swap_condition (code);
2536         code3 = unsigned_condition (code);
2537
2538         switch (code)
2539           {
2540           case LT:
2541           case GT:
2542           case LTU:
2543           case GTU:
2544             break;
2545
2546           case LE:
2547             code1 = LT;
2548             code2 = GT;
2549             break;
2550           case GE:
2551             code1 = GT;
2552             code2 = LT;
2553             break;
2554           case LEU:
2555             code1 = LTU;
2556             code2 = GTU;
2557             break;
2558           case GEU:
2559             code1 = GTU;
2560             code2 = LTU;
2561             break;
2562
2563           case EQ:
2564             code1 = NIL;
2565             code2 = NE;
2566             break;
2567           case NE:
2568             code2 = NIL;
2569             break;
2570
2571           default:
2572             abort ();
2573           }
2574
2575         /*
2576          * a < b =>
2577          *    if (hi(a) < hi(b)) goto true;
2578          *    if (hi(a) > hi(b)) goto false;
2579          *    if (lo(a) < lo(b)) goto true;
2580          *  false:
2581          */
2582         if (code1 != NIL)
2583           m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
2584         if (code2 != NIL)
2585           m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
2586
2587         m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
2588
2589         if (code2 != NIL)
2590           emit_label (label2);
2591         return 0;
2592       }
2593
2594     default:
2595       abort ();
2596     }
2597   return 0;
2598 }
2599
2600
2601 /* Split a DI, SI or HI move into several smaller move operations.
2602    The scratch register 'scratch' is used as a temporary to load
2603    store intermediate values.  It must be a hard register.  */
2604 void
2605 m68hc11_split_move (to, from, scratch)
2606      rtx to, from, scratch;
2607 {
2608   rtx low_to, low_from;
2609   rtx high_to, high_from;
2610   enum machine_mode mode;
2611   int offset = 0;
2612
2613   mode = GET_MODE (to);
2614   if (GET_MODE_SIZE (mode) == 8)
2615     mode = SImode;
2616   else if (GET_MODE_SIZE (mode) == 4)
2617     mode = HImode;
2618   else
2619     mode = QImode;
2620
2621   if (TARGET_M6812
2622       && IS_STACK_PUSH (to)
2623       && reg_mentioned_p (gen_rtx (REG, HImode, HARD_SP_REGNUM), from))
2624     {
2625       if (mode == SImode)
2626         {
2627           offset = 4;
2628         }
2629       else if (mode == HImode)
2630         {
2631           offset = 2;
2632         }
2633       else
2634         offset = 0;
2635     }
2636
2637   low_to = m68hc11_gen_lowpart (mode, to);
2638   high_to = m68hc11_gen_highpart (mode, to);
2639
2640   low_from = m68hc11_gen_lowpart (mode, from);
2641   if (mode == SImode && GET_CODE (from) == CONST_INT)
2642     {
2643       if (INTVAL (from) >= 0)
2644         high_from = const0_rtx;
2645       else
2646         high_from = constm1_rtx;
2647     }
2648   else
2649     high_from = m68hc11_gen_highpart (mode, from);
2650
2651   if (offset)
2652     {
2653       high_from = adjust_address (high_from, mode, offset);
2654       low_from = high_from;
2655     }
2656   if (mode == SImode)
2657     {
2658       m68hc11_split_move (low_to, low_from, scratch);
2659       m68hc11_split_move (high_to, high_from, scratch);
2660     }
2661   else if (H_REG_P (to) || H_REG_P (from)
2662            || (TARGET_M6812
2663                && (!m68hc11_register_indirect_p (from, GET_MODE (from))
2664                    || m68hc11_small_indexed_indirect_p (from,
2665                                                         GET_MODE (from)))
2666                && (!m68hc11_register_indirect_p (to, GET_MODE (to))
2667                    || m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
2668     {
2669       emit_move_insn (low_to, low_from);
2670       emit_move_insn (high_to, high_from);
2671     }
2672   else
2673     {
2674       rtx insn;
2675
2676       emit_move_insn (scratch, low_from);
2677       insn = emit_move_insn (low_to, scratch);
2678
2679       emit_move_insn (scratch, high_from);
2680       insn = emit_move_insn (high_to, scratch);
2681     }
2682 }
2683
2684 static rtx
2685 simplify_logical (mode, code, operand, result)
2686      enum machine_mode mode;
2687      int code;
2688      rtx operand;
2689      rtx *result;
2690 {
2691   int val;
2692   int mask;
2693
2694   *result = 0;
2695   if (GET_CODE (operand) != CONST_INT)
2696     return operand;
2697
2698   if (mode == HImode)
2699     mask = 0x0ffff;
2700   else
2701     mask = 0x0ff;
2702
2703   val = INTVAL (operand);
2704   switch (code)
2705     {
2706     case IOR:
2707       if ((val & mask) == 0)
2708         return 0;
2709       if ((val & mask) == mask)
2710         *result = constm1_rtx;
2711       break;
2712
2713     case AND:
2714       if ((val & mask) == 0)
2715         *result = const0_rtx;
2716       if ((val & mask) == mask)
2717         return 0;
2718       break;
2719
2720     case XOR:
2721       if ((val & mask) == 0)
2722         return 0;
2723       break;
2724     }
2725   return operand;
2726 }
2727
2728 static void
2729 m68hc11_emit_logical (mode, code, operands)
2730      enum machine_mode mode;
2731      int code;
2732      rtx *operands;
2733 {
2734   rtx result;
2735   int need_copy;
2736
2737   need_copy = (rtx_equal_p (operands[0], operands[1])
2738                || rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
2739
2740   operands[1] = simplify_logical (mode, code, operands[1], &result);
2741   operands[2] = simplify_logical (mode, code, operands[2], &result);
2742
2743   if (result && GET_CODE (result) == CONST_INT)
2744     {
2745       if (!H_REG_P (operands[0]) && operands[3]
2746           && (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
2747         {
2748           emit_move_insn (operands[3], result);
2749           emit_move_insn (operands[0], operands[3]);
2750         }
2751       else
2752         {
2753           emit_move_insn (operands[0], result);
2754         }
2755     }
2756   else if (operands[1] != 0 && operands[2] != 0)
2757     {
2758       rtx insn;
2759
2760       if (!H_REG_P (operands[0]) && operands[3])
2761         {
2762           emit_move_insn (operands[3], operands[1]);
2763           emit_insn (gen_rtx (SET, mode,
2764                               operands[3],
2765                               gen_rtx (code, mode,
2766                                        operands[3], operands[2])));
2767           insn = emit_move_insn (operands[0], operands[3]);
2768         }
2769       else
2770         {
2771           insn = emit_insn (gen_rtx (SET, mode,
2772                                      operands[0],
2773                                      gen_rtx (code, mode,
2774                                               operands[0], operands[2])));
2775         }
2776     }
2777
2778   /* The logical operation is similar to a copy. */
2779   else if (need_copy)
2780     {
2781       rtx src;
2782
2783       if (GET_CODE (operands[1]) == CONST_INT)
2784         src = operands[2];
2785       else
2786         src = operands[1];
2787
2788       if (!H_REG_P (operands[0]) && !H_REG_P (src))
2789         {
2790           emit_move_insn (operands[3], src);
2791           emit_move_insn (operands[0], operands[3]);
2792         }
2793       else
2794         {
2795           emit_move_insn (operands[0], src);
2796         }
2797     }
2798 }
2799
2800 void
2801 m68hc11_split_logical (mode, code, operands)
2802      enum machine_mode mode;
2803      int code;
2804      rtx *operands;
2805 {
2806   rtx low[4];
2807   rtx high[4];
2808
2809   low[0] = m68hc11_gen_lowpart (mode, operands[0]);
2810   low[1] = m68hc11_gen_lowpart (mode, operands[1]);
2811   low[2] = m68hc11_gen_lowpart (mode, operands[2]);
2812
2813   high[0] = m68hc11_gen_highpart (mode, operands[0]);
2814
2815   if (mode == SImode && GET_CODE (operands[1]) == CONST_INT)
2816     {
2817       if (INTVAL (operands[1]) >= 0)
2818         high[1] = const0_rtx;
2819       else
2820         high[1] = constm1_rtx;
2821     }
2822   else
2823     high[1] = m68hc11_gen_highpart (mode, operands[1]);
2824
2825   if (mode == SImode && GET_CODE (operands[2]) == CONST_INT)
2826     {
2827       if (INTVAL (operands[2]) >= 0)
2828         high[2] = const0_rtx;
2829       else
2830         high[2] = constm1_rtx;
2831     }
2832   else
2833     high[2] = m68hc11_gen_highpart (mode, operands[2]);
2834
2835   low[3] = operands[3];
2836   high[3] = operands[3];
2837   if (mode == SImode)
2838     {
2839       m68hc11_split_logical (HImode, code, low);
2840       m68hc11_split_logical (HImode, code, high);
2841       return;
2842     }
2843
2844   m68hc11_emit_logical (mode, code, low);
2845   m68hc11_emit_logical (mode, code, high);
2846 }
2847 \f
2848
2849 /* Code generation.  */
2850
2851 void
2852 m68hc11_output_swap (insn, operands)
2853      rtx insn ATTRIBUTE_UNUSED;
2854      rtx operands[];
2855 {
2856   /* We have to be careful with the cc_status.  An address register swap
2857      is generated for some comparison.  The comparison is made with D
2858      but the branch really uses the address register.  See the split
2859      pattern for compare.  The xgdx/xgdy preserve the flags but after
2860      the exchange, the flags will reflect to the value of X and not D.
2861      Tell this by setting the cc_status according to the cc_prev_status.  */
2862   if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
2863     {
2864       if (cc_prev_status.value1 != 0
2865           && (D_REG_P (cc_prev_status.value1)
2866               || X_REG_P (cc_prev_status.value1)))
2867         {
2868           cc_status = cc_prev_status;
2869           if (D_REG_P (cc_status.value1))
2870             cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
2871                                         HARD_X_REGNUM);
2872           else
2873             cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
2874                                         HARD_D_REGNUM);
2875         }
2876       else
2877         CC_STATUS_INIT;
2878
2879       output_asm_insn ("xgdx", operands);
2880     }
2881   else
2882     {
2883       if (cc_prev_status.value1 != 0
2884           && (D_REG_P (cc_prev_status.value1)
2885               || Y_REG_P (cc_prev_status.value1)))
2886         {
2887           cc_status = cc_prev_status;
2888           if (D_REG_P (cc_status.value1))
2889             cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
2890                                         HARD_Y_REGNUM);
2891           else
2892             cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
2893                                         HARD_D_REGNUM);
2894         }
2895       else
2896         CC_STATUS_INIT;
2897
2898       output_asm_insn ("xgdy", operands);
2899     }
2900 }
2901
2902 /* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
2903    This is used to decide whether a move that set flags should be used
2904    instead.  */
2905 int
2906 next_insn_test_reg (insn, reg)
2907      rtx insn;
2908      rtx reg;
2909 {
2910   rtx body;
2911
2912   insn = next_nonnote_insn (insn);
2913   if (GET_CODE (insn) != INSN)
2914     return 0;
2915
2916   body = PATTERN (insn);
2917   if (sets_cc0_p (body) != 1)
2918     return 0;
2919
2920   if (rtx_equal_p (XEXP (body, 1), reg) == 0)
2921     return 0;
2922
2923   return 1;
2924 }
2925
2926 /* Generate the code to move a 16-bit operand into another one.  */
2927
2928 void
2929 m68hc11_gen_movhi (insn, operands)
2930      rtx insn;
2931      rtx *operands;
2932 {
2933   int reg;
2934
2935   /* Move a register or memory to the same location.
2936      This is possible because such insn can appear
2937      in a non-optimizing mode.  */
2938   if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
2939     {
2940       cc_status = cc_prev_status;
2941       return;
2942     }
2943
2944   if (TARGET_M6812)
2945     {
2946       if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
2947         {
2948           cc_status = cc_prev_status;
2949           switch (REGNO (operands[1]))
2950             {
2951             case HARD_X_REGNUM:
2952             case HARD_Y_REGNUM:
2953             case HARD_D_REGNUM:
2954               output_asm_insn ("psh%1", operands);
2955               break;
2956             default:
2957               abort ();
2958             }
2959           return;
2960         }
2961       if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
2962         {
2963           cc_status = cc_prev_status;
2964           switch (REGNO (operands[0]))
2965             {
2966             case HARD_X_REGNUM:
2967             case HARD_Y_REGNUM:
2968             case HARD_D_REGNUM:
2969               output_asm_insn ("pul%0", operands);
2970               break;
2971             default:
2972               abort ();
2973             }
2974           return;
2975         }
2976       if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
2977         {
2978           m68hc11_notice_keep_cc (operands[0]);
2979           output_asm_insn ("tfr\t%1,%0", operands);
2980         }
2981       else if (H_REG_P (operands[0]))
2982         {
2983           if (SP_REG_P (operands[0]))
2984             output_asm_insn ("lds\t%1", operands);
2985           else
2986             output_asm_insn ("ld%0\t%1", operands);
2987         }
2988       else if (H_REG_P (operands[1]))
2989         {
2990           if (SP_REG_P (operands[1]))
2991             output_asm_insn ("sts\t%0", operands);
2992           else
2993             output_asm_insn ("st%1\t%0", operands);
2994         }
2995       else
2996         {
2997           rtx from = operands[1];
2998           rtx to = operands[0];
2999
3000           if ((m68hc11_register_indirect_p (from, GET_MODE (from))
3001                && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
3002               || (m68hc11_register_indirect_p (to, GET_MODE (to))
3003                   && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
3004             {
3005               rtx ops[3];
3006
3007               if (operands[2])
3008                 {
3009                   ops[0] = operands[2];
3010                   ops[1] = from;
3011                   ops[2] = 0;
3012                   m68hc11_gen_movhi (insn, ops);
3013                   ops[0] = to;
3014                   ops[1] = operands[2];
3015                   m68hc11_gen_movhi (insn, ops);
3016                 }
3017               else
3018                 {
3019                   /* !!!! SCz wrong here.  */
3020                   fatal_insn ("Move insn not handled", insn);
3021                 }
3022             }
3023           else
3024             {
3025               if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
3026                 {
3027                   output_asm_insn ("clr\t%h0", operands);
3028                   output_asm_insn ("clr\t%b0", operands);
3029                 }
3030               else
3031                 {
3032                   m68hc11_notice_keep_cc (operands[0]);
3033                   output_asm_insn ("movw\t%1,%0", operands);
3034                 }
3035             }
3036         }
3037       return;
3038     }
3039
3040   if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
3041     {
3042       cc_status = cc_prev_status;
3043       switch (REGNO (operands[0]))
3044         {
3045         case HARD_X_REGNUM:
3046         case HARD_Y_REGNUM:
3047           output_asm_insn ("pul%0", operands);
3048           break;
3049         case HARD_D_REGNUM:
3050           output_asm_insn ("pula", operands);
3051           output_asm_insn ("pulb", operands);
3052           break;
3053         default:
3054           abort ();
3055         }
3056       return;
3057     }
3058   /* Some moves to a hard register are special. Not all of them
3059      are really supported and we have to use a temporary
3060      location to provide them (either the stack of a temp var). */
3061   if (H_REG_P (operands[0]))
3062     {
3063       switch (REGNO (operands[0]))
3064         {
3065         case HARD_D_REGNUM:
3066           if (X_REG_P (operands[1]))
3067             {
3068               if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
3069                 {
3070                   m68hc11_output_swap (insn, operands);
3071                 }
3072               else if (next_insn_test_reg (insn, operands[0]))
3073                 {
3074                   output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
3075                 }
3076               else
3077                 {
3078                   m68hc11_notice_keep_cc (operands[0]);
3079                   output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
3080                 }
3081             }
3082           else if (Y_REG_P (operands[1]))
3083             {
3084               if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
3085                 {
3086                   m68hc11_output_swap (insn, operands);
3087                 }
3088               else
3089                 {
3090                   /* %t means *ZTMP scratch register. */
3091                   output_asm_insn ("sty\t%t1", operands);
3092                   output_asm_insn ("ldd\t%t1", operands);
3093                 }
3094             }
3095           else if (SP_REG_P (operands[1]))
3096             {
3097               CC_STATUS_INIT;
3098               if (ix_reg == 0)
3099                 create_regs_rtx ();
3100               if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
3101                 output_asm_insn ("xgdx", operands);
3102               output_asm_insn ("tsx", operands);
3103               output_asm_insn ("xgdx", operands);
3104             }
3105           else if (IS_STACK_POP (operands[1]))
3106             {
3107               output_asm_insn ("pula\n\tpulb", operands);
3108             }
3109           else if (GET_CODE (operands[1]) == CONST_INT
3110                    && INTVAL (operands[1]) == 0)
3111             {
3112               output_asm_insn ("clra\n\tclrb", operands);
3113             }
3114           else
3115             {
3116               output_asm_insn ("ldd\t%1", operands);
3117             }
3118           break;
3119
3120         case HARD_X_REGNUM:
3121           if (D_REG_P (operands[1]))
3122             {
3123               if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3124                 {
3125                   m68hc11_output_swap (insn, operands);
3126                 }
3127               else if (next_insn_test_reg (insn, operands[0]))
3128                 {
3129                   output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
3130                 }
3131               else
3132                 {
3133                   m68hc11_notice_keep_cc (operands[0]);
3134                   output_asm_insn ("pshb", operands);
3135                   output_asm_insn ("psha", operands);
3136                   output_asm_insn ("pulx", operands);
3137                 }
3138             }
3139           else if (Y_REG_P (operands[1]))
3140             {
3141               /* When both D and Y are dead, use the sequence xgdy, xgdx
3142                  to move Y into X.  The D and Y registers are modified.  */
3143               if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
3144                   && dead_register_here (insn, d_reg))
3145                 {
3146                   output_asm_insn ("xgdy", operands);
3147                   output_asm_insn ("xgdx", operands);
3148                   CC_STATUS_INIT;
3149                 }
3150               else
3151                 {
3152                   output_asm_insn ("sty\t%t1", operands);
3153                   output_asm_insn ("ldx\t%t1", operands);
3154                 }
3155             }
3156           else if (SP_REG_P (operands[1]))
3157             {
3158               /* tsx, tsy preserve the flags */
3159               cc_status = cc_prev_status;
3160               output_asm_insn ("tsx", operands);
3161             }
3162           else
3163             {
3164               output_asm_insn ("ldx\t%1", operands);
3165             }
3166           break;
3167
3168         case HARD_Y_REGNUM:
3169           if (D_REG_P (operands[1]))
3170             {
3171               if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
3172                 {
3173                   m68hc11_output_swap (insn, operands);
3174                 }
3175               else
3176                 {
3177                   output_asm_insn ("std\t%t1", operands);
3178                   output_asm_insn ("ldy\t%t1", operands);
3179                 }
3180             }
3181           else if (X_REG_P (operands[1]))
3182             {
3183               /* When both D and X are dead, use the sequence xgdx, xgdy
3184                  to move X into Y.  The D and X registers are modified.  */
3185               if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
3186                   && dead_register_here (insn, d_reg))
3187                 {
3188                   output_asm_insn ("xgdx", operands);
3189                   output_asm_insn ("xgdy", operands);
3190                   CC_STATUS_INIT;
3191                 }
3192               else
3193                 {
3194                   output_asm_insn ("stx\t%t1", operands);
3195                   output_asm_insn ("ldy\t%t1", operands);
3196                 }
3197             }
3198           else if (SP_REG_P (operands[1]))
3199             {
3200               /* tsx, tsy preserve the flags */
3201               cc_status = cc_prev_status;
3202               output_asm_insn ("tsy", operands);
3203             }
3204           else
3205             {
3206               output_asm_insn ("ldy\t%1", operands);
3207             }
3208           break;
3209
3210         case HARD_SP_REGNUM:
3211           if (D_REG_P (operands[1]))
3212             {
3213               m68hc11_notice_keep_cc (operands[0]);
3214               output_asm_insn ("xgdx", operands);
3215               output_asm_insn ("txs", operands);
3216               output_asm_insn ("xgdx", operands);
3217             }
3218           else if (X_REG_P (operands[1]))
3219             {
3220               /* tys, txs preserve the flags */
3221               cc_status = cc_prev_status;
3222               output_asm_insn ("txs", operands);
3223             }
3224           else if (Y_REG_P (operands[1]))
3225             {
3226               /* tys, txs preserve the flags */
3227               cc_status = cc_prev_status;
3228               output_asm_insn ("tys", operands);
3229             }
3230           else
3231             {
3232               /* lds sets the flags but the des does not.  */
3233               CC_STATUS_INIT;
3234               output_asm_insn ("lds\t%1", operands);
3235               output_asm_insn ("des", operands);
3236             }
3237           break;
3238
3239         default:
3240           fatal_insn ("Invalid register in the move instruction", insn);
3241           break;
3242         }
3243       return;
3244     }
3245   if (SP_REG_P (operands[1]) && REG_P (operands[0])
3246       && REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
3247     {
3248       output_asm_insn ("sts\t%0", operands);
3249       return;
3250     }
3251
3252   if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
3253     {
3254       cc_status = cc_prev_status;
3255       switch (REGNO (operands[1]))
3256         {
3257         case HARD_X_REGNUM:
3258         case HARD_Y_REGNUM:
3259           output_asm_insn ("psh%1", operands);
3260           break;
3261         case HARD_D_REGNUM:
3262           output_asm_insn ("pshb", operands);
3263           output_asm_insn ("psha", operands);
3264           break;
3265         default:
3266           abort ();
3267         }
3268       return;
3269     }
3270
3271   /* Operand 1 must be a hard register.  */
3272   if (!H_REG_P (operands[1]))
3273     {
3274       fatal_insn ("Invalid operand in the instruction", insn);
3275     }
3276
3277   reg = REGNO (operands[1]);
3278   switch (reg)
3279     {
3280     case HARD_D_REGNUM:
3281       output_asm_insn ("std\t%0", operands);
3282       break;
3283
3284     case HARD_X_REGNUM:
3285       output_asm_insn ("stx\t%0", operands);
3286       break;
3287
3288     case HARD_Y_REGNUM:
3289       output_asm_insn ("sty\t%0", operands);
3290       break;
3291
3292     case HARD_SP_REGNUM:
3293       if (ix_reg == 0)
3294         create_regs_rtx ();
3295
3296       if (reg_mentioned_p (ix_reg, operands[0]))
3297         {
3298           output_asm_insn ("sty\t%t0", operands);
3299           output_asm_insn ("tsy", operands);
3300           output_asm_insn ("sty\t%0", operands);
3301           output_asm_insn ("ldy\t%t0", operands);
3302         }
3303       else
3304         {
3305           output_asm_insn ("stx\t%t0", operands);
3306           output_asm_insn ("tsx", operands);
3307           output_asm_insn ("stx\t%0", operands);
3308           output_asm_insn ("ldx\t%t0", operands);
3309         }
3310       CC_STATUS_INIT;
3311       break;
3312
3313     default:
3314       fatal_insn ("Invalid register in the move instruction", insn);
3315       break;
3316     }
3317 }
3318
3319 void
3320 m68hc11_gen_movqi (insn, operands)
3321      rtx insn;
3322      rtx *operands;
3323 {
3324   /* Move a register or memory to the same location.
3325      This is possible because such insn can appear
3326      in a non-optimizing mode. */
3327   if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
3328     {
3329       cc_status = cc_prev_status;
3330       return;
3331     }
3332
3333   if (TARGET_M6812)
3334     {
3335
3336       if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
3337         {
3338           m68hc11_notice_keep_cc (operands[0]);
3339           output_asm_insn ("tfr\t%1,%0", operands);
3340         }
3341       else if (H_REG_P (operands[0]))
3342         {
3343           if (Q_REG_P (operands[0]))
3344             output_asm_insn ("lda%0\t%b1", operands);
3345           else if (D_REG_P (operands[0]))
3346             output_asm_insn ("ldab\t%b1", operands);
3347           else
3348             goto m6811_move;
3349         }
3350       else if (H_REG_P (operands[1]))
3351         {
3352           if (Q_REG_P (operands[1]))
3353             output_asm_insn ("sta%1\t%b0", operands);
3354           else if (D_REG_P (operands[1]))
3355             output_asm_insn ("stab\t%b0", operands);
3356           else
3357             goto m6811_move;
3358         }
3359       else
3360         {
3361           rtx from = operands[1];
3362           rtx to = operands[0];
3363
3364           if ((m68hc11_register_indirect_p (from, GET_MODE (from))
3365                && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
3366               || (m68hc11_register_indirect_p (to, GET_MODE (to))
3367                   && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
3368             {
3369               rtx ops[3];
3370
3371               if (operands[2])
3372                 {
3373                   ops[0] = operands[2];
3374                   ops[1] = from;
3375                   ops[2] = 0;
3376                   m68hc11_gen_movqi (insn, ops);
3377                   ops[0] = to;
3378                   ops[1] = operands[2];
3379                   m68hc11_gen_movqi (insn, ops);
3380                 }
3381               else
3382                 {
3383                   /* !!!! SCz wrong here.  */
3384                   fatal_insn ("Move insn not handled", insn);
3385                 }
3386             }
3387           else
3388             {
3389               if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
3390                 {
3391                   output_asm_insn ("clr\t%b0", operands);
3392                 }
3393               else
3394                 {
3395                   m68hc11_notice_keep_cc (operands[0]);
3396                   output_asm_insn ("movb\t%b1,%b0", operands);
3397                 }
3398             }
3399         }
3400       return;
3401     }
3402
3403  m6811_move:
3404   if (H_REG_P (operands[0]))
3405     {
3406       switch (REGNO (operands[0]))
3407         {
3408         case HARD_B_REGNUM:
3409         case HARD_D_REGNUM:
3410           if (X_REG_P (operands[1]))
3411             {
3412               if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
3413                 {
3414                   m68hc11_output_swap (insn, operands);
3415                 }
3416               else
3417                 {
3418                   output_asm_insn ("stx\t%t1", operands);
3419                   output_asm_insn ("ldab\t%T0", operands);
3420                 }
3421             }
3422           else if (Y_REG_P (operands[1]))
3423             {
3424               if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
3425                 {
3426                   m68hc11_output_swap (insn, operands);
3427                 }
3428               else
3429                 {
3430                   output_asm_insn ("sty\t%t1", operands);
3431                   output_asm_insn ("ldab\t%T0", operands);
3432                 }
3433             }
3434           else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
3435                    && !DA_REG_P (operands[1]))
3436             {
3437               output_asm_insn ("ldab\t%b1", operands);
3438             }
3439           else if (DA_REG_P (operands[1]))