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