OSDN Git Service

* config/stormy16/stormy16.c (stormy16_output_shift): Don't
[pf3gnuchains/gcc-fork.git] / gcc / config / stormy16 / stormy16.c
1 /* Stormy16 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 stormy16_asm_out_constructor PARAMS ((rtx, int));
49 static void stormy16_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 * stormy16_compare_op0;
54 struct rtx_def * stormy16_compare_op1;
55
56 /* Return 1 if this is a LT, GE, LTU, or GEU operator.  */
57
58 int
59 stormy16_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 stormy16_emit_cbranch (code, loc)
117      enum rtx_code code;
118      rtx loc;
119 {
120   rtx op0 = stormy16_compare_op0;
121   rtx op1 = stormy16_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;
136       
137       if (gt_p)
138         lab = gen_label_rtx ();
139       stormy16_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       stormy16_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;
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           stormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, 
161                                                       i * UNITS_PER_WORD);
162           stormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, 
163                                                       i * UNITS_PER_WORD);
164           stormy16_emit_cbranch (NE, code == EQ ? lab : loc);
165         }
166       stormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, 
167                                                   i * UNITS_PER_WORD);
168       stormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, 
169                                                   i * UNITS_PER_WORD);
170       stormy16_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    stormy16_expand_arith.  */
216
217 void
218 stormy16_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   stormy16_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 stormy16_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 stormy16_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 stormy16_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 (stormy16_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 stormy16_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 stormy16_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 stormy16_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 stormy16_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 stormy16_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               && stormy16_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 stormy16_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 stormy16_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           || ! stormy16_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       stormy16_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 stormy16_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 stormy16_stack_layout 
843 stormy16_compute_stack_layout ()
844 {
845   struct stormy16_stack_layout layout;
846   int regno;
847   const int ifun = stormy16_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 stormy16_initial_elimination_offset (from, to)
886      int from, to;
887 {
888   struct stormy16_stack_layout layout;
889   int result;
890   
891   layout = stormy16_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 stormy16_expand_prologue ()
933 {
934   struct stormy16_stack_layout layout;
935   int regno;
936   rtx insn;
937   rtx mem_push_rtx;
938   rtx mem_fake_push_rtx;
939   const int ifun = stormy16_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 = stormy16_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           && stormy16_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 stormy16_expand_epilogue ()
1027 {
1028   struct stormy16_stack_layout layout;
1029   rtx mem_pop_rtx;
1030   int regno;
1031   const int ifun = stormy16_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 = stormy16_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 stormy16_epilogue_uses (regno)
1067      int regno;
1068 {
1069   if (reload_completed && call_used_regs[regno])
1070     {
1071       const int ifun = stormy16_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 stormy16_build_va_list if it does update
1087    the word count.  */
1088 CUMULATIVE_ARGS
1089 stormy16_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 + STORMY16_WORD_SIZE (type, mode) > NUM_ARGUMENT_REGISTERS)
1100     cum = NUM_ARGUMENT_REGISTERS;
1101   
1102   cum += STORMY16_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 stormy16_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 stormy16_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 stormy16_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 (stormy16_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 stormy16_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 stormy16_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 stormy16_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 stormy16_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 stormy16_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 stormy16_asm_out_constructor
1397 #undef TARGET_ASM_DESTRUCTOR
1398 #define TARGET_ASM_DESTRUCTOR stormy16_asm_out_destructor
1399
1400 static void
1401 stormy16_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 stormy16_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 stormy16_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 stormy16_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, l;
1520
1521         if (GET_CODE (x) == CONST_INT)
1522           xx = INTVAL (x);
1523         else
1524           output_operand_lossage ("`B' operand is not constant");
1525         
1526         l = exact_log2 (xx);
1527         if (l == -1)
1528           l = exact_log2 (~xx);
1529         if (l == -1)
1530           output_operand_lossage ("`B' operand has multiple bits set");
1531         
1532         fputs (IMMEDIATE_PREFIX, file);
1533         fprintf (file, HOST_WIDE_INT_PRINT_DEC, l);
1534         return;
1535       }
1536
1537     case 'C':
1538       /* Print the symbol without a surrounding @fptr().  */
1539       if (GET_CODE (x) == SYMBOL_REF)
1540         assemble_name (file, XSTR (x, 0));
1541       else
1542         stormy16_print_operand_address (file, x);
1543       return;
1544
1545     case 'o':
1546     case 'O':
1547       /* Print the immediate operand less one, preceded by '#'.  
1548          For 'O', negate it first.  */
1549       {
1550         HOST_WIDE_INT xx;
1551         
1552         if (GET_CODE (x) == CONST_INT)
1553           xx = INTVAL (x);
1554         else
1555           output_operand_lossage ("`o' operand is not constant");
1556         
1557         if (code == 'O')
1558           xx = -xx;
1559         
1560         fputs (IMMEDIATE_PREFIX, file);
1561         fprintf (file, HOST_WIDE_INT_PRINT_DEC, xx - 1);
1562         return;
1563       }
1564
1565     case 0:
1566       /* Handled below.  */
1567       break;
1568       
1569     default:
1570       output_operand_lossage ("stormy16_print_operand: unknown code");
1571       return;
1572     }
1573
1574   switch (GET_CODE (x))
1575     {
1576     case REG:
1577       fputs (reg_names [REGNO (x)], file);
1578       break;
1579
1580     case MEM:
1581       stormy16_print_operand_address (file, XEXP (x, 0));
1582       break;
1583
1584     default:
1585       /* Some kind of constant or label; an immediate operand,
1586          so prefix it with '#' for the assembler.  */
1587       fputs (IMMEDIATE_PREFIX, file);
1588       output_addr_const (file, x);
1589       break;
1590     }
1591
1592   return;
1593 }
1594
1595 \f
1596 /* Expander for the `casesi' pattern.
1597    INDEX is the index of the switch statement.
1598    LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1599      to the first table entry.
1600    RANGE is the number of table entries.
1601    TABLE is an ADDR_VEC that is the jump table.
1602    DEFAULT_LABEL is the address to branch to if INDEX is outside the
1603      range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1604 */
1605
1606 void 
1607 stormy16_expand_casesi (index, lower_bound, range, table, default_label)
1608      rtx index;
1609      rtx lower_bound;
1610      rtx range;
1611      rtx table;
1612      rtx default_label;
1613 {
1614   HOST_WIDE_INT range_i = INTVAL (range);
1615   rtx int_index;
1616
1617   /* This code uses 'br', so it can deal only with tables of size up to
1618      8192 entries.  */
1619   if (range_i >= 8192)
1620     sorry ("switch statement of size %lu entries too large", 
1621            (unsigned long) range_i);
1622
1623   index = expand_binop (SImode, sub_optab, index, lower_bound, index, 0,
1624                         OPTAB_LIB_WIDEN);
1625   emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1,
1626                            0, default_label);
1627   int_index = gen_lowpart_common (HImode, index);
1628   emit_insn (gen_ashlhi3 (int_index, int_index, GEN_INT (2)));
1629   emit_jump_insn (gen_tablejump_pcrel (int_index, table));
1630 }
1631
1632 /* Output an ADDR_VEC.  It is output as a sequence of 'jmpf'
1633    instructions, without label or alignment or any other special
1634    constructs.  We know that the previous instruction will be the
1635    `tablejump_pcrel' output above.
1636
1637    TODO: it might be nice to output 'br' instructions if they could
1638    all reach.  */
1639
1640 void
1641 stormy16_output_addr_vec (file, label, table)
1642      FILE *file;
1643      rtx label ATTRIBUTE_UNUSED;
1644      rtx table;
1645
1646   int vlen, idx;
1647   
1648   function_section (current_function_decl);
1649
1650   vlen = XVECLEN (table, 0);
1651   for (idx = 0; idx < vlen; idx++)
1652     {
1653       fputs ("\tjmpf ", file);
1654       stormy16_print_operand_address (file, 
1655                                       XEXP (XVECEXP (table, 0, idx), 0));
1656       fputc ('\n', file);
1657     }
1658 }
1659
1660 \f
1661 /* Expander for the `call' patterns.
1662    INDEX is the index of the switch statement.
1663    LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1664      to the first table entry.
1665    RANGE is the number of table entries.
1666    TABLE is an ADDR_VEC that is the jump table.
1667    DEFAULT_LABEL is the address to branch to if INDEX is outside the
1668      range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1669 */
1670
1671 void 
1672 stormy16_expand_call (retval, dest, counter)
1673      rtx retval;
1674      rtx dest;
1675      rtx counter;
1676 {
1677   rtx call, temp;
1678   enum machine_mode mode;
1679
1680   if (GET_CODE (dest) != MEM)
1681     abort ();
1682   dest = XEXP (dest, 0);
1683
1684   if (! CONSTANT_P (dest)
1685       && GET_CODE (dest) != REG)
1686     dest = force_reg (Pmode, dest);
1687   
1688   if (retval == NULL)
1689     mode = VOIDmode;
1690   else
1691     mode = GET_MODE (retval);
1692
1693   call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest),
1694                        counter);
1695   if (retval)
1696     call = gen_rtx_SET (VOIDmode, retval, call);
1697   
1698   if (! CONSTANT_P (dest))
1699     {
1700       temp = gen_reg_rtx (HImode);
1701       emit_move_insn (temp, const0_rtx);
1702     }
1703   else
1704     temp = const0_rtx;
1705   
1706   call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, 
1707                                                 gen_rtx_USE (VOIDmode, temp)));
1708   emit_call_insn (call);
1709 }
1710 \f
1711 /* Expanders for multiword computational operations.  */
1712
1713 /* Expander for arithmetic operations; emit insns to compute
1714
1715    (set DEST (CODE:MODE SRC0 SRC1))
1716    
1717    using CARRY as a temporary.  When CODE is COMPARE, a branch
1718    template is generated (this saves duplicating code in
1719    stormy16_split_cbranch).  */
1720
1721 void 
1722 stormy16_expand_arith (mode, code, dest, src0, src1, carry)
1723      enum machine_mode mode;
1724      enum rtx_code code;
1725      rtx dest;
1726      rtx src0;
1727      rtx src1;
1728      rtx carry;
1729 {
1730   int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
1731   int i;
1732   int firstloop = 1;
1733
1734   if (code == NEG)
1735     {
1736       rtx zero_reg = gen_reg_rtx (word_mode);
1737       emit_move_insn (zero_reg, src0);
1738       src0 = zero_reg;
1739     }
1740   
1741   for (i = 0; i < num_words; i++)
1742     {
1743       rtx w_src0, w_src1, w_dest;
1744       rtx insn;
1745       
1746       if (code == NEG)
1747         w_src0 = src0;
1748       else
1749         w_src0 = simplify_gen_subreg (word_mode, src0, mode, 
1750                                       i * UNITS_PER_WORD);
1751       w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD);
1752       w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD);
1753
1754       switch (code)
1755         {
1756         case PLUS:
1757           if (firstloop
1758               && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1759             continue;
1760           
1761           if (firstloop)
1762             insn = gen_addchi4 (w_dest, w_src0, w_src1, carry);
1763           else
1764             insn = gen_addchi5 (w_dest, w_src0, w_src1, carry, carry);
1765           break;
1766
1767         case NEG:
1768         case MINUS:
1769         case COMPARE:
1770           if (code == COMPARE && i == num_words - 1)
1771             {
1772               rtx branch, sub, clobber, sub_1;
1773               
1774               sub_1 = gen_rtx_MINUS (HImode, w_src0, 
1775                                      gen_rtx_ZERO_EXTEND (HImode, carry));
1776               sub = gen_rtx_SET (VOIDmode, w_dest,
1777                                  gen_rtx_MINUS (HImode, sub_1, w_src1));
1778               clobber = gen_rtx_CLOBBER (VOIDmode, carry);
1779               branch = gen_rtx_SET (VOIDmode, pc_rtx,
1780                                     gen_rtx_IF_THEN_ELSE (VOIDmode,
1781                                                           gen_rtx_EQ (HImode,
1782                                                                       sub_1,
1783                                                                       w_src1),
1784                                                           pc_rtx,
1785                                                           pc_rtx));
1786               insn = gen_rtx_PARALLEL (VOIDmode,
1787                                        gen_rtvec (3, branch, sub, clobber));
1788             }
1789           else if (firstloop
1790                    && code != COMPARE
1791                    && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1792             continue;
1793           else if (firstloop)
1794             insn = gen_subchi4 (w_dest, w_src0, w_src1, carry);
1795           else
1796             insn = gen_subchi5 (w_dest, w_src0, w_src1, carry, carry);
1797           break;
1798
1799         case IOR:
1800         case XOR:
1801         case AND:
1802           if (GET_CODE (w_src1) == CONST_INT 
1803               && INTVAL (w_src1) == -(code == AND))
1804             continue;
1805           
1806           insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx (code, mode,
1807                                                          w_src0, w_src1));
1808           break;
1809
1810         case NOT:
1811           insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx_NOT (mode, w_src0));
1812           break;
1813
1814         default:
1815           abort ();
1816         }
1817       
1818       firstloop = 0;
1819       emit (insn);
1820     }
1821 }
1822
1823 /* Return 1 if OP is a shift operator.  */
1824
1825 int
1826 shift_operator (op, mode)
1827      register rtx op;
1828      enum machine_mode mode ATTRIBUTE_UNUSED;
1829 {
1830   enum rtx_code code = GET_CODE (op);
1831
1832   return (code == ASHIFT
1833           || code == ASHIFTRT
1834           || code == LSHIFTRT);
1835 }
1836
1837 /* The shift operations are split at output time for constant values;
1838    variable-width shifts get handed off to a library routine.  
1839
1840    Generate an output string to do (set X (CODE:MODE X SIZE_R))
1841    SIZE_R will be a CONST_INT, X will be a hard register.  */
1842
1843 const char * 
1844 stormy16_output_shift (mode, code, x, size_r, temp)
1845      enum machine_mode mode;
1846      enum rtx_code code;
1847      rtx x;
1848      rtx size_r;
1849      rtx temp;
1850 {
1851   HOST_WIDE_INT size;
1852   const char *r0, *r1, *rt;
1853   static char r[64];
1854
1855   if (GET_CODE (size_r) != CONST_INT
1856       || GET_CODE (x) != REG
1857       || mode != SImode)
1858     abort ();
1859   size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1);
1860
1861   if (size == 0)
1862     return "";
1863
1864   r0 = reg_names [REGNO (x)];
1865   r1 = reg_names [REGNO (x) + 1];
1866
1867   /* For shifts of size 1, we can use the rotate instructions.  */
1868   if (size == 1)
1869     {
1870       switch (code)
1871         {
1872         case ASHIFT:
1873           sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1);
1874           break;
1875         case ASHIFTRT:
1876           sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0);
1877           break;
1878         case LSHIFTRT:
1879           sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0);
1880           break;
1881         default:
1882           abort ();
1883         }
1884       return r;
1885     }
1886   
1887   /* For large shifts, there are easy special cases.  */
1888   if (size == 16)
1889     {
1890       switch (code)
1891         {
1892         case ASHIFT:
1893           sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0);
1894           break;
1895         case ASHIFTRT:
1896           sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1);
1897           break;
1898         case LSHIFTRT:
1899           sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1);
1900           break;
1901         default:
1902           abort ();
1903         }
1904       return r;
1905     }
1906   if (size > 16)
1907     {
1908       switch (code)
1909         {
1910         case ASHIFT:
1911           sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d", 
1912                    r1, r0, r0, r1, (int) size - 16);
1913           break;
1914         case ASHIFTRT:
1915           sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d", 
1916                    r0, r1, r1, r0, (int) size - 16);
1917           break;
1918         case LSHIFTRT:
1919           sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d", 
1920                    r0, r1, r1, r0, (int) size - 16);
1921           break;
1922         default:
1923           abort ();
1924         }
1925       return r;
1926     }
1927
1928   /* For the rest, we have to do more work.  In particular, we
1929      need a temporary.  */
1930   rt = reg_names [REGNO (temp)];
1931   switch (code)
1932     {
1933     case ASHIFT:
1934       sprintf (r, 
1935                "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s", 
1936                rt, r0, r0, (int) size, r1, (int) size, rt, (int) 16-size,
1937                r1, rt);
1938       break;
1939     case ASHIFTRT:
1940       sprintf (r, 
1941                "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", 
1942                rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
1943                r0, rt);
1944       break;
1945     case LSHIFTRT:
1946       sprintf (r, 
1947                "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", 
1948                rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
1949                r0, rt);
1950       break;
1951     default:
1952       abort ();
1953     }
1954   return r;
1955 }
1956 \f
1957 /* Attribute handling.  */
1958
1959 /* Return nonzero if the function is an interrupt function.  */
1960 int
1961 stormy16_interrupt_function_p ()
1962 {
1963   tree attributes;
1964   
1965   /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
1966      any functions are declared, which is demonstrably wrong, but
1967      it is worked around here.  FIXME.  */
1968   if (!cfun)
1969     return 0;
1970
1971   attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1972   return lookup_attribute ("interrupt", attributes) != NULL_TREE;
1973 }
1974
1975 #undef TARGET_ATTRIBUTE_TABLE
1976 #define TARGET_ATTRIBUTE_TABLE stormy16_attribute_table
1977 static tree stormy16_handle_interrupt_attribute PARAMS ((tree *, tree, tree, int, bool *));
1978 static const struct attribute_spec stormy16_attribute_table[] =
1979 {
1980   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
1981   { "interrupt", 0, 0, false, true,  true,  stormy16_handle_interrupt_attribute },
1982   { NULL,        0, 0, false, false, false, NULL }
1983 };
1984
1985 /* Handle an "interrupt" attribute;
1986    arguments as in struct attribute_spec.handler.  */
1987 static tree
1988 stormy16_handle_interrupt_attribute (node, name, args, flags, no_add_attrs)
1989      tree *node;
1990      tree name;
1991      tree args ATTRIBUTE_UNUSED;
1992      int flags ATTRIBUTE_UNUSED;
1993      bool *no_add_attrs;
1994 {
1995   if (TREE_CODE (*node) != FUNCTION_TYPE)
1996     {
1997       warning ("`%s' attribute only applies to functions",
1998                IDENTIFIER_POINTER (name));
1999       *no_add_attrs = true;
2000     }
2001
2002   return NULL_TREE;
2003 }
2004 \f
2005 struct gcc_target targetm = TARGET_INITIALIZER;