* Makefile.in (cse.o): Depend on TARGET_H.
[official-gcc.git] / gcc / config / stormy16 / stormy16.c
blob7a034626dd9efbaf270a910f6c73ff9c9c28eff2
1 /* Xstormy16 target functions.
2 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Contributed by Red Hat, Inc.
6 This file is part of GNU CC.
8 GNU CC is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
13 GNU CC is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU CC; see the file COPYING. If not, write to
20 the Free Software Foundation, 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "tm.h"
27 #include "rtl.h"
28 #include "regs.h"
29 #include "hard-reg-set.h"
30 #include "real.h"
31 #include "insn-config.h"
32 #include "conditions.h"
33 #include "insn-flags.h"
34 #include "output.h"
35 #include "insn-attr.h"
36 #include "flags.h"
37 #include "recog.h"
38 #include "toplev.h"
39 #include "obstack.h"
40 #include "tree.h"
41 #include "expr.h"
42 #include "optabs.h"
43 #include "output.h"
44 #include "except.h"
45 #include "function.h"
46 #include "target.h"
47 #include "target-def.h"
48 #include "tm_p.h"
49 #include "langhooks.h"
51 static rtx emit_addhi3_postreload PARAMS ((rtx, rtx, rtx));
52 static void xstormy16_asm_out_constructor PARAMS ((rtx, int));
53 static void xstormy16_asm_out_destructor PARAMS ((rtx, int));
54 static void xstormy16_encode_section_info PARAMS ((tree, int));
55 static void xstormy16_asm_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
56 HOST_WIDE_INT, tree));
58 static void xstormy16_init_builtins PARAMS ((void));
59 static rtx xstormy16_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
60 static bool xstormy16_rtx_costs PARAMS ((rtx, int, int, int *));
62 /* Define the information needed to generate branch and scc insns. This is
63 stored from the compare operation. */
64 struct rtx_def * xstormy16_compare_op0;
65 struct rtx_def * xstormy16_compare_op1;
67 /* Return 1 if this is a LT, GE, LTU, or GEU operator. */
69 int
70 xstormy16_ineqsi_operator (op, mode)
71 register rtx op;
72 enum machine_mode mode;
74 enum rtx_code code = GET_CODE (op);
76 return ((mode == VOIDmode || GET_MODE (op) == mode)
77 && (code == LT || code == GE || code == LTU || code == GEU));
80 /* Return 1 if this is an EQ or NE operator. */
82 int
83 equality_operator (op, mode)
84 register rtx op;
85 enum machine_mode mode;
87 return ((mode == VOIDmode || GET_MODE (op) == mode)
88 && (GET_CODE (op) == EQ || GET_CODE (op) == NE));
91 /* Return 1 if this is a comparison operator but not an EQ or NE operator. */
93 int
94 inequality_operator (op, mode)
95 register rtx op;
96 enum machine_mode mode;
98 return comparison_operator (op, mode) && ! equality_operator (op, mode);
101 /* Compute a (partial) cost for rtx X. Return true if the complete
102 cost has been computed, and false if subexpressions should be
103 scanned. In either case, *TOTAL contains the cost result. */
105 static bool
106 xstormy16_rtx_costs (x, code, outer_code, total)
107 rtx x;
108 int code, outer_code ATTRIBUTE_UNUSED;
109 int *total;
111 switch (code)
113 case CONST_INT:
114 if (INTVAL (x) < 16 && INTVAL (x) >= 0)
115 *total = COSTS_N_INSNS (1) / 2;
116 else if (INTVAL (x) < 256 && INTVAL (x) >= 0)
117 *total = COSTS_N_INSNS (1);
118 else
119 *total = COSTS_N_INSNS (2);
120 return true;
122 case CONST_DOUBLE:
123 case CONST:
124 case SYMBOL_REF:
125 case LABEL_REF:
126 *total = COSTS_N_INSNS(2);
127 return true;
129 case MULT:
130 *total = COSTS_N_INSNS (35 + 6);
131 return true;
132 case DIV:
133 *total = COSTS_N_INSNS (51 - 6);
134 return true;
136 default:
137 return false;
142 /* Branches are handled as follows:
144 1. HImode compare-and-branches. The machine supports these
145 natively, so the appropriate pattern is emitted directly.
147 2. SImode EQ and NE. These are emitted as pairs of HImode
148 compare-and-branches.
150 3. SImode LT, GE, LTU and GEU. These are emitted as a sequence
151 of a SImode subtract followed by a branch (not a compare-and-branch),
152 like this:
157 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like:
165 /* Emit a branch of kind CODE to location LOC. */
167 void
168 xstormy16_emit_cbranch (code, loc)
169 enum rtx_code code;
170 rtx loc;
172 rtx op0 = xstormy16_compare_op0;
173 rtx op1 = xstormy16_compare_op1;
174 rtx condition_rtx, loc_ref, branch, cy_clobber;
175 rtvec vec;
176 enum machine_mode mode;
178 mode = GET_MODE (op0);
179 if (mode != HImode && mode != SImode)
180 abort ();
182 if (mode == SImode
183 && (code == GT || code == LE || code == GTU || code == LEU))
185 int unsigned_p = (code == GTU || code == LEU);
186 int gt_p = (code == GT || code == GTU);
187 rtx lab = NULL_RTX;
189 if (gt_p)
190 lab = gen_label_rtx ();
191 xstormy16_emit_cbranch (unsigned_p ? LTU : LT, gt_p ? lab : loc);
192 /* This should be generated as a comparison against the temporary
193 created by the previous insn, but reload can't handle that. */
194 xstormy16_emit_cbranch (gt_p ? NE : EQ, loc);
195 if (gt_p)
196 emit_label (lab);
197 return;
199 else if (mode == SImode
200 && (code == NE || code == EQ)
201 && op1 != const0_rtx)
203 rtx lab = NULL_RTX;
204 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
205 int i;
207 if (code == EQ)
208 lab = gen_label_rtx ();
210 for (i = 0; i < num_words - 1; i++)
212 xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode,
213 i * UNITS_PER_WORD);
214 xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode,
215 i * UNITS_PER_WORD);
216 xstormy16_emit_cbranch (NE, code == EQ ? lab : loc);
218 xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode,
219 i * UNITS_PER_WORD);
220 xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode,
221 i * UNITS_PER_WORD);
222 xstormy16_emit_cbranch (code, loc);
224 if (code == EQ)
225 emit_label (lab);
226 return;
229 /* We can't allow reload to try to generate any reload after a branch,
230 so when some register must match we must make the temporary ourselves. */
231 if (mode != HImode)
233 rtx tmp;
234 tmp = gen_reg_rtx (mode);
235 emit_move_insn (tmp, op0);
236 op0 = tmp;
239 condition_rtx = gen_rtx (code, mode, op0, op1);
240 loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
241 branch = gen_rtx_SET (VOIDmode, pc_rtx,
242 gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
243 loc_ref, pc_rtx));
245 cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (BImode));
247 if (mode == HImode)
248 vec = gen_rtvec (2, branch, cy_clobber);
249 else if (code == NE || code == EQ)
250 vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0));
251 else
253 rtx sub;
254 #if 0
255 sub = gen_rtx_SET (VOIDmode, op0, gen_rtx_MINUS (SImode, op0, op1));
256 #else
257 sub = gen_rtx_CLOBBER (SImode, op0);
258 #endif
259 vec = gen_rtvec (3, branch, sub, cy_clobber);
262 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
265 /* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split
266 the arithmetic operation. Most of the work is done by
267 xstormy16_expand_arith. */
269 void
270 xstormy16_split_cbranch (mode, label, comparison, dest, carry)
271 enum machine_mode mode;
272 rtx label;
273 rtx comparison;
274 rtx dest;
275 rtx carry;
277 rtx op0 = XEXP (comparison, 0);
278 rtx op1 = XEXP (comparison, 1);
279 rtx seq, last_insn;
280 rtx compare;
282 start_sequence ();
283 xstormy16_expand_arith (mode, COMPARE, dest, op0, op1, carry);
284 seq = get_insns ();
285 end_sequence ();
287 if (! INSN_P (seq))
288 abort ();
290 last_insn = seq;
291 while (NEXT_INSN (last_insn) != NULL_RTX)
292 last_insn = NEXT_INSN (last_insn);
294 compare = SET_SRC (XVECEXP (PATTERN (last_insn), 0, 0));
295 PUT_CODE (XEXP (compare, 0), GET_CODE (comparison));
296 XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label);
297 emit_insn (seq);
301 /* Return the string to output a conditional branch to LABEL, which is
302 the operand number of the label.
304 OP is the conditional expression, or NULL for branch-always.
306 REVERSED is nonzero if we should reverse the sense of the comparison.
308 INSN is the insn. */
310 char *
311 xstormy16_output_cbranch_hi (op, label, reversed, insn)
312 rtx op;
313 const char * label;
314 int reversed;
315 rtx insn;
317 static char string[64];
318 int need_longbranch = (op != NULL_RTX
319 ? get_attr_length (insn) == 8
320 : get_attr_length (insn) == 4);
321 int really_reversed = reversed ^ need_longbranch;
322 const char *ccode;
323 const char *template;
324 const char *operands;
325 enum rtx_code code;
327 if (! op)
329 if (need_longbranch)
330 ccode = "jmpf";
331 else
332 ccode = "br";
333 sprintf (string, "%s %s", ccode, label);
334 return string;
337 code = GET_CODE (op);
339 if (GET_CODE (XEXP (op, 0)) != REG)
341 code = swap_condition (code);
342 operands = "%3,%2";
344 else
345 operands = "%2,%3";
347 /* Work out which way this really branches. */
348 if (really_reversed)
349 code = reverse_condition (code);
351 switch (code)
353 case EQ: ccode = "z"; break;
354 case NE: ccode = "nz"; break;
355 case GE: ccode = "ge"; break;
356 case LT: ccode = "lt"; break;
357 case GT: ccode = "gt"; break;
358 case LE: ccode = "le"; break;
359 case GEU: ccode = "nc"; break;
360 case LTU: ccode = "c"; break;
361 case GTU: ccode = "hi"; break;
362 case LEU: ccode = "ls"; break;
364 default:
365 abort ();
368 if (need_longbranch)
369 template = "b%s %s,.+8 | jmpf %s";
370 else
371 template = "b%s %s,%s";
372 sprintf (string, template, ccode, operands, label);
374 return string;
377 /* Return the string to output a conditional branch to LABEL, which is
378 the operand number of the label, but suitable for the tail of a
379 SImode branch.
381 OP is the conditional expression (OP is never NULL_RTX).
383 REVERSED is nonzero if we should reverse the sense of the comparison.
385 INSN is the insn. */
387 char *
388 xstormy16_output_cbranch_si (op, label, reversed, insn)
389 rtx op;
390 const char * label;
391 int reversed;
392 rtx insn;
394 static char string[64];
395 int need_longbranch = get_attr_length (insn) >= 8;
396 int really_reversed = reversed ^ need_longbranch;
397 const char *ccode;
398 const char *template;
399 char prevop[16];
400 enum rtx_code code;
402 code = GET_CODE (op);
404 /* Work out which way this really branches. */
405 if (really_reversed)
406 code = reverse_condition (code);
408 switch (code)
410 case EQ: ccode = "z"; break;
411 case NE: ccode = "nz"; break;
412 case GE: ccode = "ge"; break;
413 case LT: ccode = "lt"; break;
414 case GEU: ccode = "nc"; break;
415 case LTU: ccode = "c"; break;
417 /* The missing codes above should never be generated. */
418 default:
419 abort ();
422 switch (code)
424 case EQ: case NE:
426 int regnum;
428 if (GET_CODE (XEXP (op, 0)) != REG)
429 abort ();
431 regnum = REGNO (XEXP (op, 0));
432 sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]);
434 break;
436 case GE: case LT: case GEU: case LTU:
437 strcpy (prevop, "sbc %2,%3");
438 break;
440 default:
441 abort ();
444 if (need_longbranch)
445 template = "%s | b%s .+6 | jmpf %s";
446 else
447 template = "%s | b%s %s";
448 sprintf (string, template, prevop, ccode, label);
450 return string;
453 /* Many machines have some registers that cannot be copied directly to or from
454 memory or even from other types of registers. An example is the `MQ'
455 register, which on most machines, can only be copied to or from general
456 registers, but not memory. Some machines allow copying all registers to and
457 from memory, but require a scratch register for stores to some memory
458 locations (e.g., those with symbolic address on the RT, and those with
459 certain symbolic address on the SPARC when compiling PIC). In some cases,
460 both an intermediate and a scratch register are required.
462 You should define these macros to indicate to the reload phase that it may
463 need to allocate at least one register for a reload in addition to the
464 register to contain the data. Specifically, if copying X to a register
465 CLASS in MODE requires an intermediate register, you should define
466 `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
467 whose registers can be used as intermediate registers or scratch registers.
469 If copying a register CLASS in MODE to X requires an intermediate or scratch
470 register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
471 largest register class required. If the requirements for input and output
472 reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
473 instead of defining both macros identically.
475 The values returned by these macros are often `GENERAL_REGS'. Return
476 `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
477 to or from a register of CLASS in MODE without requiring a scratch register.
478 Do not define this macro if it would always return `NO_REGS'.
480 If a scratch register is required (either with or without an intermediate
481 register), you should define patterns for `reload_inM' or `reload_outM', as
482 required.. These patterns, which will normally be implemented with a
483 `define_expand', should be similar to the `movM' patterns, except that
484 operand 2 is the scratch register.
486 Define constraints for the reload register and scratch register that contain
487 a single register class. If the original reload register (whose class is
488 CLASS) can meet the constraint given in the pattern, the value returned by
489 these macros is used for the class of the scratch register. Otherwise, two
490 additional reload registers are required. Their classes are obtained from
491 the constraints in the insn pattern.
493 X might be a pseudo-register or a `subreg' of a pseudo-register, which could
494 either be in a hard register or in memory. Use `true_regnum' to find out;
495 it will return -1 if the pseudo is in memory and the hard register number if
496 it is in a register.
498 These macros should not be used in the case where a particular class of
499 registers can only be copied to memory and not to another class of
500 registers. In that case, secondary reload registers are not needed and
501 would not be helpful. Instead, a stack location must be used to perform the
502 copy and the `movM' pattern should use memory as an intermediate storage.
503 This case often occurs between floating-point and general registers. */
505 enum reg_class
506 xstormy16_secondary_reload_class (class, mode, x)
507 enum reg_class class;
508 enum machine_mode mode;
509 rtx x;
511 /* This chip has the interesting property that only the first eight
512 registers can be moved to/from memory. */
513 if ((GET_CODE (x) == MEM
514 || ((GET_CODE (x) == SUBREG || GET_CODE (x) == REG)
515 && (true_regnum (x) == -1
516 || true_regnum (x) >= FIRST_PSEUDO_REGISTER)))
517 && ! reg_class_subset_p (class, EIGHT_REGS))
518 return EIGHT_REGS;
520 /* When reloading a PLUS, the carry register will be required
521 unless the inc or dec instructions can be used. */
522 if (xstormy16_carry_plus_operand (x, mode))
523 return CARRY_REGS;
525 return NO_REGS;
528 /* Recognize a PLUS that needs the carry register. */
530 xstormy16_carry_plus_operand (x, mode)
531 rtx x;
532 enum machine_mode mode ATTRIBUTE_UNUSED;
534 return (GET_CODE (x) == PLUS
535 && GET_CODE (XEXP (x, 1)) == CONST_INT
536 && (INTVAL (XEXP (x, 1)) < -4 || INTVAL (XEXP (x, 1)) > 4));
540 enum reg_class
541 xstormy16_preferred_reload_class (x, class)
542 enum reg_class class;
543 rtx x;
545 if (class == GENERAL_REGS
546 && GET_CODE (x) == MEM)
547 return EIGHT_REGS;
549 return class;
552 #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \
553 (GET_CODE (X) == CONST_INT \
554 && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
556 #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \
557 (GET_CODE (X) == CONST_INT \
558 && INTVAL (X) + (OFFSET) >= 0 \
559 && INTVAL (X) + (OFFSET) < 0x8000 \
560 && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
563 xstormy16_legitimate_address_p (mode, x, strict)
564 enum machine_mode mode ATTRIBUTE_UNUSED;
565 rtx x;
566 int strict;
568 if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0))
569 return 1;
571 if (GET_CODE (x) == PLUS
572 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0))
573 x = XEXP (x, 0);
575 if (GET_CODE (x) == POST_INC
576 || GET_CODE (x) == PRE_DEC)
577 x = XEXP (x, 0);
579 if (GET_CODE (x) == REG && REGNO_OK_FOR_BASE_P (REGNO (x))
580 && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER))
581 return 1;
583 return 0;
586 /* Return nonzero if memory address X (an RTX) can have different
587 meanings depending on the machine mode of the memory reference it
588 is used for or if the address is valid for some modes but not
589 others.
591 Autoincrement and autodecrement addresses typically have mode-dependent
592 effects because the amount of the increment or decrement is the size of the
593 operand being addressed. Some machines have other mode-dependent addresses.
594 Many RISC machines have no mode-dependent addresses.
596 You may assume that ADDR is a valid address for the machine.
598 On this chip, this is true if the address is valid with an offset
599 of 0 but not of 6, because in that case it cannot be used as an
600 address for DImode or DFmode, or if the address is a post-increment
601 or pre-decrement address. */
603 xstormy16_mode_dependent_address_p (x)
604 rtx x;
606 if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)
607 && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6))
608 return 1;
610 if (GET_CODE (x) == PLUS
611 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)
612 && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6))
613 return 1;
615 if (GET_CODE (x) == PLUS)
616 x = XEXP (x, 0);
618 if (GET_CODE (x) == POST_INC
619 || GET_CODE (x) == PRE_DEC)
620 return 1;
622 return 0;
625 /* A C expression that defines the optional machine-dependent constraint
626 letters (`Q', `R', `S', `T', `U') that can be used to segregate specific
627 types of operands, usually memory references, for the target machine.
628 Normally this macro will not be defined. If it is required for a particular
629 target machine, it should return 1 if VALUE corresponds to the operand type
630 represented by the constraint letter C. If C is not defined as an extra
631 constraint, the value returned should be 0 regardless of VALUE. */
633 xstormy16_extra_constraint_p (x, c)
634 rtx x;
635 int c;
637 switch (c)
639 /* 'Q' is for pushes. */
640 case 'Q':
641 return (GET_CODE (x) == MEM
642 && GET_CODE (XEXP (x, 0)) == POST_INC
643 && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
645 /* 'R' is for pops. */
646 case 'R':
647 return (GET_CODE (x) == MEM
648 && GET_CODE (XEXP (x, 0)) == PRE_DEC
649 && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
651 /* 'S' is for immediate memory addresses. */
652 case 'S':
653 return (GET_CODE (x) == MEM
654 && GET_CODE (XEXP (x, 0)) == CONST_INT
655 && xstormy16_legitimate_address_p (VOIDmode, XEXP (x, 0), 0));
657 /* 'T' is for Rx. */
658 case 'T':
659 /* Not implemented yet. */
660 return 0;
662 /* 'U' is for CONST_INT values not between 2 and 15 inclusive,
663 for allocating a scratch register for 32-bit shifts. */
664 case 'U':
665 return (GET_CODE (x) == CONST_INT
666 && (INTVAL (x) < 2 || INTVAL (x) > 15));
668 default:
669 return 0;
674 short_memory_operand (x, mode)
675 rtx x;
676 enum machine_mode mode;
678 if (! memory_operand (x, mode))
679 return 0;
680 return (GET_CODE (XEXP (x, 0)) != PLUS);
684 nonimmediate_nonstack_operand (op, mode)
685 rtx op;
686 enum machine_mode mode;
688 /* 'Q' is for pushes, 'R' for pops. */
689 return (nonimmediate_operand (op, mode)
690 && ! xstormy16_extra_constraint_p (op, 'Q')
691 && ! xstormy16_extra_constraint_p (op, 'R'));
694 /* Splitter for the 'move' patterns, for modes not directly implemeted
695 by hardware. Emit insns to copy a value of mode MODE from SRC to
696 DEST.
698 This function is only called when reload_completed.
701 void
702 xstormy16_split_move (mode, dest, src)
703 enum machine_mode mode;
704 rtx dest;
705 rtx src;
707 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
708 int direction, end, i;
709 int src_modifies = 0;
710 int dest_modifies = 0;
711 int src_volatile = 0;
712 int dest_volatile = 0;
713 rtx mem_operand;
714 rtx auto_inc_reg_rtx = NULL_RTX;
716 /* Check initial conditions. */
717 if (! reload_completed
718 || mode == QImode || mode == HImode
719 || ! nonimmediate_operand (dest, mode)
720 || ! general_operand (src, mode))
721 abort ();
723 /* This case is not supported below, and shouldn't be generated. */
724 if (GET_CODE (dest) == MEM
725 && GET_CODE (src) == MEM)
726 abort ();
728 /* This case is very very bad after reload, so trap it now. */
729 if (GET_CODE (dest) == SUBREG
730 || GET_CODE (src) == SUBREG)
731 abort ();
733 /* The general idea is to copy by words, offsetting the source and
734 destination. Normally the least-significant word will be copied
735 first, but for pre-dec operations it's better to copy the
736 most-significant word first. Only one operand can be a pre-dec
737 or post-inc operand.
739 It's also possible that the copy overlaps so that the direction
740 must be reversed. */
741 direction = 1;
743 if (GET_CODE (dest) == MEM)
745 mem_operand = XEXP (dest, 0);
746 dest_modifies = side_effects_p (mem_operand);
747 if (auto_inc_p (mem_operand))
748 auto_inc_reg_rtx = XEXP (mem_operand, 0);
749 dest_volatile = MEM_VOLATILE_P (dest);
750 if (dest_volatile)
752 dest = copy_rtx (dest);
753 MEM_VOLATILE_P (dest) = 0;
756 else if (GET_CODE (src) == MEM)
758 mem_operand = XEXP (src, 0);
759 src_modifies = side_effects_p (mem_operand);
760 if (auto_inc_p (mem_operand))
761 auto_inc_reg_rtx = XEXP (mem_operand, 0);
762 src_volatile = MEM_VOLATILE_P (src);
763 if (src_volatile)
765 src = copy_rtx (src);
766 MEM_VOLATILE_P (src) = 0;
769 else
770 mem_operand = NULL_RTX;
772 if (mem_operand == NULL_RTX)
774 if (GET_CODE (src) == REG
775 && GET_CODE (dest) == REG
776 && reg_overlap_mentioned_p (dest, src)
777 && REGNO (dest) > REGNO (src))
778 direction = -1;
780 else if (GET_CODE (mem_operand) == PRE_DEC
781 || (GET_CODE (mem_operand) == PLUS
782 && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC))
783 direction = -1;
784 else if (GET_CODE (src) == MEM
785 && reg_overlap_mentioned_p (dest, src))
787 int regno;
788 if (GET_CODE (dest) != REG)
789 abort ();
790 regno = REGNO (dest);
792 if (! refers_to_regno_p (regno, regno + num_words, mem_operand, 0))
793 abort ();
795 if (refers_to_regno_p (regno, regno + 1, mem_operand, 0))
796 direction = -1;
797 else if (refers_to_regno_p (regno + num_words - 1, regno + num_words,
798 mem_operand, 0))
799 direction = 1;
800 else
801 /* This means something like
802 (set (reg:DI r0) (mem:DI (reg:HI r1)))
803 which we'd need to support by doing the set of the second word
804 last. */
805 abort ();
808 end = direction < 0 ? -1 : num_words;
809 for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction)
811 rtx w_src, w_dest, insn;
813 if (src_modifies)
814 w_src = gen_rtx_MEM (word_mode, mem_operand);
815 else
816 w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD);
817 if (src_volatile)
818 MEM_VOLATILE_P (w_src) = 1;
819 if (dest_modifies)
820 w_dest = gen_rtx_MEM (word_mode, mem_operand);
821 else
822 w_dest = simplify_gen_subreg (word_mode, dest, mode,
823 i * UNITS_PER_WORD);
824 if (dest_volatile)
825 MEM_VOLATILE_P (w_dest) = 1;
827 /* The simplify_subreg calls must always be able to simplify. */
828 if (GET_CODE (w_src) == SUBREG
829 || GET_CODE (w_dest) == SUBREG)
830 abort ();
832 insn = emit_insn (gen_rtx_SET (VOIDmode, w_dest, w_src));
833 if (auto_inc_reg_rtx)
834 REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
835 auto_inc_reg_rtx,
836 REG_NOTES (insn));
840 /* Expander for the 'move' patterns. Emit insns to copy a value of
841 mode MODE from SRC to DEST. */
843 void
844 xstormy16_expand_move (mode, dest, src)
845 enum machine_mode mode;
846 rtx dest;
847 rtx src;
849 /* There are only limited immediate-to-memory move instructions. */
850 if (! reload_in_progress
851 && ! reload_completed
852 && GET_CODE (dest) == MEM
853 && (GET_CODE (XEXP (dest, 0)) != CONST_INT
854 || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0))
855 && GET_CODE (src) != REG
856 && GET_CODE (src) != SUBREG)
857 src = copy_to_mode_reg (mode, src);
859 /* Don't emit something we would immediately split. */
860 if (reload_completed
861 && mode != HImode && mode != QImode)
863 xstormy16_split_move (mode, dest, src);
864 return;
867 emit_insn (gen_rtx_SET (VOIDmode, dest, src));
871 /* Stack Layout:
873 The stack is laid out as follows:
875 SP->
876 FP-> Local variables
877 Register save area (up to 4 words)
878 Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
880 AP-> Return address (two words)
881 9th procedure parameter word
882 10th procedure parameter word
884 last procedure parameter word
886 The frame pointer location is tuned to make it most likely that all
887 parameters and local variables can be accessed using a load-indexed
888 instruction. */
890 /* A structure to describe the layout. */
891 struct xstormy16_stack_layout
893 /* Size of the topmost three items on the stack. */
894 int locals_size;
895 int register_save_size;
896 int stdarg_save_size;
897 /* Sum of the above items. */
898 int frame_size;
899 /* Various offsets. */
900 int first_local_minus_ap;
901 int sp_minus_fp;
902 int fp_minus_ap;
905 /* Does REGNO need to be saved? */
906 #define REG_NEEDS_SAVE(REGNUM, IFUN) \
907 ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM]) \
908 || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM] \
909 && (regs_ever_live[REGNUM] || ! current_function_is_leaf)))
911 /* Compute the stack layout. */
912 struct xstormy16_stack_layout
913 xstormy16_compute_stack_layout ()
915 struct xstormy16_stack_layout layout;
916 int regno;
917 const int ifun = xstormy16_interrupt_function_p ();
919 layout.locals_size = get_frame_size ();
921 layout.register_save_size = 0;
922 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
923 if (REG_NEEDS_SAVE (regno, ifun))
924 layout.register_save_size += UNITS_PER_WORD;
926 if (current_function_stdarg)
927 layout.stdarg_save_size = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
928 else
929 layout.stdarg_save_size = 0;
931 layout.frame_size = (layout.locals_size
932 + layout.register_save_size
933 + layout.stdarg_save_size);
935 if (current_function_args_size <= 2048 && current_function_args_size != -1)
937 if (layout.frame_size + INCOMING_FRAME_SP_OFFSET
938 + current_function_args_size <= 2048)
939 layout.fp_minus_ap = layout.frame_size + INCOMING_FRAME_SP_OFFSET;
940 else
941 layout.fp_minus_ap = 2048 - current_function_args_size;
943 else
944 layout.fp_minus_ap = (layout.stdarg_save_size
945 + layout.register_save_size
946 + INCOMING_FRAME_SP_OFFSET);
947 layout.sp_minus_fp = (layout.frame_size + INCOMING_FRAME_SP_OFFSET
948 - layout.fp_minus_ap);
949 layout.first_local_minus_ap = layout.sp_minus_fp - layout.locals_size;
950 return layout;
953 /* Determine how all the special registers get eliminated. */
955 xstormy16_initial_elimination_offset (from, to)
956 int from, to;
958 struct xstormy16_stack_layout layout;
959 int result;
961 layout = xstormy16_compute_stack_layout ();
963 if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
964 result = layout.sp_minus_fp - layout.locals_size;
965 else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
966 result = -layout.locals_size;
967 else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
968 result = -layout.fp_minus_ap;
969 else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
970 result = -(layout.sp_minus_fp + layout.fp_minus_ap);
971 else
972 abort ();
974 return result;
977 static rtx
978 emit_addhi3_postreload (dest, src0, src1)
979 rtx dest;
980 rtx src0;
981 rtx src1;
983 rtx set, clobber, insn;
985 set = gen_rtx_SET (VOIDmode, dest, gen_rtx_PLUS (HImode, src0, src1));
986 clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, 16));
987 insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
988 return insn;
991 /* Called after register allocation to add any instructions needed for
992 the prologue. Using a prologue insn is favored compared to putting
993 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
994 since it allows the scheduler to intermix instructions with the
995 saves of the caller saved registers. In some cases, it might be
996 necessary to emit a barrier instruction as the last insn to prevent
997 such scheduling.
999 Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
1000 so that the debug info generation code can handle them properly. */
1001 void
1002 xstormy16_expand_prologue ()
1004 struct xstormy16_stack_layout layout;
1005 int regno;
1006 rtx insn;
1007 rtx mem_push_rtx;
1008 rtx mem_fake_push_rtx;
1009 const int ifun = xstormy16_interrupt_function_p ();
1011 mem_push_rtx = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
1012 mem_push_rtx = gen_rtx_MEM (HImode, mem_push_rtx);
1013 mem_fake_push_rtx = gen_rtx_PRE_INC (Pmode, stack_pointer_rtx);
1014 mem_fake_push_rtx = gen_rtx_MEM (HImode, mem_fake_push_rtx);
1016 layout = xstormy16_compute_stack_layout ();
1018 /* Save the argument registers if necessary. */
1019 if (layout.stdarg_save_size)
1020 for (regno = FIRST_ARGUMENT_REGISTER;
1021 regno < FIRST_ARGUMENT_REGISTER + NUM_ARGUMENT_REGISTERS;
1022 regno++)
1024 rtx reg = gen_rtx_REG (HImode, regno);
1025 insn = emit_move_insn (mem_push_rtx, reg);
1026 RTX_FRAME_RELATED_P (insn) = 1;
1027 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1028 gen_rtx_SET (VOIDmode,
1029 mem_fake_push_rtx,
1030 reg),
1031 REG_NOTES (insn));
1034 /* Push each of the registers to save. */
1035 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
1036 if (REG_NEEDS_SAVE (regno, ifun))
1038 rtx reg = gen_rtx_REG (HImode, regno);
1039 insn = emit_move_insn (mem_push_rtx, reg);
1040 RTX_FRAME_RELATED_P (insn) = 1;
1041 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1042 gen_rtx_SET (VOIDmode,
1043 mem_fake_push_rtx,
1044 reg),
1045 REG_NOTES (insn));
1048 /* It's just possible that the SP here might be what we need for
1049 the new FP... */
1050 if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1052 insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1053 RTX_FRAME_RELATED_P (insn) = 1;
1056 /* Allocate space for local variables. */
1057 if (layout.locals_size)
1059 insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1060 GEN_INT (layout.locals_size));
1061 RTX_FRAME_RELATED_P (insn) = 1;
1064 /* Set up the frame pointer, if required. */
1065 if (frame_pointer_needed && layout.sp_minus_fp != layout.locals_size)
1067 insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1068 RTX_FRAME_RELATED_P (insn) = 1;
1069 if (layout.sp_minus_fp)
1071 insn = emit_addhi3_postreload (hard_frame_pointer_rtx,
1072 hard_frame_pointer_rtx,
1073 GEN_INT (-layout.sp_minus_fp));
1074 RTX_FRAME_RELATED_P (insn) = 1;
1079 /* Do we need an epilogue at all? */
1081 direct_return ()
1083 return (reload_completed
1084 && xstormy16_compute_stack_layout ().frame_size == 0);
1087 /* Called after register allocation to add any instructions needed for
1088 the epilogue. Using an epilogue insn is favored compared to putting
1089 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1090 since it allows the scheduler to intermix instructions with the
1091 saves of the caller saved registers. In some cases, it might be
1092 necessary to emit a barrier instruction as the last insn to prevent
1093 such scheduling. */
1095 void
1096 xstormy16_expand_epilogue ()
1098 struct xstormy16_stack_layout layout;
1099 rtx mem_pop_rtx;
1100 int regno;
1101 const int ifun = xstormy16_interrupt_function_p ();
1103 mem_pop_rtx = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
1104 mem_pop_rtx = gen_rtx_MEM (HImode, mem_pop_rtx);
1106 layout = xstormy16_compute_stack_layout ();
1108 /* Pop the stack for the locals. */
1109 if (layout.locals_size)
1111 if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1112 emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
1113 else
1114 emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1115 GEN_INT (- layout.locals_size));
1118 /* Restore any call-saved registers. */
1119 for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
1120 if (REG_NEEDS_SAVE (regno, ifun))
1121 emit_move_insn (gen_rtx_REG (HImode, regno), mem_pop_rtx);
1123 /* Pop the stack for the stdarg save area. */
1124 if (layout.stdarg_save_size)
1125 emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1126 GEN_INT (- layout.stdarg_save_size));
1128 /* Return. */
1129 if (ifun)
1130 emit_jump_insn (gen_return_internal_interrupt ());
1131 else
1132 emit_jump_insn (gen_return_internal ());
1136 xstormy16_epilogue_uses (regno)
1137 int regno;
1139 if (reload_completed && call_used_regs[regno])
1141 const int ifun = xstormy16_interrupt_function_p ();
1142 return REG_NEEDS_SAVE (regno, ifun);
1144 return 0;
1147 /* Return an updated summarizer variable CUM to advance past an
1148 argument in the argument list. The values MODE, TYPE and NAMED
1149 describe that argument. Once this is done, the variable CUM is
1150 suitable for analyzing the *following* argument with
1151 `FUNCTION_ARG', etc.
1153 This function need not do anything if the argument in question was
1154 passed on the stack. The compiler knows how to track the amount of
1155 stack space used for arguments without any special help. However,
1156 it makes life easier for xstormy16_build_va_list if it does update
1157 the word count. */
1158 CUMULATIVE_ARGS
1159 xstormy16_function_arg_advance (cum, mode, type, named)
1160 CUMULATIVE_ARGS cum;
1161 enum machine_mode mode;
1162 tree type;
1163 int named ATTRIBUTE_UNUSED;
1165 /* If an argument would otherwise be passed partially in registers,
1166 and partially on the stack, the whole of it is passed on the
1167 stack. */
1168 if (cum < NUM_ARGUMENT_REGISTERS
1169 && cum + XSTORMY16_WORD_SIZE (type, mode) > NUM_ARGUMENT_REGISTERS)
1170 cum = NUM_ARGUMENT_REGISTERS;
1172 cum += XSTORMY16_WORD_SIZE (type, mode);
1174 return cum;
1177 /* Do any needed setup for a variadic function. CUM has not been updated
1178 for the last named argument which has type TYPE and mode MODE. */
1179 void
1180 xstormy16_setup_incoming_varargs (cum, int_mode, type, pretend_size)
1181 CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED;
1182 int int_mode ATTRIBUTE_UNUSED;
1183 tree type ATTRIBUTE_UNUSED;
1184 int * pretend_size ATTRIBUTE_UNUSED;
1188 /* Build the va_list type.
1190 For this chip, va_list is a record containing a counter and a pointer.
1191 The counter is of type 'int' and indicates how many bytes
1192 have been used to date. The pointer indicates the stack position
1193 for arguments that have not been passed in registers.
1194 To keep the layout nice, the pointer is first in the structure. */
1196 tree
1197 xstormy16_build_va_list ()
1199 tree f_1, f_2, record, type_decl;
1201 record = (*lang_hooks.types.make_type) (RECORD_TYPE);
1202 type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
1204 f_1 = build_decl (FIELD_DECL, get_identifier ("base"),
1205 ptr_type_node);
1206 f_2 = build_decl (FIELD_DECL, get_identifier ("count"),
1207 unsigned_type_node);
1209 DECL_FIELD_CONTEXT (f_1) = record;
1210 DECL_FIELD_CONTEXT (f_2) = record;
1212 TREE_CHAIN (record) = type_decl;
1213 TYPE_NAME (record) = type_decl;
1214 TYPE_FIELDS (record) = f_1;
1215 TREE_CHAIN (f_1) = f_2;
1217 layout_type (record);
1219 return record;
1222 /* Implement the stdarg/varargs va_start macro. STDARG_P is nonzero if this
1223 is stdarg.h instead of varargs.h. VALIST is the tree of the va_list
1224 variable to initialize. NEXTARG is the machine independent notion of the
1225 'next' argument after the variable arguments. */
1226 void
1227 xstormy16_expand_builtin_va_start (valist, nextarg)
1228 tree valist;
1229 rtx nextarg ATTRIBUTE_UNUSED;
1231 tree f_base, f_count;
1232 tree base, count;
1233 tree t;
1235 if (xstormy16_interrupt_function_p ())
1236 error ("cannot use va_start in interrupt function");
1238 f_base = TYPE_FIELDS (va_list_type_node);
1239 f_count = TREE_CHAIN (f_base);
1241 base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1242 count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1244 t = make_tree (TREE_TYPE (base), virtual_incoming_args_rtx);
1245 t = build (PLUS_EXPR, TREE_TYPE (base), t,
1246 build_int_2 (INCOMING_FRAME_SP_OFFSET, 0));
1247 t = build (MODIFY_EXPR, TREE_TYPE (base), base, t);
1248 TREE_SIDE_EFFECTS (t) = 1;
1249 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1251 t = build (MODIFY_EXPR, TREE_TYPE (count), count,
1252 build_int_2 (current_function_args_info * UNITS_PER_WORD, 0));
1253 TREE_SIDE_EFFECTS (t) = 1;
1254 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1257 /* Implement the stdarg/varargs va_arg macro. VALIST is the variable
1258 of type va_list as a tree, TYPE is the type passed to va_arg.
1259 Note: This algorithm is documented in stormy-abi. */
1262 xstormy16_expand_builtin_va_arg (valist, type)
1263 tree valist;
1264 tree type;
1266 tree f_base, f_count;
1267 tree base, count;
1268 rtx count_rtx, addr_rtx, r;
1269 rtx lab_gotaddr, lab_fromstack;
1270 tree t;
1271 int size, size_of_reg_args;
1272 tree size_tree, count_plus_size;
1273 rtx count_plus_size_rtx;
1275 f_base = TYPE_FIELDS (va_list_type_node);
1276 f_count = TREE_CHAIN (f_base);
1278 base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1279 count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1281 size = PUSH_ROUNDING (int_size_in_bytes (type));
1282 size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
1284 size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
1286 count_rtx = expand_expr (count, NULL_RTX, HImode, EXPAND_NORMAL);
1287 lab_gotaddr = gen_label_rtx ();
1288 lab_fromstack = gen_label_rtx ();
1289 addr_rtx = gen_reg_rtx (Pmode);
1291 count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1292 count_plus_size_rtx = expand_expr (count_plus_size, NULL_RTX, HImode, EXPAND_NORMAL);
1293 emit_cmp_and_jump_insns (count_plus_size_rtx, GEN_INT (size_of_reg_args),
1294 GTU, const1_rtx, HImode, 1, lab_fromstack);
1296 t = build (PLUS_EXPR, ptr_type_node, base, count);
1297 r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1298 if (r != addr_rtx)
1299 emit_move_insn (addr_rtx, r);
1301 emit_jump_insn (gen_jump (lab_gotaddr));
1302 emit_barrier ();
1303 emit_label (lab_fromstack);
1305 /* Arguments larger than a word might need to skip over some
1306 registers, since arguments are either passed entirely in
1307 registers or entirely on the stack. */
1308 if (size > 2 || size < 0)
1310 rtx lab_notransition = gen_label_rtx ();
1311 emit_cmp_and_jump_insns (count_rtx, GEN_INT (NUM_ARGUMENT_REGISTERS
1312 * UNITS_PER_WORD),
1313 GEU, const1_rtx, HImode, 1, lab_notransition);
1315 t = build (MODIFY_EXPR, TREE_TYPE (count), count,
1316 build_int_2 (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD, 0));
1317 TREE_SIDE_EFFECTS (t) = 1;
1318 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1320 emit_label (lab_notransition);
1323 t = build (PLUS_EXPR, sizetype, size_tree,
1324 build_int_2 ((- NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD
1325 + INCOMING_FRAME_SP_OFFSET),
1326 -1));
1327 t = build (PLUS_EXPR, TREE_TYPE (count), count, fold (t));
1328 t = build (MINUS_EXPR, TREE_TYPE (base), base, t);
1329 r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1330 if (r != addr_rtx)
1331 emit_move_insn (addr_rtx, r);
1333 emit_label (lab_gotaddr);
1335 count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1336 t = build (MODIFY_EXPR, TREE_TYPE (count), count, count_plus_size);
1337 TREE_SIDE_EFFECTS (t) = 1;
1338 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1340 return addr_rtx;
1343 /* Initialize the variable parts of a trampoline. ADDR is an RTX for
1344 the address of the trampoline; FNADDR is an RTX for the address of
1345 the nested function; STATIC_CHAIN is an RTX for the static chain
1346 value that should be passed to the function when it is called. */
1347 void
1348 xstormy16_initialize_trampoline (addr, fnaddr, static_chain)
1349 rtx addr;
1350 rtx fnaddr;
1351 rtx static_chain;
1353 rtx reg_addr = gen_reg_rtx (Pmode);
1354 rtx temp = gen_reg_rtx (HImode);
1355 rtx reg_fnaddr = gen_reg_rtx (HImode);
1356 rtx reg_addr_mem;
1358 reg_addr_mem = gen_rtx_MEM (HImode, reg_addr);
1360 emit_move_insn (reg_addr, addr);
1361 emit_move_insn (temp, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM));
1362 emit_move_insn (reg_addr_mem, temp);
1363 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1364 emit_move_insn (temp, static_chain);
1365 emit_move_insn (reg_addr_mem, temp);
1366 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1367 emit_move_insn (reg_fnaddr, fnaddr);
1368 emit_move_insn (temp, reg_fnaddr);
1369 emit_insn (gen_andhi3 (temp, temp, GEN_INT (0xFF)));
1370 emit_insn (gen_iorhi3 (temp, temp, GEN_INT (0x0200)));
1371 emit_move_insn (reg_addr_mem, temp);
1372 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1373 emit_insn (gen_lshrhi3 (reg_fnaddr, reg_fnaddr, GEN_INT (8)));
1374 emit_move_insn (reg_addr_mem, reg_fnaddr);
1377 /* Create an RTX representing the place where a function returns a
1378 value of data type VALTYPE. VALTYPE is a tree node representing a
1379 data type. Write `TYPE_MODE (VALTYPE)' to get the machine mode
1380 used to represent that type. On many machines, only the mode is
1381 relevant. (Actually, on most machines, scalar values are returned
1382 in the same place regardless of mode).
1384 If `PROMOTE_FUNCTION_RETURN' is defined, you must apply the same promotion
1385 rules specified in `PROMOTE_MODE' if VALTYPE is a scalar type.
1387 If the precise function being called is known, FUNC is a tree node
1388 (`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer. This makes it
1389 possible to use a different value-returning convention for specific
1390 functions when all their calls are known.
1392 `FUNCTION_VALUE' is not used for return vales with aggregate data types,
1393 because these are returned in another way. See `STRUCT_VALUE_REGNUM' and
1394 related macros. */
1396 xstormy16_function_value (valtype, func)
1397 tree valtype;
1398 tree func ATTRIBUTE_UNUSED;
1400 enum machine_mode mode;
1401 mode = TYPE_MODE (valtype);
1402 PROMOTE_MODE (mode, 0, valtype);
1403 return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
1406 /* A C compound statement that outputs the assembler code for a thunk function,
1407 used to implement C++ virtual function calls with multiple inheritance. The
1408 thunk acts as a wrapper around a virtual function, adjusting the implicit
1409 object parameter before handing control off to the real function.
1411 First, emit code to add the integer DELTA to the location that contains the
1412 incoming first argument. Assume that this argument contains a pointer, and
1413 is the one used to pass the `this' pointer in C++. This is the incoming
1414 argument *before* the function prologue, e.g. `%o0' on a sparc. The
1415 addition must preserve the values of all other incoming arguments.
1417 After the addition, emit code to jump to FUNCTION, which is a
1418 `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch
1419 the return address. Hence returning from FUNCTION will return to whoever
1420 called the current `thunk'.
1422 The effect must be as if @var{function} had been called directly
1423 with the adjusted first argument. This macro is responsible for
1424 emitting all of the code for a thunk function;
1425 TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are
1426 not invoked.
1428 The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been
1429 extracted from it.) It might possibly be useful on some targets, but
1430 probably not. */
1432 static void
1433 xstormy16_asm_output_mi_thunk (file, thunk_fndecl, delta,
1434 vcall_offset, function)
1435 FILE *file;
1436 tree thunk_fndecl ATTRIBUTE_UNUSED;
1437 HOST_WIDE_INT delta;
1438 HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
1439 tree function;
1441 int regnum = FIRST_ARGUMENT_REGISTER;
1443 /* There might be a hidden first argument for a returned structure. */
1444 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
1445 regnum += 1;
1447 fprintf (file, "\tadd %s,#0x%x\n", reg_names[regnum], (int) delta & 0xFFFF);
1448 fputs ("\tjmpf ", file);
1449 assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
1450 putc ('\n', file);
1453 /* Mark functions with SYMBOL_REF_FLAG. */
1455 static void
1456 xstormy16_encode_section_info (decl, first)
1457 tree decl;
1458 int first ATTRIBUTE_UNUSED;
1460 if (TREE_CODE (decl) == FUNCTION_DECL)
1461 SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
1464 /* Output constructors and destructors. Just like
1465 default_named_section_asm_out_* but don't set the sections writable. */
1466 #undef TARGET_ASM_CONSTRUCTOR
1467 #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
1468 #undef TARGET_ASM_DESTRUCTOR
1469 #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1471 static void
1472 xstormy16_asm_out_destructor (symbol, priority)
1473 rtx symbol;
1474 int priority;
1476 const char *section = ".dtors";
1477 char buf[16];
1479 /* ??? This only works reliably with the GNU linker. */
1480 if (priority != DEFAULT_INIT_PRIORITY)
1482 sprintf (buf, ".dtors.%.5u",
1483 /* Invert the numbering so the linker puts us in the proper
1484 order; constructors are run from right to left, and the
1485 linker sorts in increasing order. */
1486 MAX_INIT_PRIORITY - priority);
1487 section = buf;
1490 named_section_flags (section, 0);
1491 assemble_align (POINTER_SIZE);
1492 assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1495 static void
1496 xstormy16_asm_out_constructor (symbol, priority)
1497 rtx symbol;
1498 int priority;
1500 const char *section = ".ctors";
1501 char buf[16];
1503 /* ??? This only works reliably with the GNU linker. */
1504 if (priority != DEFAULT_INIT_PRIORITY)
1506 sprintf (buf, ".ctors.%.5u",
1507 /* Invert the numbering so the linker puts us in the proper
1508 order; constructors are run from right to left, and the
1509 linker sorts in increasing order. */
1510 MAX_INIT_PRIORITY - priority);
1511 section = buf;
1514 named_section_flags (section, 0);
1515 assemble_align (POINTER_SIZE);
1516 assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1519 /* Print a memory address as an operand to reference that memory location. */
1520 void
1521 xstormy16_print_operand_address (file, address)
1522 FILE * file;
1523 rtx address;
1525 HOST_WIDE_INT offset;
1526 int pre_dec, post_inc;
1528 /* There are a few easy cases. */
1529 if (GET_CODE (address) == CONST_INT)
1531 fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (address) & 0xFFFF);
1532 return;
1535 if (CONSTANT_P (address) || GET_CODE (address) == CODE_LABEL)
1537 output_addr_const (file, address);
1538 return;
1541 /* Otherwise, it's hopefully something of the form
1542 (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...))
1545 if (GET_CODE (address) == PLUS)
1547 if (GET_CODE (XEXP (address, 1)) != CONST_INT)
1548 abort ();
1549 offset = INTVAL (XEXP (address, 1));
1550 address = XEXP (address, 0);
1552 else
1553 offset = 0;
1555 pre_dec = (GET_CODE (address) == PRE_DEC);
1556 post_inc = (GET_CODE (address) == POST_INC);
1557 if (pre_dec || post_inc)
1558 address = XEXP (address, 0);
1560 if (GET_CODE (address) != REG)
1561 abort ();
1563 fputc ('(', file);
1564 if (pre_dec)
1565 fputs ("--", file);
1566 fputs (reg_names [REGNO (address)], file);
1567 if (post_inc)
1568 fputs ("++", file);
1569 if (offset != 0)
1571 fputc (',', file);
1572 fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
1574 fputc (')', file);
1577 /* Print an operand to an assembler instruction. */
1578 void
1579 xstormy16_print_operand (file, x, code)
1580 FILE * file;
1581 rtx x;
1582 int code;
1584 switch (code)
1586 case 'B':
1587 /* There is either one bit set, or one bit clear, in X.
1588 Print it preceded by '#'. */
1590 HOST_WIDE_INT xx = 1;
1591 HOST_WIDE_INT l;
1593 if (GET_CODE (x) == CONST_INT)
1594 xx = INTVAL (x);
1595 else
1596 output_operand_lossage ("`B' operand is not constant");
1598 l = exact_log2 (xx);
1599 if (l == -1)
1600 l = exact_log2 (~xx);
1601 if (l == -1)
1602 output_operand_lossage ("`B' operand has multiple bits set");
1604 fputs (IMMEDIATE_PREFIX, file);
1605 fprintf (file, HOST_WIDE_INT_PRINT_DEC, l);
1606 return;
1609 case 'C':
1610 /* Print the symbol without a surrounding @fptr(). */
1611 if (GET_CODE (x) == SYMBOL_REF)
1612 assemble_name (file, XSTR (x, 0));
1613 else if (GET_CODE (x) == LABEL_REF)
1614 output_asm_label (x);
1615 else
1616 xstormy16_print_operand_address (file, x);
1617 return;
1619 case 'o':
1620 case 'O':
1621 /* Print the immediate operand less one, preceded by '#'.
1622 For 'O', negate it first. */
1624 HOST_WIDE_INT xx = 0;
1626 if (GET_CODE (x) == CONST_INT)
1627 xx = INTVAL (x);
1628 else
1629 output_operand_lossage ("`o' operand is not constant");
1631 if (code == 'O')
1632 xx = -xx;
1634 fputs (IMMEDIATE_PREFIX, file);
1635 fprintf (file, HOST_WIDE_INT_PRINT_DEC, xx - 1);
1636 return;
1639 case 0:
1640 /* Handled below. */
1641 break;
1643 default:
1644 output_operand_lossage ("xstormy16_print_operand: unknown code");
1645 return;
1648 switch (GET_CODE (x))
1650 case REG:
1651 fputs (reg_names [REGNO (x)], file);
1652 break;
1654 case MEM:
1655 xstormy16_print_operand_address (file, XEXP (x, 0));
1656 break;
1658 default:
1659 /* Some kind of constant or label; an immediate operand,
1660 so prefix it with '#' for the assembler. */
1661 fputs (IMMEDIATE_PREFIX, file);
1662 output_addr_const (file, x);
1663 break;
1666 return;
1670 /* Expander for the `casesi' pattern.
1671 INDEX is the index of the switch statement.
1672 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1673 to the first table entry.
1674 RANGE is the number of table entries.
1675 TABLE is an ADDR_VEC that is the jump table.
1676 DEFAULT_LABEL is the address to branch to if INDEX is outside the
1677 range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1680 void
1681 xstormy16_expand_casesi (index, lower_bound, range, table, default_label)
1682 rtx index;
1683 rtx lower_bound;
1684 rtx range;
1685 rtx table;
1686 rtx default_label;
1688 HOST_WIDE_INT range_i = INTVAL (range);
1689 rtx int_index;
1691 /* This code uses 'br', so it can deal only with tables of size up to
1692 8192 entries. */
1693 if (range_i >= 8192)
1694 sorry ("switch statement of size %lu entries too large",
1695 (unsigned long) range_i);
1697 index = expand_binop (SImode, sub_optab, index, lower_bound, NULL_RTX, 0,
1698 OPTAB_LIB_WIDEN);
1699 emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1,
1700 default_label);
1701 int_index = gen_lowpart_common (HImode, index);
1702 emit_insn (gen_ashlhi3 (int_index, int_index, GEN_INT (2)));
1703 emit_jump_insn (gen_tablejump_pcrel (int_index, table));
1706 /* Output an ADDR_VEC. It is output as a sequence of 'jmpf'
1707 instructions, without label or alignment or any other special
1708 constructs. We know that the previous instruction will be the
1709 `tablejump_pcrel' output above.
1711 TODO: it might be nice to output 'br' instructions if they could
1712 all reach. */
1714 void
1715 xstormy16_output_addr_vec (file, label, table)
1716 FILE *file;
1717 rtx label ATTRIBUTE_UNUSED;
1718 rtx table;
1720 int vlen, idx;
1722 function_section (current_function_decl);
1724 vlen = XVECLEN (table, 0);
1725 for (idx = 0; idx < vlen; idx++)
1727 fputs ("\tjmpf ", file);
1728 output_asm_label (XEXP (XVECEXP (table, 0, idx), 0));
1729 fputc ('\n', file);
1734 /* Expander for the `call' patterns.
1735 INDEX is the index of the switch statement.
1736 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1737 to the first table entry.
1738 RANGE is the number of table entries.
1739 TABLE is an ADDR_VEC that is the jump table.
1740 DEFAULT_LABEL is the address to branch to if INDEX is outside the
1741 range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1744 void
1745 xstormy16_expand_call (retval, dest, counter)
1746 rtx retval;
1747 rtx dest;
1748 rtx counter;
1750 rtx call, temp;
1751 enum machine_mode mode;
1753 if (GET_CODE (dest) != MEM)
1754 abort ();
1755 dest = XEXP (dest, 0);
1757 if (! CONSTANT_P (dest)
1758 && GET_CODE (dest) != REG)
1759 dest = force_reg (Pmode, dest);
1761 if (retval == NULL)
1762 mode = VOIDmode;
1763 else
1764 mode = GET_MODE (retval);
1766 call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest),
1767 counter);
1768 if (retval)
1769 call = gen_rtx_SET (VOIDmode, retval, call);
1771 if (! CONSTANT_P (dest))
1773 temp = gen_reg_rtx (HImode);
1774 emit_move_insn (temp, const0_rtx);
1776 else
1777 temp = const0_rtx;
1779 call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call,
1780 gen_rtx_USE (VOIDmode, temp)));
1781 emit_call_insn (call);
1784 /* Expanders for multiword computational operations. */
1786 /* Expander for arithmetic operations; emit insns to compute
1788 (set DEST (CODE:MODE SRC0 SRC1))
1790 using CARRY as a temporary. When CODE is COMPARE, a branch
1791 template is generated (this saves duplicating code in
1792 xstormy16_split_cbranch). */
1794 void
1795 xstormy16_expand_arith (mode, code, dest, src0, src1, carry)
1796 enum machine_mode mode;
1797 enum rtx_code code;
1798 rtx dest;
1799 rtx src0;
1800 rtx src1;
1801 rtx carry;
1803 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
1804 int i;
1805 int firstloop = 1;
1807 if (code == NEG)
1809 rtx zero_reg = gen_reg_rtx (word_mode);
1810 emit_move_insn (zero_reg, src0);
1811 src0 = zero_reg;
1814 for (i = 0; i < num_words; i++)
1816 rtx w_src0, w_src1, w_dest;
1817 rtx insn;
1819 if (code == NEG)
1820 w_src0 = src0;
1821 else
1822 w_src0 = simplify_gen_subreg (word_mode, src0, mode,
1823 i * UNITS_PER_WORD);
1824 w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD);
1825 w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD);
1827 switch (code)
1829 case PLUS:
1830 if (firstloop
1831 && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1832 continue;
1834 if (firstloop)
1835 insn = gen_addchi4 (w_dest, w_src0, w_src1, carry);
1836 else
1837 insn = gen_addchi5 (w_dest, w_src0, w_src1, carry, carry);
1838 break;
1840 case NEG:
1841 case MINUS:
1842 case COMPARE:
1843 if (code == COMPARE && i == num_words - 1)
1845 rtx branch, sub, clobber, sub_1;
1847 sub_1 = gen_rtx_MINUS (HImode, w_src0,
1848 gen_rtx_ZERO_EXTEND (HImode, carry));
1849 sub = gen_rtx_SET (VOIDmode, w_dest,
1850 gen_rtx_MINUS (HImode, sub_1, w_src1));
1851 clobber = gen_rtx_CLOBBER (VOIDmode, carry);
1852 branch = gen_rtx_SET (VOIDmode, pc_rtx,
1853 gen_rtx_IF_THEN_ELSE (VOIDmode,
1854 gen_rtx_EQ (HImode,
1855 sub_1,
1856 w_src1),
1857 pc_rtx,
1858 pc_rtx));
1859 insn = gen_rtx_PARALLEL (VOIDmode,
1860 gen_rtvec (3, branch, sub, clobber));
1862 else if (firstloop
1863 && code != COMPARE
1864 && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1865 continue;
1866 else if (firstloop)
1867 insn = gen_subchi4 (w_dest, w_src0, w_src1, carry);
1868 else
1869 insn = gen_subchi5 (w_dest, w_src0, w_src1, carry, carry);
1870 break;
1872 case IOR:
1873 case XOR:
1874 case AND:
1875 if (GET_CODE (w_src1) == CONST_INT
1876 && INTVAL (w_src1) == -(code == AND))
1877 continue;
1879 insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx (code, mode,
1880 w_src0, w_src1));
1881 break;
1883 case NOT:
1884 insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx_NOT (mode, w_src0));
1885 break;
1887 default:
1888 abort ();
1891 firstloop = 0;
1892 emit (insn);
1896 /* Return 1 if OP is a shift operator. */
1899 shift_operator (op, mode)
1900 register rtx op;
1901 enum machine_mode mode ATTRIBUTE_UNUSED;
1903 enum rtx_code code = GET_CODE (op);
1905 return (code == ASHIFT
1906 || code == ASHIFTRT
1907 || code == LSHIFTRT);
1910 /* The shift operations are split at output time for constant values;
1911 variable-width shifts get handed off to a library routine.
1913 Generate an output string to do (set X (CODE:MODE X SIZE_R))
1914 SIZE_R will be a CONST_INT, X will be a hard register. */
1916 const char *
1917 xstormy16_output_shift (mode, code, x, size_r, temp)
1918 enum machine_mode mode;
1919 enum rtx_code code;
1920 rtx x;
1921 rtx size_r;
1922 rtx temp;
1924 HOST_WIDE_INT size;
1925 const char *r0, *r1, *rt;
1926 static char r[64];
1928 if (GET_CODE (size_r) != CONST_INT
1929 || GET_CODE (x) != REG
1930 || mode != SImode)
1931 abort ();
1932 size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1);
1934 if (size == 0)
1935 return "";
1937 r0 = reg_names [REGNO (x)];
1938 r1 = reg_names [REGNO (x) + 1];
1940 /* For shifts of size 1, we can use the rotate instructions. */
1941 if (size == 1)
1943 switch (code)
1945 case ASHIFT:
1946 sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1);
1947 break;
1948 case ASHIFTRT:
1949 sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0);
1950 break;
1951 case LSHIFTRT:
1952 sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0);
1953 break;
1954 default:
1955 abort ();
1957 return r;
1960 /* For large shifts, there are easy special cases. */
1961 if (size == 16)
1963 switch (code)
1965 case ASHIFT:
1966 sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0);
1967 break;
1968 case ASHIFTRT:
1969 sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1);
1970 break;
1971 case LSHIFTRT:
1972 sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1);
1973 break;
1974 default:
1975 abort ();
1977 return r;
1979 if (size > 16)
1981 switch (code)
1983 case ASHIFT:
1984 sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d",
1985 r1, r0, r0, r1, (int) size - 16);
1986 break;
1987 case ASHIFTRT:
1988 sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d",
1989 r0, r1, r1, r0, (int) size - 16);
1990 break;
1991 case LSHIFTRT:
1992 sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d",
1993 r0, r1, r1, r0, (int) size - 16);
1994 break;
1995 default:
1996 abort ();
1998 return r;
2001 /* For the rest, we have to do more work. In particular, we
2002 need a temporary. */
2003 rt = reg_names [REGNO (temp)];
2004 switch (code)
2006 case ASHIFT:
2007 sprintf (r,
2008 "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s",
2009 rt, r0, r0, (int) size, r1, (int) size, rt, (int) 16-size,
2010 r1, rt);
2011 break;
2012 case ASHIFTRT:
2013 sprintf (r,
2014 "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
2015 rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
2016 r0, rt);
2017 break;
2018 case LSHIFTRT:
2019 sprintf (r,
2020 "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
2021 rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
2022 r0, rt);
2023 break;
2024 default:
2025 abort ();
2027 return r;
2030 /* Attribute handling. */
2032 /* Return nonzero if the function is an interrupt function. */
2034 xstormy16_interrupt_function_p ()
2036 tree attributes;
2038 /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
2039 any functions are declared, which is demonstrably wrong, but
2040 it is worked around here. FIXME. */
2041 if (!cfun)
2042 return 0;
2044 attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
2045 return lookup_attribute ("interrupt", attributes) != NULL_TREE;
2048 #undef TARGET_ATTRIBUTE_TABLE
2049 #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
2050 static tree xstormy16_handle_interrupt_attribute PARAMS ((tree *, tree, tree, int, bool *));
2051 static const struct attribute_spec xstormy16_attribute_table[] =
2053 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
2054 { "interrupt", 0, 0, false, true, true, xstormy16_handle_interrupt_attribute },
2055 { NULL, 0, 0, false, false, false, NULL }
2058 /* Handle an "interrupt" attribute;
2059 arguments as in struct attribute_spec.handler. */
2060 static tree
2061 xstormy16_handle_interrupt_attribute (node, name, args, flags, no_add_attrs)
2062 tree *node;
2063 tree name;
2064 tree args ATTRIBUTE_UNUSED;
2065 int flags ATTRIBUTE_UNUSED;
2066 bool *no_add_attrs;
2068 if (TREE_CODE (*node) != FUNCTION_TYPE)
2070 warning ("`%s' attribute only applies to functions",
2071 IDENTIFIER_POINTER (name));
2072 *no_add_attrs = true;
2075 return NULL_TREE;
2078 #undef TARGET_INIT_BUILTINS
2079 #define TARGET_INIT_BUILTINS xstormy16_init_builtins
2080 #undef TARGET_EXPAND_BUILTIN
2081 #define TARGET_EXPAND_BUILTIN xstormy16_expand_builtin
2083 static struct {
2084 const char *name;
2085 int md_code;
2086 const char *arg_ops; /* 0..9, t for temp register, r for return value */
2087 const char *arg_types; /* s=short,l=long, upper case for unsigned */
2088 } s16builtins[] = {
2089 { "__sdivlh", CODE_FOR_sdivlh, "rt01", "sls" },
2090 { "__smodlh", CODE_FOR_sdivlh, "tr01", "sls" },
2091 { "__udivlh", CODE_FOR_udivlh, "rt01", "SLS" },
2092 { "__umodlh", CODE_FOR_udivlh, "tr01", "SLS" },
2093 { 0, 0, 0, 0 }
2096 static void
2097 xstormy16_init_builtins ()
2099 tree args, ret_type, arg;
2100 int i, a;
2102 ret_type = void_type_node;
2104 for (i=0; s16builtins[i].name; i++)
2106 args = void_list_node;
2107 for (a=strlen (s16builtins[i].arg_types)-1; a>=0; a--)
2109 switch (s16builtins[i].arg_types[a])
2111 case 's': arg = short_integer_type_node; break;
2112 case 'S': arg = short_unsigned_type_node; break;
2113 case 'l': arg = long_integer_type_node; break;
2114 case 'L': arg = long_unsigned_type_node; break;
2115 default: abort();
2117 if (a == 0)
2118 ret_type = arg;
2119 else
2120 args = tree_cons (NULL_TREE, arg, args);
2122 builtin_function (s16builtins[i].name,
2123 build_function_type (ret_type, args),
2124 i, BUILT_IN_MD, NULL, NULL);
2128 static rtx
2129 xstormy16_expand_builtin(exp, target, subtarget, mode, ignore)
2130 tree exp;
2131 rtx target;
2132 rtx subtarget ATTRIBUTE_UNUSED;
2133 enum machine_mode mode ATTRIBUTE_UNUSED;
2134 int ignore ATTRIBUTE_UNUSED;
2136 rtx op[10], args[10], pat, copyto[10], retval = 0;
2137 tree fndecl, argtree;
2138 int i, a, o, code;
2140 fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
2141 argtree = TREE_OPERAND (exp, 1);
2142 i = DECL_FUNCTION_CODE (fndecl);
2143 code = s16builtins[i].md_code;
2145 for (a = 0; a < 10 && argtree; a++)
2147 args[a] = expand_expr (TREE_VALUE (argtree), NULL_RTX, VOIDmode, 0);
2148 argtree = TREE_CHAIN (argtree);
2151 for (o = 0; s16builtins[i].arg_ops[o]; o++)
2153 char ao = s16builtins[i].arg_ops[o];
2154 char c = insn_data[code].operand[o].constraint[0];
2155 int omode;
2157 copyto[o] = 0;
2159 omode = insn_data[code].operand[o].mode;
2160 if (ao == 'r')
2161 op[o] = target ? target : gen_reg_rtx (omode);
2162 else if (ao == 't')
2163 op[o] = gen_reg_rtx (omode);
2164 else
2165 op[o] = args[(int) hex_value (ao)];
2167 if (! (*insn_data[code].operand[o].predicate) (op[o], GET_MODE (op[o])))
2169 if (c == '+' || c == '=')
2171 copyto[o] = op[o];
2172 op[o] = gen_reg_rtx (omode);
2174 else
2175 op[o] = copy_to_mode_reg (omode, op[o]);
2178 if (ao == 'r')
2179 retval = op[o];
2182 pat = GEN_FCN (code) (op[0], op[1], op[2], op[3], op[4],
2183 op[5], op[6], op[7], op[8], op[9]);
2184 emit_insn (pat);
2186 for (o = 0; s16builtins[i].arg_ops[o]; o++)
2187 if (copyto[o])
2189 emit_move_insn (copyto[o], op[o]);
2190 if (op[o] == retval)
2191 retval = copyto[o];
2194 return retval;
2198 #undef TARGET_ASM_ALIGNED_HI_OP
2199 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
2200 #undef TARGET_ASM_ALIGNED_SI_OP
2201 #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
2202 #undef TARGET_ENCODE_SECTION_INFO
2203 #define TARGET_ENCODE_SECTION_INFO xstormy16_encode_section_info
2205 #undef TARGET_ASM_OUTPUT_MI_THUNK
2206 #define TARGET_ASM_OUTPUT_MI_THUNK xstormy16_asm_output_mi_thunk
2207 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
2208 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
2210 #undef TARGET_RTX_COSTS
2211 #define TARGET_RTX_COSTS xstormy16_rtx_costs
2213 struct gcc_target targetm = TARGET_INITIALIZER;