OSDN Git Service

9de076883054512974edf20249b13c9046426d49
[pf3gnuchains/gcc-fork.git] / gcc / config / stormy16 / stormy16.c
1 /* Xstormy16 target functions.
2    Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3    Contributed by Red Hat, Inc.
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 #include "config.h"
23 #include "system.h"
24 #include "rtl.h"
25 #include "regs.h"
26 #include "hard-reg-set.h"
27 #include "real.h"
28 #include "insn-config.h"
29 #include "conditions.h"
30 #include "insn-flags.h"
31 #include "output.h"
32 #include "insn-attr.h"
33 #include "flags.h"
34 #include "recog.h"
35 #include "toplev.h"
36 #include "obstack.h"
37 #include "tree.h"
38 #include "expr.h"
39 #include "optabs.h"
40 #include "output.h"
41 #include "except.h"
42 #include "function.h"
43 #include "target.h"
44 #include "target-def.h"
45 #include "tm_p.h"
46
47 static rtx emit_addhi3_postreload PARAMS ((rtx, rtx, rtx));
48 static void xstormy16_asm_out_constructor PARAMS ((rtx, int));
49 static void xstormy16_asm_out_destructor PARAMS ((rtx, int));
50
51 /* Define the information needed to generate branch and scc insns.  This is
52    stored from the compare operation.  */
53 struct rtx_def * xstormy16_compare_op0;
54 struct rtx_def * xstormy16_compare_op1;
55
56 /* Return 1 if this is a LT, GE, LTU, or GEU operator.  */
57
58 int
59 xstormy16_ineqsi_operator (op, mode)
60     register rtx op;
61     enum machine_mode mode;
62 {
63   enum rtx_code code = GET_CODE (op);
64   
65   return ((mode == VOIDmode || GET_MODE (op) == mode)
66           && (code == LT || code == GE || code == LTU || code == GEU));
67 }
68
69 /* Return 1 if this is an EQ or NE operator.  */
70
71 int
72 equality_operator (op, mode)
73     register rtx op;
74     enum machine_mode mode;
75 {
76   return ((mode == VOIDmode || GET_MODE (op) == mode)
77           && (GET_CODE (op) == EQ || GET_CODE (op) == NE));
78 }
79
80 /* Return 1 if this is a comparison operator but not an EQ or NE operator.  */
81
82 int
83 inequality_operator (op, mode)
84     register rtx op;
85     enum machine_mode mode;
86 {
87   return comparison_operator (op, mode) && ! equality_operator (op, mode);
88 }
89
90 /* Branches are handled as follows:
91
92    1. HImode compare-and-branches.  The machine supports these
93       natively, so the appropriate pattern is emitted directly.
94
95    2. SImode EQ and NE.  These are emitted as pairs of HImode
96       compare-and-branches.      
97
98    3. SImode LT, GE, LTU and GEU.  These are emitted as a sequence
99       of a SImode subtract followed by a branch (not a compare-and-branch),
100       like this:
101       sub
102       sbc
103       blt
104
105    4. SImode GT, LE, GTU, LEU.  These are emitted as a sequence like:
106       sub
107       sbc
108       blt
109       or
110       bne
111 */
112
113 /* Emit a branch of kind CODE to location LOC.  */
114
115 void
116 xstormy16_emit_cbranch (code, loc)
117      enum rtx_code code;
118      rtx loc;
119 {
120   rtx op0 = xstormy16_compare_op0;
121   rtx op1 = xstormy16_compare_op1;
122   rtx condition_rtx, loc_ref, branch, cy_clobber;
123   rtvec vec;
124   enum machine_mode mode;
125   
126   mode = GET_MODE (op0);
127   if (mode != HImode && mode != SImode)
128     abort ();
129
130   if (mode == SImode
131       && (code == GT || code == LE || code == GTU || code == LEU))
132     {
133       int unsigned_p = (code == GTU || code == LEU);
134       int gt_p = (code == GT || code == GTU);
135       rtx lab = NULL_RTX;
136       
137       if (gt_p)
138         lab = gen_label_rtx ();
139       xstormy16_emit_cbranch (unsigned_p ? LTU : LT, gt_p ? lab : loc);
140       /* This should be generated as a comparison against the temporary
141          created by the previous insn, but reload can't handle that.  */
142       xstormy16_emit_cbranch (gt_p ? NE : EQ, loc);
143       if (gt_p)
144         emit_label (lab);
145       return;
146     }
147   else if (mode == SImode 
148            && (code == NE || code == EQ)
149            && op1 != const0_rtx)
150     {
151       rtx lab = NULL_RTX;
152       int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
153       int i;
154       
155       if (code == EQ)
156         lab = gen_label_rtx ();
157       
158       for (i = 0; i < num_words - 1; i++)
159         {
160           xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, 
161                                                       i * UNITS_PER_WORD);
162           xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, 
163                                                       i * UNITS_PER_WORD);
164           xstormy16_emit_cbranch (NE, code == EQ ? lab : loc);
165         }
166       xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, 
167                                                   i * UNITS_PER_WORD);
168       xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, 
169                                                   i * UNITS_PER_WORD);
170       xstormy16_emit_cbranch (code, loc);
171
172       if (code == EQ)
173         emit_label (lab);
174       return;
175     }
176
177   /* We can't allow reload to try to generate any reload after a branch,
178      so when some register must match we must make the temporary ourselves.  */
179   if (mode != HImode)
180     {
181       rtx tmp;
182       tmp = gen_reg_rtx (mode);
183       emit_move_insn (tmp, op0);
184       op0 = tmp;
185     }
186
187   condition_rtx = gen_rtx (code, mode, op0, op1);
188   loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
189   branch = gen_rtx_SET (VOIDmode, pc_rtx,
190                         gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
191                                               loc_ref, pc_rtx));
192
193   cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (BImode));
194
195   if (mode == HImode)
196     vec = gen_rtvec (2, branch, cy_clobber);
197   else if (code == NE || code == EQ)
198     vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0));
199   else
200     {
201       rtx sub;
202 #if 0
203       sub = gen_rtx_SET (VOIDmode, op0, gen_rtx_MINUS (SImode, op0, op1));
204 #else
205       sub = gen_rtx_CLOBBER (SImode, op0);
206 #endif
207       vec = gen_rtvec (3, branch, sub, cy_clobber);
208     }
209
210   emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
211 }
212
213 /* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split
214    the arithmetic operation.  Most of the work is done by
215    xstormy16_expand_arith.  */
216
217 void
218 xstormy16_split_cbranch (mode, label, comparison, dest, carry)
219      enum machine_mode mode;
220      rtx label;
221      rtx comparison;
222      rtx dest;
223      rtx carry;
224 {
225   rtx op0 = XEXP (comparison, 0);
226   rtx op1 = XEXP (comparison, 1);
227   rtx seq;
228   rtx compare;
229   
230   start_sequence ();
231   xstormy16_expand_arith (mode, COMPARE, dest, op0, op1, carry);
232   seq = gen_sequence ();
233   end_sequence ();
234   compare = SET_SRC (XVECEXP (PATTERN (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1)),
235                               0, 0));
236   PUT_CODE (XEXP (compare, 0), GET_CODE (comparison));
237   XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label);
238   emit_insn (seq);
239 }
240
241
242 /* Return the string to output a conditional branch to LABEL, which is
243    the operand number of the label.
244
245    OP is the conditional expression, or NULL for branch-always.
246
247    REVERSED is non-zero if we should reverse the sense of the comparison.
248
249    INSN is the insn.  */
250
251 char *
252 xstormy16_output_cbranch_hi (op, label, reversed, insn)
253      rtx op;
254      const char * label;
255      int reversed;
256      rtx insn;
257 {
258   static char string[64];
259   int need_longbranch = (op != NULL_RTX
260                          ? get_attr_length (insn) == 8
261                          : get_attr_length (insn) == 4);
262   int really_reversed = reversed ^ need_longbranch;
263   const char *ccode;
264   const char *template;
265   const char *operands;
266   enum rtx_code code;
267   
268   if (! op)
269     {
270       if (need_longbranch)
271         ccode = "jmpf";
272       else
273         ccode = "br";
274       sprintf (string, "%s %s", ccode, label);
275       return string;
276     }
277
278   code = GET_CODE (op);
279
280   if (GET_CODE (XEXP (op, 0)) != REG)
281     {
282       code = swap_condition (code);
283       operands = "%3,%2";
284     }
285   else
286       operands = "%2,%3";
287
288   /* Work out which way this really branches.  */
289   if (really_reversed)
290     code = reverse_condition (code);
291
292   switch (code)
293     {
294     case EQ:   ccode = "z";   break;
295     case NE:   ccode = "nz";  break;
296     case GE:   ccode = "ge";  break;
297     case LT:   ccode = "lt";  break;
298     case GT:   ccode = "gt";  break;
299     case LE:   ccode = "le";  break;
300     case GEU:  ccode = "nc";  break;
301     case LTU:  ccode = "c";   break;
302     case GTU:  ccode = "hi";  break;
303     case LEU:  ccode = "ls";  break;
304       
305     default:
306       abort ();
307     }
308
309   if (need_longbranch)
310     template = "b%s %s,.+8 | jmpf %s";
311   else
312     template = "b%s %s,%s";
313   sprintf (string, template, ccode, operands, label);
314   
315   return string;
316 }
317
318 /* Return the string to output a conditional branch to LABEL, which is
319    the operand number of the label, but suitable for the tail of a
320    SImode branch.
321
322    OP is the conditional expression (OP is never NULL_RTX).
323
324    REVERSED is non-zero if we should reverse the sense of the comparison.
325
326    INSN is the insn.  */
327
328 char *
329 xstormy16_output_cbranch_si (op, label, reversed, insn)
330      rtx op;
331      const char * label;
332      int reversed;
333      rtx insn;
334 {
335   static char string[64];
336   int need_longbranch = get_attr_length (insn) >= 8;
337   int really_reversed = reversed ^ need_longbranch;
338   const char *ccode;
339   const char *template;
340   char prevop[16];
341   enum rtx_code code;
342   
343   code = GET_CODE (op);
344
345   /* Work out which way this really branches.  */
346   if (really_reversed)
347     code = reverse_condition (code);
348
349   switch (code)
350     {
351     case EQ:   ccode = "z";   break;
352     case NE:   ccode = "nz";  break;
353     case GE:   ccode = "ge";  break;
354     case LT:   ccode = "lt";  break;
355     case GEU:  ccode = "nc";  break;
356     case LTU:  ccode = "c";   break;
357
358       /* The missing codes above should never be generated.  */
359     default:
360       abort ();
361     }
362
363   switch (code)
364     {
365     case EQ: case NE:
366       {
367         int regnum;
368         
369         if (GET_CODE (XEXP (op, 0)) != REG)
370           abort ();
371       
372         regnum = REGNO (XEXP (op, 0));
373         sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]);
374       }
375       break;
376
377     case GE: case LT: case GEU: case LTU:
378       strcpy (prevop, "sbc %2,%3");
379       break;
380
381     default:
382       abort ();
383     }
384
385   if (need_longbranch)
386     template = "%s | b%s .+6 | jmpf %s";
387   else
388     template = "%s | b%s %s";
389   sprintf (string, template, prevop, ccode, label);
390   
391   return string;
392 }
393 \f
394 /* Many machines have some registers that cannot be copied directly to or from
395    memory or even from other types of registers.  An example is the `MQ'
396    register, which on most machines, can only be copied to or from general
397    registers, but not memory.  Some machines allow copying all registers to and
398    from memory, but require a scratch register for stores to some memory
399    locations (e.g., those with symbolic address on the RT, and those with
400    certain symbolic address on the Sparc when compiling PIC).  In some cases,
401    both an intermediate and a scratch register are required.
402
403    You should define these macros to indicate to the reload phase that it may
404    need to allocate at least one register for a reload in addition to the
405    register to contain the data.  Specifically, if copying X to a register
406    CLASS in MODE requires an intermediate register, you should define
407    `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
408    whose registers can be used as intermediate registers or scratch registers.
409
410    If copying a register CLASS in MODE to X requires an intermediate or scratch
411    register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
412    largest register class required.  If the requirements for input and output
413    reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
414    instead of defining both macros identically.
415
416    The values returned by these macros are often `GENERAL_REGS'.  Return
417    `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
418    to or from a register of CLASS in MODE without requiring a scratch register.
419    Do not define this macro if it would always return `NO_REGS'.
420
421    If a scratch register is required (either with or without an intermediate
422    register), you should define patterns for `reload_inM' or `reload_outM', as
423    required..  These patterns, which will normally be implemented with a
424    `define_expand', should be similar to the `movM' patterns, except that
425    operand 2 is the scratch register.
426
427    Define constraints for the reload register and scratch register that contain
428    a single register class.  If the original reload register (whose class is
429    CLASS) can meet the constraint given in the pattern, the value returned by
430    these macros is used for the class of the scratch register.  Otherwise, two
431    additional reload registers are required.  Their classes are obtained from
432    the constraints in the insn pattern.
433
434    X might be a pseudo-register or a `subreg' of a pseudo-register, which could
435    either be in a hard register or in memory.  Use `true_regnum' to find out;
436    it will return -1 if the pseudo is in memory and the hard register number if
437    it is in a register.
438
439    These macros should not be used in the case where a particular class of
440    registers can only be copied to memory and not to another class of
441    registers.  In that case, secondary reload registers are not needed and
442    would not be helpful.  Instead, a stack location must be used to perform the
443    copy and the `movM' pattern should use memory as a intermediate storage.
444    This case often occurs between floating-point and general registers.  */
445
446 enum reg_class
447 xstormy16_secondary_reload_class (class, mode, x)
448      enum reg_class class;
449      enum machine_mode mode;
450      rtx x;
451 {
452   /* This chip has the interesting property that only the first eight
453      registers can be moved to/from memory.  */
454   if ((GET_CODE (x) == MEM
455        || ((GET_CODE (x) == SUBREG || GET_CODE (x) == REG)
456            && (true_regnum (x) == -1
457                || true_regnum (x) >= FIRST_PSEUDO_REGISTER)))
458       && ! reg_class_subset_p (class, EIGHT_REGS))
459     return EIGHT_REGS;
460
461   /* When reloading a PLUS, the carry register will be required
462      unless the inc or dec instructions can be used.  */
463   if (xstormy16_carry_plus_operand (x, mode))
464     return CARRY_REGS;
465
466   return NO_REGS;
467 }
468
469 /* Recognise a PLUS that needs the carry register.  */
470 int
471 xstormy16_carry_plus_operand (x, mode)
472      rtx x;
473      enum machine_mode mode ATTRIBUTE_UNUSED;
474 {
475   return (GET_CODE (x) == PLUS
476           && GET_CODE (XEXP (x, 1)) == CONST_INT
477           && (INTVAL (XEXP (x, 1)) < -4 || INTVAL (XEXP (x, 1)) > 4));
478 }
479
480
481 enum reg_class
482 xstormy16_preferred_reload_class (x, class)
483      enum reg_class class;
484      rtx x;
485 {
486   if (class == GENERAL_REGS
487       && GET_CODE (x) == MEM)
488     return EIGHT_REGS;
489
490   return class;
491 }
492
493 #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET)                         \
494  (GET_CODE (X) == CONST_INT                                             \
495   && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
496
497 #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET)                        \
498  (GET_CODE (X) == CONST_INT                                              \
499   && INTVAL (X) + (OFFSET) >= 0                                          \
500   && INTVAL (X) + (OFFSET) < 0x8000                                      \
501   && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
502
503 int
504 xstormy16_legitimate_address_p (mode, x, strict)
505      enum machine_mode mode ATTRIBUTE_UNUSED;
506      rtx x;
507      int strict;
508 {
509   if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0))
510     return 1;
511
512   if (GET_CODE (x) == PLUS
513       && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0))
514     x = XEXP (x, 0);
515   
516   if (GET_CODE (x) == POST_INC
517       || GET_CODE (x) == PRE_DEC)
518     x = XEXP (x, 0);
519   
520   if (GET_CODE (x) == REG && REGNO_OK_FOR_BASE_P (REGNO (x))
521       && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER))
522     return 1;
523   
524   return 0;
525 }
526
527 /* Return nonzero if memory address X (an RTX) can have different
528    meanings depending on the machine mode of the memory reference it
529    is used for or if the address is valid for some modes but not
530    others.
531
532    Autoincrement and autodecrement addresses typically have mode-dependent
533    effects because the amount of the increment or decrement is the size of the
534    operand being addressed.  Some machines have other mode-dependent addresses.
535    Many RISC machines have no mode-dependent addresses.
536
537    You may assume that ADDR is a valid address for the machine.  
538    
539    On this chip, this is true if the address is valid with an offset
540    of 0 but not of 6, because in that case it cannot be used as an
541    address for DImode or DFmode, or if the address is a post-increment
542    or pre-decrement address.  */
543 int
544 xstormy16_mode_dependent_address_p (x)
545      rtx x;
546 {
547   if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)
548       && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6))
549     return 1;
550   
551   if (GET_CODE (x) == PLUS
552       && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)
553       && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6))
554     return 1;
555
556   if (GET_CODE (x) == PLUS)
557     x = XEXP (x, 0);
558
559   if (GET_CODE (x) == POST_INC
560       || GET_CODE (x) == PRE_DEC)
561     return 1;
562
563   return 0;
564 }
565
566 /* A C expression that defines the optional machine-dependent constraint
567    letters (`Q', `R', `S', `T', `U') that can be used to segregate specific
568    types of operands, usually memory references, for the target machine.
569    Normally this macro will not be defined.  If it is required for a particular
570    target machine, it should return 1 if VALUE corresponds to the operand type
571    represented by the constraint letter C.  If C is not defined as an extra
572    constraint, the value returned should be 0 regardless of VALUE.  */
573 int
574 xstormy16_extra_constraint_p (x, c)
575      rtx x;
576      int c;
577 {
578   switch (c)
579     {
580       /* 'Q' is for pushes.  */
581     case 'Q':
582       return (GET_CODE (x) == MEM
583               && GET_CODE (XEXP (x, 0)) == POST_INC
584               && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
585
586       /* 'R' is for pops.  */
587     case 'R':
588       return (GET_CODE (x) == MEM
589               && GET_CODE (XEXP (x, 0)) == PRE_DEC
590               && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
591
592       /* 'S' is for immediate memory addresses.  */
593     case 'S':
594       return (GET_CODE (x) == MEM
595               && GET_CODE (XEXP (x, 0)) == CONST_INT
596               && xstormy16_legitimate_address_p (VOIDmode, XEXP (x, 0), 0));
597
598       /* 'T' is for Rx.  */
599     case 'T':
600       /* Not implemented yet.  */
601       return 0;
602
603       /* 'U' is for CONST_INT values not between 2 and 15 inclusive,
604          for allocating a scratch register for 32-bit shifts.  */
605     case 'U':
606       return (GET_CODE (x) == CONST_INT
607               && (INTVAL (x) < 2 || INTVAL (x) > 15));
608
609     default:
610       return 0;
611     }
612 }
613
614 int
615 short_memory_operand (x, mode)
616      rtx x;
617      enum machine_mode mode;
618 {
619   if (! memory_operand (x, mode))
620     return 0;
621   return (GET_CODE (XEXP (x, 0)) != PLUS);
622 }
623
624 /* Splitter for the 'move' patterns, for modes not directly implemeted
625    by hardware.  Emit insns to copy a value of mode MODE from SRC to
626    DEST.
627
628    This function is only called when reload_completed.
629    */
630
631 void 
632 xstormy16_split_move (mode, dest, src)
633      enum machine_mode mode;
634      rtx dest;
635      rtx src;
636 {
637   int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
638   int direction, end, i;
639   int src_modifies = 0;
640   int dest_modifies = 0;
641   int src_volatile = 0;
642   int dest_volatile = 0;
643   rtx mem_operand;
644   rtx auto_inc_reg_rtx = NULL_RTX;
645   
646   /* Check initial conditions.  */
647   if (! reload_completed
648       || mode == QImode || mode == HImode
649       || ! nonimmediate_operand (dest, mode)
650       || ! general_operand (src, mode))
651     abort ();
652
653   /* This case is not supported below, and shouldn't be generated.  */
654   if (GET_CODE (dest) == MEM
655       && GET_CODE (src) == MEM)
656     abort ();
657
658   /* This case is very very bad after reload, so trap it now.  */
659   if (GET_CODE (dest) == SUBREG
660       || GET_CODE (src) == SUBREG)
661     abort ();
662
663   /* The general idea is to copy by words, offsetting the source and
664      destination.  Normally the least-significant word will be copied
665      first, but for pre-dec operations it's better to copy the 
666      most-significant word first.  Only one operand can be a pre-dec
667      or post-inc operand.  
668
669      It's also possible that the copy overlaps so that the direction
670      must be reversed.  */
671   direction = 1;
672   
673   if (GET_CODE (dest) == MEM)
674     {
675       mem_operand = XEXP (dest, 0);
676       dest_modifies = side_effects_p (mem_operand);
677       if (auto_inc_p (mem_operand))
678         auto_inc_reg_rtx = XEXP (mem_operand, 0);
679       dest_volatile = MEM_VOLATILE_P (dest);
680       if (dest_volatile)
681         {
682           dest = copy_rtx (dest);
683           MEM_VOLATILE_P (dest) = 0;
684         }
685     }
686   else if (GET_CODE (src) == MEM)
687     {
688       mem_operand = XEXP (src, 0);
689       src_modifies = side_effects_p (mem_operand);
690       if (auto_inc_p (mem_operand))
691         auto_inc_reg_rtx = XEXP (mem_operand, 0);
692       src_volatile = MEM_VOLATILE_P (src);
693       if (src_volatile)
694         {
695           src = copy_rtx (src);
696           MEM_VOLATILE_P (src) = 0;
697         }
698     }
699   else
700     mem_operand = NULL_RTX;
701
702   if (mem_operand == NULL_RTX)
703     {
704       if (GET_CODE (src) == REG
705           && GET_CODE (dest) == REG
706           && reg_overlap_mentioned_p (dest, src)
707           && REGNO (dest) > REGNO (src))
708         direction = -1;
709     }
710   else if (GET_CODE (mem_operand) == PRE_DEC
711       || (GET_CODE (mem_operand) == PLUS 
712           && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC))
713     direction = -1;
714   else if (GET_CODE (src) == MEM
715            && reg_overlap_mentioned_p (dest, src))
716     {
717       int regno;
718       if (GET_CODE (dest) != REG)
719         abort ();
720       regno = REGNO (dest);
721       
722       if (! refers_to_regno_p (regno, regno + num_words, mem_operand, 0))
723         abort ();
724       
725       if (refers_to_regno_p (regno, regno + 1, mem_operand, 0))
726         direction = -1;
727       else if (refers_to_regno_p (regno + num_words - 1, regno + num_words,
728                                   mem_operand, 0))
729         direction = 1;
730       else
731         /* This means something like
732            (set (reg:DI r0) (mem:DI (reg:HI r1)))
733            which we'd need to support by doing the set of the second word
734            last.  */
735         abort ();
736     }
737
738   end = direction < 0 ? -1 : num_words;
739   for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction)
740     {
741       rtx w_src, w_dest, insn;
742
743       if (src_modifies)
744         w_src = gen_rtx_MEM (word_mode, mem_operand);
745       else
746         w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD);
747       if (src_volatile)
748         MEM_VOLATILE_P (w_src) = 1;
749       if (dest_modifies)
750         w_dest = gen_rtx_MEM (word_mode, mem_operand);
751       else
752         w_dest = simplify_gen_subreg (word_mode, dest, mode, 
753                                       i * UNITS_PER_WORD);
754       if (dest_volatile)
755         MEM_VOLATILE_P (w_dest) = 1;
756       
757       /* The simplify_subreg calls must always be able to simplify.  */
758       if (GET_CODE (w_src) == SUBREG
759           || GET_CODE (w_dest) == SUBREG)
760         abort ();
761       
762       insn = emit_insn (gen_rtx_SET (VOIDmode, w_dest, w_src));
763       if (auto_inc_reg_rtx)
764         REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
765                                             auto_inc_reg_rtx,
766                                             REG_NOTES (insn));
767     }
768 }
769
770 /* Expander for the 'move' patterns.  Emit insns to copy a value of
771    mode MODE from SRC to DEST.  */
772
773 void 
774 xstormy16_expand_move (mode, dest, src)
775      enum machine_mode mode;
776      rtx dest;
777      rtx src;
778 {
779   /* There are only limited immediate-to-memory move instructions.  */
780   if (! reload_in_progress
781       && ! reload_completed
782       && GET_CODE (dest) == MEM
783       && (GET_CODE (XEXP (dest, 0)) != CONST_INT
784           || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0))
785       && GET_CODE (src) != REG
786       && GET_CODE (src) != SUBREG)
787     src = copy_to_mode_reg (mode, src);
788
789   /* Don't emit something we would immediately split.  */
790   if (reload_completed
791       && mode != HImode && mode != QImode)
792     {
793       xstormy16_split_move (mode, dest, src);
794       return;
795     }
796   
797   emit_insn (gen_rtx_SET (VOIDmode, dest, src));
798 }
799
800 \f
801 /* Stack Layout:
802
803    The stack is laid out as follows:
804
805 SP->
806 FP->    Local variables
807         Register save area (up to 4 words)
808         Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
809
810 AP->    Return address (two words)
811         9th procedure parameter word
812         10th procedure parameter word
813         ...
814         last procedure parameter word
815
816   The frame pointer location is tuned to make it most likely that all
817   parameters and local variables can be accessed using a load-indexed
818   instruction.  */
819
820 /* A structure to describe the layout.  */
821 struct xstormy16_stack_layout
822 {
823   /* Size of the topmost three items on the stack.  */
824   int locals_size;
825   int register_save_size;
826   int stdarg_save_size;
827   /* Sum of the above items.  */
828   int frame_size;
829   /* Various offsets.  */
830   int first_local_minus_ap;
831   int sp_minus_fp;
832   int fp_minus_ap;
833 };
834
835 /* Does REGNO need to be saved?  */
836 #define REG_NEEDS_SAVE(REGNUM, IFUN)                                    \
837   ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM])                 \
838    || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM]           \
839        && (regs_ever_live[REGNUM] || ! current_function_is_leaf)))
840
841 /* Compute the stack layout.  */
842 struct xstormy16_stack_layout 
843 xstormy16_compute_stack_layout ()
844 {
845   struct xstormy16_stack_layout layout;
846   int regno;
847   const int ifun = xstormy16_interrupt_function_p ();
848
849   layout.locals_size = get_frame_size ();
850   
851   layout.register_save_size = 0;
852   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
853     if (REG_NEEDS_SAVE (regno, ifun))
854       layout.register_save_size += UNITS_PER_WORD;
855   
856   if (current_function_varargs || current_function_stdarg)
857     layout.stdarg_save_size = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
858   else
859     layout.stdarg_save_size = 0;
860   
861   layout.frame_size = (layout.locals_size 
862                        + layout.register_save_size 
863                        + layout.stdarg_save_size);
864   
865   if (current_function_args_size <= 2048 && current_function_args_size != -1)
866     {
867       if (layout.frame_size + INCOMING_FRAME_SP_OFFSET 
868           + current_function_args_size <= 2048)
869         layout.fp_minus_ap = layout.frame_size + INCOMING_FRAME_SP_OFFSET;
870       else
871         layout.fp_minus_ap = 2048 - current_function_args_size;
872     }
873   else
874     layout.fp_minus_ap = (layout.stdarg_save_size 
875                           + layout.register_save_size
876                           + INCOMING_FRAME_SP_OFFSET);
877   layout.sp_minus_fp = (layout.frame_size + INCOMING_FRAME_SP_OFFSET 
878                         - layout.fp_minus_ap);
879   layout.first_local_minus_ap = layout.sp_minus_fp - layout.locals_size;
880   return layout;
881 }
882
883 /* Determine how all the special registers get eliminated.  */
884 int
885 xstormy16_initial_elimination_offset (from, to)
886      int from, to;
887 {
888   struct xstormy16_stack_layout layout;
889   int result;
890   
891   layout = xstormy16_compute_stack_layout ();
892
893   if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
894     result = layout.sp_minus_fp - layout.locals_size;
895   else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
896     result = -layout.locals_size;
897   else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
898     result = -layout.fp_minus_ap;
899   else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
900     result = -(layout.sp_minus_fp + layout.fp_minus_ap);
901   else
902     abort ();
903
904   return result;
905 }
906
907 static rtx
908 emit_addhi3_postreload (dest, src0, src1)
909      rtx dest;
910      rtx src0;
911      rtx src1;
912 {
913   rtx set, clobber, insn;
914   
915   set = gen_rtx_SET (VOIDmode, dest, gen_rtx_PLUS (HImode, src0, src1));
916   clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, 16));
917   insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
918   return insn;
919 }
920
921 /* Called after register allocation to add any instructions needed for
922    the prologue.  Using a prologue insn is favored compared to putting
923    all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
924    since it allows the scheduler to intermix instructions with the
925    saves of the caller saved registers.  In some cases, it might be
926    necessary to emit a barrier instruction as the last insn to prevent
927    such scheduling.
928
929    Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
930    so that the debug info generation code can handle them properly.  */
931 void
932 xstormy16_expand_prologue ()
933 {
934   struct xstormy16_stack_layout layout;
935   int regno;
936   rtx insn;
937   rtx mem_push_rtx;
938   rtx mem_fake_push_rtx;
939   const int ifun = xstormy16_interrupt_function_p ();
940   
941   mem_push_rtx = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
942   mem_push_rtx = gen_rtx_MEM (HImode, mem_push_rtx);
943   mem_fake_push_rtx = gen_rtx_PRE_INC (Pmode, stack_pointer_rtx);
944   mem_fake_push_rtx = gen_rtx_MEM (HImode, mem_fake_push_rtx);
945     
946   layout = xstormy16_compute_stack_layout ();
947
948   /* Save the argument registers if necessary.  */
949   if (layout.stdarg_save_size)
950     for (regno = FIRST_ARGUMENT_REGISTER; 
951          regno < FIRST_ARGUMENT_REGISTER + NUM_ARGUMENT_REGISTERS;
952          regno++)
953       {
954         rtx reg = gen_rtx_REG (HImode, regno);
955         insn = emit_move_insn (mem_push_rtx, reg);
956         RTX_FRAME_RELATED_P (insn) = 1;
957         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
958                                               gen_rtx_SET (VOIDmode,
959                                                            mem_fake_push_rtx,
960                                                            reg),
961                                               REG_NOTES (insn));
962       }
963   
964   /* Push each of the registers to save.  */
965   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
966     if (REG_NEEDS_SAVE (regno, ifun))
967       {
968         rtx reg = gen_rtx_REG (HImode, regno);
969         insn = emit_move_insn (mem_push_rtx, reg);
970         RTX_FRAME_RELATED_P (insn) = 1;
971         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
972                                               gen_rtx_SET (VOIDmode,
973                                                            mem_fake_push_rtx,
974                                                            reg),
975                                               REG_NOTES (insn));
976       }
977
978   /* It's just possible that the SP here might be what we need for
979      the new FP... */
980   if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
981     {
982       insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
983       RTX_FRAME_RELATED_P (insn) = 1;
984     }
985
986   /* Allocate space for local variables.  */
987   if (layout.locals_size)
988     {
989       insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
990                                      GEN_INT (layout.locals_size));
991       RTX_FRAME_RELATED_P (insn) = 1;
992     }
993
994   /* Set up the frame pointer, if required.  */
995   if (frame_pointer_needed && layout.sp_minus_fp != layout.locals_size)
996     {
997       insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
998       RTX_FRAME_RELATED_P (insn) = 1;
999       if (layout.sp_minus_fp)
1000         {
1001           insn = emit_addhi3_postreload (hard_frame_pointer_rtx,
1002                                          hard_frame_pointer_rtx,
1003                                          GEN_INT (-layout.sp_minus_fp));
1004           RTX_FRAME_RELATED_P (insn) = 1;
1005         }
1006     }
1007 }
1008
1009 /* Do we need an epilogue at all?  */
1010 int
1011 direct_return ()
1012 {
1013   return (reload_completed 
1014           && xstormy16_compute_stack_layout ().frame_size == 0);
1015 }
1016
1017 /* Called after register allocation to add any instructions needed for
1018    the epilogue.  Using a epilogue insn is favored compared to putting
1019    all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1020    since it allows the scheduler to intermix instructions with the
1021    saves of the caller saved registers.  In some cases, it might be
1022    necessary to emit a barrier instruction as the last insn to prevent
1023    such scheduling.  */
1024
1025 void
1026 xstormy16_expand_epilogue ()
1027 {
1028   struct xstormy16_stack_layout layout;
1029   rtx mem_pop_rtx;
1030   int regno;
1031   const int ifun = xstormy16_interrupt_function_p ();
1032   
1033   mem_pop_rtx = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
1034   mem_pop_rtx = gen_rtx_MEM (HImode, mem_pop_rtx);
1035   
1036   layout = xstormy16_compute_stack_layout ();
1037
1038   /* Pop the stack for the locals.  */
1039   if (layout.locals_size)
1040     {
1041       if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1042         emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
1043       else
1044         emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1045                                 GEN_INT (- layout.locals_size));
1046     }
1047
1048   /* Restore any call-saved registers.  */
1049   for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
1050     if (REG_NEEDS_SAVE (regno, ifun))
1051       emit_move_insn (gen_rtx_REG (HImode, regno), mem_pop_rtx);
1052   
1053   /* Pop the stack for the stdarg save area.  */
1054   if (layout.stdarg_save_size)
1055     emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1056                             GEN_INT (- layout.stdarg_save_size));
1057
1058   /* Return.  */
1059   if (ifun)
1060     emit_jump_insn (gen_return_internal_interrupt ());
1061   else
1062     emit_jump_insn (gen_return_internal ());
1063 }
1064
1065 int
1066 xstormy16_epilogue_uses (regno)
1067      int regno;
1068 {
1069   if (reload_completed && call_used_regs[regno])
1070     {
1071       const int ifun = xstormy16_interrupt_function_p ();
1072       return REG_NEEDS_SAVE (regno, ifun);
1073     }
1074   return 0;
1075 }
1076 \f
1077 /* Return an updated summarizer variable CUM to advance past an
1078    argument in the argument list.  The values MODE, TYPE and NAMED
1079    describe that argument.  Once this is done, the variable CUM is
1080    suitable for analyzing the *following* argument with
1081    `FUNCTION_ARG', etc.
1082
1083    This function need not do anything if the argument in question was
1084    passed on the stack.  The compiler knows how to track the amount of
1085    stack space used for arguments without any special help.  However,
1086    it makes life easier for xstormy16_build_va_list if it does update
1087    the word count.  */
1088 CUMULATIVE_ARGS
1089 xstormy16_function_arg_advance (cum, mode, type, named)
1090      CUMULATIVE_ARGS cum;
1091      enum machine_mode mode;
1092      tree type;
1093      int named ATTRIBUTE_UNUSED;
1094 {
1095   /* If an argument would otherwise be passed partially in registers,
1096      and partially on the stack, the whole of it is passed on the
1097      stack.  */
1098   if (cum < NUM_ARGUMENT_REGISTERS
1099       && cum + XSTORMY16_WORD_SIZE (type, mode) > NUM_ARGUMENT_REGISTERS)
1100     cum = NUM_ARGUMENT_REGISTERS;
1101   
1102   cum += XSTORMY16_WORD_SIZE (type, mode);
1103   
1104   return cum;
1105 }
1106
1107 /* Do any needed setup for a variadic function.  CUM has not been updated
1108    for the last named argument which has type TYPE and mode MODE.  */
1109 void
1110 xstormy16_setup_incoming_varargs (cum, int_mode, type, pretend_size)
1111      CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED;
1112      int             int_mode ATTRIBUTE_UNUSED;
1113      tree            type ATTRIBUTE_UNUSED;
1114      int *           pretend_size ATTRIBUTE_UNUSED;
1115 {
1116 }
1117
1118 /* Build the va_list type.
1119
1120    For this chip, va_list is a record containing a counter and a pointer.
1121    The counter is of type 'int' and indicates how many bytes
1122    have been used to date.  The pointer indicates the stack position
1123    for arguments that have not been passed in registers.  
1124    To keep the layout nice, the pointer is first in the structure.  */
1125
1126 tree
1127 xstormy16_build_va_list ()
1128 {
1129   tree f_1, f_2, record, type_decl;
1130
1131   record = make_lang_type (RECORD_TYPE);
1132   type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
1133
1134   f_1 = build_decl (FIELD_DECL, get_identifier ("base"),
1135                       ptr_type_node);
1136   f_2 = build_decl (FIELD_DECL, get_identifier ("count"), 
1137                       unsigned_type_node);
1138
1139   DECL_FIELD_CONTEXT (f_1) = record;
1140   DECL_FIELD_CONTEXT (f_2) = record;
1141
1142   TREE_CHAIN (record) = type_decl;
1143   TYPE_NAME (record) = type_decl;
1144   TYPE_FIELDS (record) = f_1;
1145   TREE_CHAIN (f_1) = f_2;
1146
1147   layout_type (record);
1148
1149   return record;
1150 }
1151
1152 /* Implement the stdarg/varargs va_start macro.  STDARG_P is non-zero if this
1153    is stdarg.h instead of varargs.h.  VALIST is the tree of the va_list
1154    variable to initialize.  NEXTARG is the machine independent notion of the
1155    'next' argument after the variable arguments.  */
1156 void
1157 xstormy16_expand_builtin_va_start (stdarg_p, valist, nextarg)
1158      int stdarg_p ATTRIBUTE_UNUSED;
1159      tree valist;
1160      rtx nextarg ATTRIBUTE_UNUSED;
1161 {
1162   tree f_base, f_count;
1163   tree base, count;
1164   tree t;
1165
1166   if (xstormy16_interrupt_function_p ())
1167     error ("cannot use va_start in interrupt function");
1168   
1169   f_base = TYPE_FIELDS (va_list_type_node);
1170   f_count = TREE_CHAIN (f_base);
1171   
1172   base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1173   count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1174
1175   t = make_tree (TREE_TYPE (base), virtual_incoming_args_rtx);
1176   t = build (PLUS_EXPR, TREE_TYPE (base), t, 
1177              build_int_2 (INCOMING_FRAME_SP_OFFSET, 0));
1178   t = build (MODIFY_EXPR, TREE_TYPE (base), base, t);
1179   TREE_SIDE_EFFECTS (t) = 1;
1180   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1181
1182   t = build (MODIFY_EXPR, TREE_TYPE (count), count, 
1183              build_int_2 (current_function_args_info * UNITS_PER_WORD, 0));
1184   TREE_SIDE_EFFECTS (t) = 1;
1185   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1186 }
1187
1188 /* Implement the stdarg/varargs va_arg macro.  VALIST is the variable
1189    of type va_list as a tree, TYPE is the type passed to va_arg.
1190    Note:  This algorithm is documented in stormy-abi.  */
1191    
1192 rtx
1193 xstormy16_expand_builtin_va_arg (valist, type)
1194      tree valist;
1195      tree type;
1196 {
1197   tree f_base, f_count;
1198   tree base, count;
1199   rtx count_rtx, addr_rtx, r;
1200   rtx lab_gotaddr, lab_fromstack;
1201   tree t;
1202   int size, size_of_reg_args;
1203   tree size_tree, count_plus_size;
1204   rtx count_plus_size_rtx;
1205   
1206   f_base = TYPE_FIELDS (va_list_type_node);
1207   f_count = TREE_CHAIN (f_base);
1208   
1209   base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1210   count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1211
1212   size = PUSH_ROUNDING (int_size_in_bytes (type));
1213   size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
1214   
1215   size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
1216
1217   count_rtx = expand_expr (count, NULL_RTX, HImode, EXPAND_NORMAL);
1218   lab_gotaddr = gen_label_rtx ();
1219   lab_fromstack = gen_label_rtx ();
1220   addr_rtx = gen_reg_rtx (Pmode);
1221
1222   count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1223   count_plus_size_rtx = expand_expr (count_plus_size, NULL_RTX, HImode, EXPAND_NORMAL);
1224   emit_cmp_and_jump_insns (count_plus_size_rtx, GEN_INT (size_of_reg_args),
1225                            GTU, const1_rtx, HImode, 1, 1, lab_fromstack);
1226   
1227   t = build (PLUS_EXPR, ptr_type_node, base, count);
1228   r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1229   if (r != addr_rtx)
1230     emit_move_insn (addr_rtx, r);
1231
1232   emit_jump_insn (gen_jump (lab_gotaddr));
1233   emit_barrier ();
1234   emit_label (lab_fromstack);
1235   
1236   /* Arguments larger than a word might need to skip over some
1237      registers, since arguments are either passed entirely in
1238      registers or entirely on the stack.  */
1239   if (size > 2 || size < 0)
1240     {
1241       rtx lab_notransition = gen_label_rtx ();
1242       emit_cmp_and_jump_insns (count_rtx, GEN_INT (NUM_ARGUMENT_REGISTERS 
1243                                                    * UNITS_PER_WORD),
1244                                GEU, const1_rtx, HImode, 1, 1, 
1245                                lab_notransition);
1246       
1247       t = build (MODIFY_EXPR, TREE_TYPE (count), count, 
1248                  build_int_2 (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD, 0));
1249       TREE_SIDE_EFFECTS (t) = 1;
1250       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1251       
1252       emit_label (lab_notransition);
1253     }
1254
1255   t = build (PLUS_EXPR, sizetype, size_tree,
1256              build_int_2 ((- NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD
1257                            + INCOMING_FRAME_SP_OFFSET),
1258                           -1));
1259   t = build (PLUS_EXPR, TREE_TYPE (count), count, fold (t));
1260   t = build (MINUS_EXPR, TREE_TYPE (base), base, t);
1261   r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1262   if (r != addr_rtx)
1263     emit_move_insn (addr_rtx, r);
1264              
1265   emit_label (lab_gotaddr);
1266
1267   count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1268   t = build (MODIFY_EXPR, TREE_TYPE (count), count, count_plus_size);
1269   TREE_SIDE_EFFECTS (t) = 1;
1270   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1271
1272   return addr_rtx;
1273 }
1274
1275 /* Initialize the variable parts of a trampoline.  ADDR is an RTX for
1276    the address of the trampoline; FNADDR is an RTX for the address of
1277    the nested function; STATIC_CHAIN is an RTX for the static chain
1278    value that should be passed to the function when it is called.  */
1279 void
1280 xstormy16_initialize_trampoline (addr, fnaddr, static_chain)
1281      rtx addr;
1282      rtx fnaddr;
1283      rtx static_chain;
1284 {
1285   rtx reg_addr = gen_reg_rtx (Pmode);
1286   rtx temp = gen_reg_rtx (HImode);
1287   rtx reg_fnaddr = gen_reg_rtx (HImode);
1288   rtx reg_addr_mem;
1289
1290   reg_addr_mem = gen_rtx_MEM (HImode, reg_addr);
1291     
1292   emit_move_insn (reg_addr, addr);
1293   emit_move_insn (temp, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM));
1294   emit_move_insn (reg_addr_mem, temp);
1295   emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1296   emit_move_insn (temp, static_chain);
1297   emit_move_insn (reg_addr_mem, temp);
1298   emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1299   emit_move_insn (reg_fnaddr, fnaddr);
1300   emit_move_insn (temp, reg_fnaddr);
1301   emit_insn (gen_andhi3 (temp, temp, GEN_INT (0xFF)));
1302   emit_insn (gen_iorhi3 (temp, temp, GEN_INT (0x0200)));
1303   emit_move_insn (reg_addr_mem, temp);
1304   emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1305   emit_insn (gen_lshrhi3 (reg_fnaddr, reg_fnaddr, GEN_INT (8)));
1306   emit_move_insn (reg_addr_mem, reg_fnaddr);
1307 }
1308
1309 /* Create an RTX representing the place where a function returns a
1310    value of data type VALTYPE.  VALTYPE is a tree node representing a
1311    data type.  Write `TYPE_MODE (VALTYPE)' to get the machine mode
1312    used to represent that type.  On many machines, only the mode is
1313    relevant.  (Actually, on most machines, scalar values are returned
1314    in the same place regardless of mode).
1315
1316    If `PROMOTE_FUNCTION_RETURN' is defined, you must apply the same promotion
1317    rules specified in `PROMOTE_MODE' if VALTYPE is a scalar type.
1318
1319    If the precise function being called is known, FUNC is a tree node
1320    (`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer.  This makes it
1321    possible to use a different value-returning convention for specific
1322    functions when all their calls are known.
1323
1324    `FUNCTION_VALUE' is not used for return vales with aggregate data types,
1325    because these are returned in another way.  See `STRUCT_VALUE_REGNUM' and
1326    related macros.  */
1327 rtx
1328 xstormy16_function_value (valtype, func)
1329      tree valtype;
1330      tree func ATTRIBUTE_UNUSED;
1331 {
1332   enum machine_mode mode;
1333   mode = TYPE_MODE (valtype);
1334   PROMOTE_MODE (mode, 0, valtype);
1335   return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
1336 }
1337
1338 /* A C compound statement that outputs the assembler code for a thunk function,
1339    used to implement C++ virtual function calls with multiple inheritance.  The
1340    thunk acts as a wrapper around a virtual function, adjusting the implicit
1341    object parameter before handing control off to the real function.
1342
1343    First, emit code to add the integer DELTA to the location that contains the
1344    incoming first argument.  Assume that this argument contains a pointer, and
1345    is the one used to pass the `this' pointer in C++.  This is the incoming
1346    argument *before* the function prologue, e.g. `%o0' on a sparc.  The
1347    addition must preserve the values of all other incoming arguments.
1348
1349    After the addition, emit code to jump to FUNCTION, which is a
1350    `FUNCTION_DECL'.  This is a direct pure jump, not a call, and does not touch
1351    the return address.  Hence returning from FUNCTION will return to whoever
1352    called the current `thunk'.
1353
1354    The effect must be as if @var{function} had been called directly
1355    with the adjusted first argument.  This macro is responsible for
1356    emitting all of the code for a thunk function;
1357    TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are
1358    not invoked.
1359
1360    The THUNK_FNDECL is redundant.  (DELTA and FUNCTION have already been
1361    extracted from it.)  It might possibly be useful on some targets, but
1362    probably not.  */
1363
1364 void
1365 xstormy16_asm_output_mi_thunk (file, thunk_fndecl, delta, function)
1366      FILE *file;
1367      tree thunk_fndecl ATTRIBUTE_UNUSED;
1368      int delta;
1369      tree function;
1370 {
1371   int regnum = FIRST_ARGUMENT_REGISTER;
1372   
1373   /* There might be a hidden first argument for a returned structure.  */
1374   if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
1375     regnum += 1;
1376   
1377   fprintf (file, "\tadd %s,#0x%x\n", reg_names[regnum], (delta) & 0xFFFF);
1378   fputs ("\tjmpf ", file);
1379   assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
1380   putc ('\n', file);
1381 }
1382
1383 /* Mark functions with SYMBOL_REF_FLAG.  */
1384
1385 void
1386 xstormy16_encode_section_info (decl)
1387      tree decl;
1388 {
1389   if (TREE_CODE (decl) == FUNCTION_DECL)
1390     SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
1391 }
1392
1393 /* Output constructors and destructors.  Just like 
1394    default_named_section_asm_out_* but don't set the sections writable.  */
1395 #undef TARGET_ASM_CONSTRUCTOR
1396 #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
1397 #undef TARGET_ASM_DESTRUCTOR
1398 #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1399
1400 static void
1401 xstormy16_asm_out_destructor (symbol, priority)
1402      rtx symbol;
1403      int priority;
1404 {
1405   const char *section = ".dtors";
1406   char buf[16];
1407
1408   /* ??? This only works reliably with the GNU linker.   */
1409   if (priority != DEFAULT_INIT_PRIORITY)
1410     {
1411       sprintf (buf, ".dtors.%.5u",
1412                /* Invert the numbering so the linker puts us in the proper
1413                   order; constructors are run from right to left, and the
1414                   linker sorts in increasing order.  */
1415                MAX_INIT_PRIORITY - priority);
1416       section = buf;
1417     }
1418
1419   named_section_flags (section, 0);
1420   assemble_align (POINTER_SIZE);
1421   assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1422 }
1423
1424 static void
1425 xstormy16_asm_out_constructor (symbol, priority)
1426      rtx symbol;
1427      int priority;
1428 {
1429   const char *section = ".ctors";
1430   char buf[16];
1431
1432   /* ??? This only works reliably with the GNU linker.   */
1433   if (priority != DEFAULT_INIT_PRIORITY)
1434     {
1435       sprintf (buf, ".ctors.%.5u",
1436                /* Invert the numbering so the linker puts us in the proper
1437                   order; constructors are run from right to left, and the
1438                   linker sorts in increasing order.  */
1439                MAX_INIT_PRIORITY - priority);
1440       section = buf;
1441     }
1442
1443   named_section_flags (section, 0);
1444   assemble_align (POINTER_SIZE);
1445   assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1446 }
1447 \f
1448 /* Print a memory address as an operand to reference that memory location.  */
1449 void
1450 xstormy16_print_operand_address (file, address)
1451      FILE * file;
1452      rtx    address;
1453 {
1454   HOST_WIDE_INT offset;
1455   int pre_dec, post_inc;
1456
1457   /* There are a few easy cases.  */
1458   if (GET_CODE (address) == CONST_INT)
1459     {
1460       fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (address) & 0xFFFF);
1461       return;
1462     }
1463   
1464   if (CONSTANT_P (address) || GET_CODE (address) == CODE_LABEL)
1465     {
1466       output_addr_const (file, address);
1467       return;
1468     }
1469   
1470   /* Otherwise, it's hopefully something of the form 
1471      (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...))
1472   */
1473
1474   if (GET_CODE (address) == PLUS)
1475     {
1476       if (GET_CODE (XEXP (address, 1)) != CONST_INT)
1477         abort ();
1478       offset = INTVAL (XEXP (address, 1));
1479       address = XEXP (address, 0);
1480     }
1481   else
1482     offset = 0;
1483
1484   pre_dec = (GET_CODE (address) == PRE_DEC);
1485   post_inc = (GET_CODE (address) == POST_INC);
1486   if (pre_dec || post_inc)
1487     address = XEXP (address, 0);
1488   
1489   if (GET_CODE (address) != REG)
1490     abort ();
1491
1492   fputc ('(', file);
1493   if (pre_dec)
1494     fputs ("--", file);
1495   fputs (reg_names [REGNO (address)], file);
1496   if (post_inc)
1497     fputs ("++", file);
1498   if (offset != 0)
1499     {
1500       fputc (',', file);
1501       fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
1502     }
1503   fputc (')', file);
1504 }
1505
1506 /* Print an operand to a assembler instruction.  */
1507 void
1508 xstormy16_print_operand (file, x, code)
1509      FILE * file;
1510      rtx    x;
1511      int    code;
1512 {
1513   switch (code)
1514     {
1515     case 'B':
1516         /* There is either one bit set, or one bit clear, in X.
1517            Print it preceded by '#'.  */
1518       {
1519         HOST_WIDE_INT xx = 1;
1520         HOST_WIDE_INT l;
1521
1522         if (GET_CODE (x) == CONST_INT)
1523           xx = INTVAL (x);
1524         else
1525           output_operand_lossage ("`B' operand is not constant");
1526         
1527         l = exact_log2 (xx);
1528         if (l == -1)
1529           l = exact_log2 (~xx);
1530         if (l == -1)
1531           output_operand_lossage ("`B' operand has multiple bits set");
1532         
1533         fputs (IMMEDIATE_PREFIX, file);
1534         fprintf (file, HOST_WIDE_INT_PRINT_DEC, l);
1535         return;
1536       }
1537
1538     case 'C':
1539       /* Print the symbol without a surrounding @fptr().  */
1540       if (GET_CODE (x) == SYMBOL_REF)
1541         assemble_name (file, XSTR (x, 0));
1542       else if (GET_CODE (x) == LABEL_REF)
1543         output_asm_label (x);
1544       else
1545         xstormy16_print_operand_address (file, x);
1546       return;
1547
1548     case 'o':
1549     case 'O':
1550       /* Print the immediate operand less one, preceded by '#'.  
1551          For 'O', negate it first.  */
1552       {
1553         HOST_WIDE_INT xx = 0;
1554         
1555         if (GET_CODE (x) == CONST_INT)
1556           xx = INTVAL (x);
1557         else
1558           output_operand_lossage ("`o' operand is not constant");
1559         
1560         if (code == 'O')
1561           xx = -xx;
1562         
1563         fputs (IMMEDIATE_PREFIX, file);
1564         fprintf (file, HOST_WIDE_INT_PRINT_DEC, xx - 1);
1565         return;
1566       }
1567
1568     case 0:
1569       /* Handled below.  */
1570       break;
1571       
1572     default:
1573       output_operand_lossage ("xstormy16_print_operand: unknown code");
1574       return;
1575     }
1576
1577   switch (GET_CODE (x))
1578     {
1579     case REG:
1580       fputs (reg_names [REGNO (x)], file);
1581       break;
1582
1583     case MEM:
1584       xstormy16_print_operand_address (file, XEXP (x, 0));
1585       break;
1586
1587     default:
1588       /* Some kind of constant or label; an immediate operand,
1589          so prefix it with '#' for the assembler.  */
1590       fputs (IMMEDIATE_PREFIX, file);
1591       output_addr_const (file, x);
1592       break;
1593     }
1594
1595   return;
1596 }
1597
1598 \f
1599 /* Expander for the `casesi' pattern.
1600    INDEX is the index of the switch statement.
1601    LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1602      to the first table entry.
1603    RANGE is the number of table entries.
1604    TABLE is an ADDR_VEC that is the jump table.
1605    DEFAULT_LABEL is the address to branch to if INDEX is outside the
1606      range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1607 */
1608
1609 void 
1610 xstormy16_expand_casesi (index, lower_bound, range, table, default_label)
1611      rtx index;
1612      rtx lower_bound;
1613      rtx range;
1614      rtx table;
1615      rtx default_label;
1616 {
1617   HOST_WIDE_INT range_i = INTVAL (range);
1618   rtx int_index;
1619
1620   /* This code uses 'br', so it can deal only with tables of size up to
1621      8192 entries.  */
1622   if (range_i >= 8192)
1623     sorry ("switch statement of size %lu entries too large", 
1624            (unsigned long) range_i);
1625
1626   index = expand_binop (SImode, sub_optab, index, lower_bound, NULL_RTX, 0,
1627                         OPTAB_LIB_WIDEN);
1628   emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1,
1629                            0, default_label);
1630   int_index = gen_lowpart_common (HImode, index);
1631   emit_insn (gen_ashlhi3 (int_index, int_index, GEN_INT (2)));
1632   emit_jump_insn (gen_tablejump_pcrel (int_index, table));
1633 }
1634
1635 /* Output an ADDR_VEC.  It is output as a sequence of 'jmpf'
1636    instructions, without label or alignment or any other special
1637    constructs.  We know that the previous instruction will be the
1638    `tablejump_pcrel' output above.
1639
1640    TODO: it might be nice to output 'br' instructions if they could
1641    all reach.  */
1642
1643 void
1644 xstormy16_output_addr_vec (file, label, table)
1645      FILE *file;
1646      rtx label ATTRIBUTE_UNUSED;
1647      rtx table;
1648
1649   int vlen, idx;
1650   
1651   function_section (current_function_decl);
1652
1653   vlen = XVECLEN (table, 0);
1654   for (idx = 0; idx < vlen; idx++)
1655     {
1656       fputs ("\tjmpf ", file);
1657       output_asm_label (XEXP (XVECEXP (table, 0, idx), 0));
1658       fputc ('\n', file);
1659     }
1660 }
1661
1662 \f
1663 /* Expander for the `call' patterns.
1664    INDEX is the index of the switch statement.
1665    LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1666      to the first table entry.
1667    RANGE is the number of table entries.
1668    TABLE is an ADDR_VEC that is the jump table.
1669    DEFAULT_LABEL is the address to branch to if INDEX is outside the
1670      range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1671 */
1672
1673 void 
1674 xstormy16_expand_call (retval, dest, counter)
1675      rtx retval;
1676      rtx dest;
1677      rtx counter;
1678 {
1679   rtx call, temp;
1680   enum machine_mode mode;
1681
1682   if (GET_CODE (dest) != MEM)
1683     abort ();
1684   dest = XEXP (dest, 0);
1685
1686   if (! CONSTANT_P (dest)
1687       && GET_CODE (dest) != REG)
1688     dest = force_reg (Pmode, dest);
1689   
1690   if (retval == NULL)
1691     mode = VOIDmode;
1692   else
1693     mode = GET_MODE (retval);
1694
1695   call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest),
1696                        counter);
1697   if (retval)
1698     call = gen_rtx_SET (VOIDmode, retval, call);
1699   
1700   if (! CONSTANT_P (dest))
1701     {
1702       temp = gen_reg_rtx (HImode);
1703       emit_move_insn (temp, const0_rtx);
1704     }
1705   else
1706     temp = const0_rtx;
1707   
1708   call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, 
1709                                                 gen_rtx_USE (VOIDmode, temp)));
1710   emit_call_insn (call);
1711 }
1712 \f
1713 /* Expanders for multiword computational operations.  */
1714
1715 /* Expander for arithmetic operations; emit insns to compute
1716
1717    (set DEST (CODE:MODE SRC0 SRC1))
1718    
1719    using CARRY as a temporary.  When CODE is COMPARE, a branch
1720    template is generated (this saves duplicating code in
1721    xstormy16_split_cbranch).  */
1722
1723 void 
1724 xstormy16_expand_arith (mode, code, dest, src0, src1, carry)
1725      enum machine_mode mode;
1726      enum rtx_code code;
1727      rtx dest;
1728      rtx src0;
1729      rtx src1;
1730      rtx carry;
1731 {
1732   int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
1733   int i;
1734   int firstloop = 1;
1735
1736   if (code == NEG)
1737     {
1738       rtx zero_reg = gen_reg_rtx (word_mode);
1739       emit_move_insn (zero_reg, src0);
1740       src0 = zero_reg;
1741     }
1742   
1743   for (i = 0; i < num_words; i++)
1744     {
1745       rtx w_src0, w_src1, w_dest;
1746       rtx insn;
1747       
1748       if (code == NEG)
1749         w_src0 = src0;
1750       else
1751         w_src0 = simplify_gen_subreg (word_mode, src0, mode, 
1752                                       i * UNITS_PER_WORD);
1753       w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD);
1754       w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD);
1755
1756       switch (code)
1757         {
1758         case PLUS:
1759           if (firstloop
1760               && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1761             continue;
1762           
1763           if (firstloop)
1764             insn = gen_addchi4 (w_dest, w_src0, w_src1, carry);
1765           else
1766             insn = gen_addchi5 (w_dest, w_src0, w_src1, carry, carry);
1767           break;
1768
1769         case NEG:
1770         case MINUS:
1771         case COMPARE:
1772           if (code == COMPARE && i == num_words - 1)
1773             {
1774               rtx branch, sub, clobber, sub_1;
1775               
1776               sub_1 = gen_rtx_MINUS (HImode, w_src0, 
1777                                      gen_rtx_ZERO_EXTEND (HImode, carry));
1778               sub = gen_rtx_SET (VOIDmode, w_dest,
1779                                  gen_rtx_MINUS (HImode, sub_1, w_src1));
1780               clobber = gen_rtx_CLOBBER (VOIDmode, carry);
1781               branch = gen_rtx_SET (VOIDmode, pc_rtx,
1782                                     gen_rtx_IF_THEN_ELSE (VOIDmode,
1783                                                           gen_rtx_EQ (HImode,
1784                                                                       sub_1,
1785                                                                       w_src1),
1786                                                           pc_rtx,
1787                                                           pc_rtx));
1788               insn = gen_rtx_PARALLEL (VOIDmode,
1789                                        gen_rtvec (3, branch, sub, clobber));
1790             }
1791           else if (firstloop
1792                    && code != COMPARE
1793                    && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1794             continue;
1795           else if (firstloop)
1796             insn = gen_subchi4 (w_dest, w_src0, w_src1, carry);
1797           else
1798             insn = gen_subchi5 (w_dest, w_src0, w_src1, carry, carry);
1799           break;
1800
1801         case IOR:
1802         case XOR:
1803         case AND:
1804           if (GET_CODE (w_src1) == CONST_INT 
1805               && INTVAL (w_src1) == -(code == AND))
1806             continue;
1807           
1808           insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx (code, mode,
1809                                                          w_src0, w_src1));
1810           break;
1811
1812         case NOT:
1813           insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx_NOT (mode, w_src0));
1814           break;
1815
1816         default:
1817           abort ();
1818         }
1819       
1820       firstloop = 0;
1821       emit (insn);
1822     }
1823 }
1824
1825 /* Return 1 if OP is a shift operator.  */
1826
1827 int
1828 shift_operator (op, mode)
1829      register rtx op;
1830      enum machine_mode mode ATTRIBUTE_UNUSED;
1831 {
1832   enum rtx_code code = GET_CODE (op);
1833
1834   return (code == ASHIFT
1835           || code == ASHIFTRT
1836           || code == LSHIFTRT);
1837 }
1838
1839 /* The shift operations are split at output time for constant values;
1840    variable-width shifts get handed off to a library routine.  
1841
1842    Generate an output string to do (set X (CODE:MODE X SIZE_R))
1843    SIZE_R will be a CONST_INT, X will be a hard register.  */
1844
1845 const char * 
1846 xstormy16_output_shift (mode, code, x, size_r, temp)
1847      enum machine_mode mode;
1848      enum rtx_code code;
1849      rtx x;
1850      rtx size_r;
1851      rtx temp;
1852 {
1853   HOST_WIDE_INT size;
1854   const char *r0, *r1, *rt;
1855   static char r[64];
1856
1857   if (GET_CODE (size_r) != CONST_INT
1858       || GET_CODE (x) != REG
1859       || mode != SImode)
1860     abort ();
1861   size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1);
1862
1863   if (size == 0)
1864     return "";
1865
1866   r0 = reg_names [REGNO (x)];
1867   r1 = reg_names [REGNO (x) + 1];
1868
1869   /* For shifts of size 1, we can use the rotate instructions.  */
1870   if (size == 1)
1871     {
1872       switch (code)
1873         {
1874         case ASHIFT:
1875           sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1);
1876           break;
1877         case ASHIFTRT:
1878           sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0);
1879           break;
1880         case LSHIFTRT:
1881           sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0);
1882           break;
1883         default:
1884           abort ();
1885         }
1886       return r;
1887     }
1888   
1889   /* For large shifts, there are easy special cases.  */
1890   if (size == 16)
1891     {
1892       switch (code)
1893         {
1894         case ASHIFT:
1895           sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0);
1896           break;
1897         case ASHIFTRT:
1898           sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1);
1899           break;
1900         case LSHIFTRT:
1901           sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1);
1902           break;
1903         default:
1904           abort ();
1905         }
1906       return r;
1907     }
1908   if (size > 16)
1909     {
1910       switch (code)
1911         {
1912         case ASHIFT:
1913           sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d", 
1914                    r1, r0, r0, r1, (int) size - 16);
1915           break;
1916         case ASHIFTRT:
1917           sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d", 
1918                    r0, r1, r1, r0, (int) size - 16);
1919           break;
1920         case LSHIFTRT:
1921           sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d", 
1922                    r0, r1, r1, r0, (int) size - 16);
1923           break;
1924         default:
1925           abort ();
1926         }
1927       return r;
1928     }
1929
1930   /* For the rest, we have to do more work.  In particular, we
1931      need a temporary.  */
1932   rt = reg_names [REGNO (temp)];
1933   switch (code)
1934     {
1935     case ASHIFT:
1936       sprintf (r, 
1937                "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s", 
1938                rt, r0, r0, (int) size, r1, (int) size, rt, (int) 16-size,
1939                r1, rt);
1940       break;
1941     case ASHIFTRT:
1942       sprintf (r, 
1943                "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", 
1944                rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
1945                r0, rt);
1946       break;
1947     case LSHIFTRT:
1948       sprintf (r, 
1949                "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", 
1950                rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
1951                r0, rt);
1952       break;
1953     default:
1954       abort ();
1955     }
1956   return r;
1957 }
1958 \f
1959 /* Attribute handling.  */
1960
1961 /* Return nonzero if the function is an interrupt function.  */
1962 int
1963 xstormy16_interrupt_function_p ()
1964 {
1965   tree attributes;
1966   
1967   /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
1968      any functions are declared, which is demonstrably wrong, but
1969      it is worked around here.  FIXME.  */
1970   if (!cfun)
1971     return 0;
1972
1973   attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1974   return lookup_attribute ("interrupt", attributes) != NULL_TREE;
1975 }
1976
1977 #undef TARGET_ATTRIBUTE_TABLE
1978 #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
1979 static tree xstormy16_handle_interrupt_attribute PARAMS ((tree *, tree, tree, int, bool *));
1980 static const struct attribute_spec xstormy16_attribute_table[] =
1981 {
1982   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
1983   { "interrupt", 0, 0, false, true,  true,  xstormy16_handle_interrupt_attribute },
1984   { NULL,        0, 0, false, false, false, NULL }
1985 };
1986
1987 /* Handle an "interrupt" attribute;
1988    arguments as in struct attribute_spec.handler.  */
1989 static tree
1990 xstormy16_handle_interrupt_attribute (node, name, args, flags, no_add_attrs)
1991      tree *node;
1992      tree name;
1993      tree args ATTRIBUTE_UNUSED;
1994      int flags ATTRIBUTE_UNUSED;
1995      bool *no_add_attrs;
1996 {
1997   if (TREE_CODE (*node) != FUNCTION_TYPE)
1998     {
1999       warning ("`%s' attribute only applies to functions",
2000                IDENTIFIER_POINTER (name));
2001       *no_add_attrs = true;
2002     }
2003
2004   return NULL_TREE;
2005 }
2006 \f
2007 struct gcc_target targetm = TARGET_INITIALIZER;