]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/pdp11/pdp11.c
Include function.h in most files.
[thirdparty/gcc.git] / gcc / config / pdp11 / pdp11.c
1 /* Subroutines for gcc2 for pdp11.
2 Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
3 Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at).
4
5 This file is part of GNU CC.
6
7 GNU CC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 1, or (at your option)
10 any later version.
11
12 GNU CC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU CC; see the file COPYING. If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 #include "config.h"
23 #include <stdio.h>
24 #include "rtl.h"
25 #include "regs.h"
26 #include "hard-reg-set.h"
27 #include "real.h"
28 #include "insn-config.h"
29 #include "conditions.h"
30 #include "insn-flags.h"
31 #include "function.h"
32 #include "output.h"
33 #include "insn-attr.h"
34 #include "flags.h"
35 #include "recog.h"
36
37 /*
38 #define FPU_REG_P(X) ((X)>=8 && (X)<14)
39 #define CPU_REG_P(X) ((X)>=0 && (X)<8)
40 */
41
42 /* this is the current value returned by the macro FIRST_PARM_OFFSET
43 defined in tm.h */
44 int current_first_parm_offset;
45
46 /* This is where the condition code register lives. */
47 /* rtx cc0_reg_rtx; - no longer needed? */
48
49 static rtx find_addr_reg ();
50
51 /* Nonzero if OP is a valid second operand for an arithmetic insn. */
52
53 int
54 arith_operand (op, mode)
55 rtx op;
56 enum machine_mode mode;
57 {
58 return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
59 }
60
61 int
62 const_immediate_operand (op, mode)
63 rtx op;
64 enum machine_mode mode;
65 {
66 return (GET_CODE (op) == CONST_INT);
67 }
68
69 int
70 immediate15_operand (op, mode)
71 rtx op;
72 enum machine_mode mode;
73 {
74 return (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0x8000) == 0x0000));
75 }
76
77 int
78 expand_shift_operand (op, mode)
79 rtx op;
80 enum machine_mode mode;
81 {
82 return (GET_CODE (op) == CONST_INT
83 && abs (INTVAL(op)) > 1
84 && abs (INTVAL(op)) <= 4);
85 }
86
87 /*
88 stream is a stdio stream to output the code to.
89 size is an int: how many units of temporary storage to allocate.
90 Refer to the array `regs_ever_live' to determine which registers
91 to save; `regs_ever_live[I]' is nonzero if register number I
92 is ever used in the function. This macro is responsible for
93 knowing which registers should not be saved even if used.
94 */
95
96 void
97 output_function_prologue(stream, size)
98 FILE *stream;
99 int size;
100 {
101 int fsize = ((size) + 1) & ~1;
102 int regno;
103
104 int via_ac = -1;
105
106 fprintf (stream, "\n\t; /* function prologue %s*/\n", current_function_name);
107
108 /* if we are outputting code for main,
109 the switch FPU to right mode if TARGET_FPU */
110 if ( (strcmp ("main", current_function_name) == 0)
111 && TARGET_FPU)
112 {
113 fprintf(stream, "\t;/* switch cpu to double float, single integer */\n");
114 fprintf(stream, "\tsetd\n");
115 fprintf(stream, "\tseti\n\n");
116 }
117
118 if (frame_pointer_needed)
119 {
120 fprintf(stream, "\tmov fp, -(sp)\n");
121 fprintf(stream, "\tmov sp, fp\n");
122 }
123 else
124 {
125 /* DON'T SAVE FP */
126 }
127
128 /* make frame */
129 if (fsize)
130 fprintf (stream, "\tsub $%o, sp\n", fsize);
131
132 /* save CPU registers */
133 for (regno = 0; regno < 8; regno++)
134 if (regs_ever_live[regno] && ! call_used_regs[regno])
135 if (! ((regno == FRAME_POINTER_REGNUM)
136 && frame_pointer_needed))
137 fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]);
138 /* fpu regs saving */
139
140 /* via_ac specifies the ac to use for saving ac4, ac5 */
141 via_ac = -1;
142
143 for (regno = 8; regno < FIRST_PSEUDO_REGISTER ; regno++)
144 {
145 /* ac0 - ac3 */
146 if (LOAD_FPU_REG_P(regno)
147 && regs_ever_live[regno]
148 && ! call_used_regs[regno])
149 {
150 fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[regno]);
151 via_ac = regno;
152 }
153
154 /* maybe make ac4, ac5 call used regs?? */
155 /* ac4 - ac5 */
156 if (NO_LOAD_FPU_REG_P(regno)
157 && regs_ever_live[regno]
158 && ! call_used_regs[regno])
159 {
160 if (via_ac == -1)
161 abort();
162
163 fprintf (stream, "\tfldd %s, %s\n", reg_names[regno], reg_names[via_ac]);
164 fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[via_ac]);
165 }
166 }
167
168 fprintf (stream, "\t;/* end of prologue */\n\n");
169 }
170
171 /*
172 The function epilogue should not depend on the current stack pointer!
173 It should use the frame pointer only. This is mandatory because
174 of alloca; we also take advantage of it to omit stack adjustments
175 before returning. */
176
177 /* maybe we can make leaf functions faster by switching to the
178 second register file - this way we don't have to save regs!
179 leaf functions are ~ 50% of all functions (dynamically!)
180
181 set/clear bit 11 (dec. 2048) of status word for switching register files -
182 but how can we do this? the pdp11/45 manual says bit may only
183 be set (p.24), but not cleared!
184
185 switching to kernel is probably more expensive, so we'll leave it
186 like this and not use the second set of registers...
187
188 maybe as option if you want to generate code for kernel mode? */
189
190
191 void
192 output_function_epilogue(stream, size)
193 FILE *stream;
194 int size;
195 {
196 int fsize = ((size) + 1) & ~1;
197 int i, j, k;
198
199 int via_ac;
200
201 fprintf (stream, "\n\t; /*function epilogue */\n");
202
203 if (frame_pointer_needed)
204 {
205 /* hope this is safe - m68k does it also .... */
206 regs_ever_live[FRAME_POINTER_REGNUM] = 0;
207
208 for (i =7, j = 0 ; i >= 0 ; i--)
209 if (regs_ever_live[i] && ! call_used_regs[i])
210 j++;
211
212 /* remember # of pushed bytes for CPU regs */
213 k = 2*j;
214
215 for (i =7 ; i >= 0 ; i--)
216 if (regs_ever_live[i] && ! call_used_regs[i])
217 fprintf(stream, "\tmov %o(fp), %s\n",-fsize-2*j--, reg_names[i]);
218
219 /* get ACs */
220 via_ac = FIRST_PSEUDO_REGISTER -1;
221
222 for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
223 if (regs_ever_live[i] && ! call_used_regs[i])
224 {
225 via_ac = i;
226 k += 8;
227 }
228
229 for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
230 {
231 if (LOAD_FPU_REG_P(i)
232 && regs_ever_live[i]
233 && ! call_used_regs[i])
234 {
235 fprintf(stream, "\tfldd %o(fp), %s\n", -fsize-k, reg_names[i]);
236 k -= 8;
237 }
238
239 if (NO_LOAD_FPU_REG_P(i)
240 && regs_ever_live[i]
241 && ! call_used_regs[i])
242 {
243 if (! LOAD_FPU_REG_P(via_ac))
244 abort();
245
246 fprintf(stream, "\tfldd %o(fp), %s\n", -fsize-k, reg_names[via_ac]);
247 fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]);
248 k -= 8;
249 }
250 }
251
252 fprintf(stream, "\tmov fp, sp\n");
253 fprintf (stream, "\tmov (sp)+, fp\n");
254 }
255 else
256 {
257 via_ac = FIRST_PSEUDO_REGISTER -1;
258
259 /* get ACs */
260 for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
261 if (regs_ever_live[i] && call_used_regs[i])
262 via_ac = i;
263
264 for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
265 {
266 if (LOAD_FPU_REG_P(i)
267 && regs_ever_live[i]
268 && ! call_used_regs[i])
269 fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[i]);
270
271 if (NO_LOAD_FPU_REG_P(i)
272 && regs_ever_live[i]
273 && ! call_used_regs[i])
274 {
275 if (! LOAD_FPU_REG_P(via_ac))
276 abort();
277
278 fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[via_ac]);
279 fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]);
280 }
281 }
282
283 for (i=7; i >= 0; i--)
284 if (regs_ever_live[i] && !call_used_regs[i])
285 fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]);
286
287 if (fsize)
288 fprintf((stream), "\tadd $%o, sp\n", fsize);
289 }
290
291 fprintf (stream, "\trts pc\n");
292 fprintf (stream, "\t;/* end of epilogue*/\n\n\n");
293 }
294
295 /* Return the best assembler insn template
296 for moving operands[1] into operands[0] as a fullword. */
297 static char *
298 singlemove_string (operands)
299 rtx *operands;
300 {
301 if (operands[1] != const0_rtx)
302 return "mov %1,%0";
303
304 return "clr %0";
305 }
306
307 \f
308 /* Output assembler code to perform a doubleword move insn
309 with operands OPERANDS. */
310
311 char *
312 output_move_double (operands)
313 rtx *operands;
314 {
315 enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
316 rtx latehalf[2];
317 rtx addreg0 = 0, addreg1 = 0;
318
319 /* First classify both operands. */
320
321 if (REG_P (operands[0]))
322 optype0 = REGOP;
323 else if (offsettable_memref_p (operands[0]))
324 optype0 = OFFSOP;
325 else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
326 optype0 = POPOP;
327 else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
328 optype0 = PUSHOP;
329 else if (GET_CODE (operands[0]) == MEM)
330 optype0 = MEMOP;
331 else
332 optype0 = RNDOP;
333
334 if (REG_P (operands[1]))
335 optype1 = REGOP;
336 else if (CONSTANT_P (operands[1]))
337 #if 0
338 || GET_CODE (operands[1]) == CONST_DOUBLE)
339 #endif
340 optype1 = CNSTOP;
341 else if (offsettable_memref_p (operands[1]))
342 optype1 = OFFSOP;
343 else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
344 optype1 = POPOP;
345 else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
346 optype1 = PUSHOP;
347 else if (GET_CODE (operands[1]) == MEM)
348 optype1 = MEMOP;
349 else
350 optype1 = RNDOP;
351
352 /* Check for the cases that the operand constraints are not
353 supposed to allow to happen. Abort if we get one,
354 because generating code for these cases is painful. */
355
356 if (optype0 == RNDOP || optype1 == RNDOP)
357 abort ();
358
359 /* If one operand is decrementing and one is incrementing
360 decrement the former register explicitly
361 and change that operand into ordinary indexing. */
362
363 if (optype0 == PUSHOP && optype1 == POPOP)
364 {
365 operands[0] = XEXP (XEXP (operands[0], 0), 0);
366 output_asm_insn ("sub $4,%0", operands);
367 operands[0] = gen_rtx (MEM, SImode, operands[0]);
368 optype0 = OFFSOP;
369 }
370 if (optype0 == POPOP && optype1 == PUSHOP)
371 {
372 operands[1] = XEXP (XEXP (operands[1], 0), 0);
373 output_asm_insn ("sub $4,%1", operands);
374 operands[1] = gen_rtx (MEM, SImode, operands[1]);
375 optype1 = OFFSOP;
376 }
377
378 /* If an operand is an unoffsettable memory ref, find a register
379 we can increment temporarily to make it refer to the second word. */
380
381 if (optype0 == MEMOP)
382 addreg0 = find_addr_reg (XEXP (operands[0], 0));
383
384 if (optype1 == MEMOP)
385 addreg1 = find_addr_reg (XEXP (operands[1], 0));
386
387 /* Ok, we can do one word at a time.
388 Normally we do the low-numbered word first,
389 but if either operand is autodecrementing then we
390 do the high-numbered word first.
391
392 In either case, set up in LATEHALF the operands to use
393 for the high-numbered word and in some cases alter the
394 operands in OPERANDS to be suitable for the low-numbered word. */
395
396 if (optype0 == REGOP)
397 latehalf[0] = gen_rtx (REG, HImode, REGNO (operands[0]) + 1);
398 else if (optype0 == OFFSOP)
399 latehalf[0] = adj_offsettable_operand (operands[0], 2);
400 else
401 latehalf[0] = operands[0];
402
403 if (optype1 == REGOP)
404 latehalf[1] = gen_rtx (REG, HImode, REGNO (operands[1]) + 1);
405 else if (optype1 == OFFSOP)
406 latehalf[1] = adj_offsettable_operand (operands[1], 2);
407 else if (optype1 == CNSTOP)
408 {
409 if (CONSTANT_P (operands[1]))
410 {
411 /* now the mess begins, high word is in lower word???
412
413 that's what ashc makes me think, but I don't remember :-( */
414 latehalf[1] = GEN_INT (INTVAL(operands[1])>>16);
415 operands[1] = GEN_INT (INTVAL(operands[1])&0xff);
416 }
417 else if (GET_CODE (operands[1]) == CONST_DOUBLE)
418 {
419 /* immediate 32 bit values not allowed */
420 abort();
421 }
422 }
423 else
424 latehalf[1] = operands[1];
425
426 /* If insn is effectively movd N(sp),-(sp) then we will do the
427 high word first. We should use the adjusted operand 1 (which is N+4(sp))
428 for the low word as well, to compensate for the first decrement of sp. */
429 if (optype0 == PUSHOP
430 && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
431 && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
432 operands[1] = latehalf[1];
433
434 /* If one or both operands autodecrementing,
435 do the two words, high-numbered first. */
436
437 /* Likewise, the first move would clobber the source of the second one,
438 do them in the other order. This happens only for registers;
439 such overlap can't happen in memory unless the user explicitly
440 sets it up, and that is an undefined circumstance. */
441
442 if (optype0 == PUSHOP || optype1 == PUSHOP
443 || (optype0 == REGOP && optype1 == REGOP
444 && REGNO (operands[0]) == REGNO (latehalf[1])))
445 {
446 /* Make any unoffsettable addresses point at high-numbered word. */
447 if (addreg0)
448 output_asm_insn ("add $2,%0", &addreg0);
449 if (addreg1)
450 output_asm_insn ("add $2,%0", &addreg1);
451
452 /* Do that word. */
453 output_asm_insn (singlemove_string (latehalf), latehalf);
454
455 /* Undo the adds we just did. */
456 if (addreg0)
457 output_asm_insn ("sub $2,%0", &addreg0);
458 if (addreg1)
459 output_asm_insn ("sub $2,%0", &addreg1);
460
461 /* Do low-numbered word. */
462 return singlemove_string (operands);
463 }
464
465 /* Normal case: do the two words, low-numbered first. */
466
467 output_asm_insn (singlemove_string (operands), operands);
468
469 /* Make any unoffsettable addresses point at high-numbered word. */
470 if (addreg0)
471 output_asm_insn ("add $2,%0", &addreg0);
472 if (addreg1)
473 output_asm_insn ("add $2,%0", &addreg1);
474
475 /* Do that word. */
476 output_asm_insn (singlemove_string (latehalf), latehalf);
477
478 /* Undo the adds we just did. */
479 if (addreg0)
480 output_asm_insn ("sub $2,%0", &addreg0);
481 if (addreg1)
482 output_asm_insn ("sub $2,%0", &addreg1);
483
484 return "";
485 }
486 /* Output assembler code to perform a quadword move insn
487 with operands OPERANDS. */
488
489 char *
490 output_move_quad (operands)
491 rtx *operands;
492 {
493 enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
494 rtx latehalf[2];
495 rtx addreg0 = 0, addreg1 = 0;
496
497 output_asm_insn(";; movdi/df: %1 -> %0", operands);
498
499 if (REG_P (operands[0]))
500 optype0 = REGOP;
501 else if (offsettable_memref_p (operands[0]))
502 optype0 = OFFSOP;
503 else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
504 optype0 = POPOP;
505 else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
506 optype0 = PUSHOP;
507 else if (GET_CODE (operands[0]) == MEM)
508 optype0 = MEMOP;
509 else
510 optype0 = RNDOP;
511
512 if (REG_P (operands[1]))
513 optype1 = REGOP;
514 else if (CONSTANT_P (operands[1])
515 || GET_CODE (operands[1]) == CONST_DOUBLE)
516 optype1 = CNSTOP;
517 else if (offsettable_memref_p (operands[1]))
518 optype1 = OFFSOP;
519 else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
520 optype1 = POPOP;
521 else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
522 optype1 = PUSHOP;
523 else if (GET_CODE (operands[1]) == MEM)
524 optype1 = MEMOP;
525 else
526 optype1 = RNDOP;
527
528 /* Check for the cases that the operand constraints are not
529 supposed to allow to happen. Abort if we get one,
530 because generating code for these cases is painful. */
531
532 if (optype0 == RNDOP || optype1 == RNDOP)
533 abort ();
534
535 /* check if we move a CPU reg to an FPU reg, or vice versa! */
536 if (optype0 == REGOP && optype1 == REGOP)
537 /* bogus - 64 bit cannot reside in CPU! */
538 if (CPU_REG_P(REGNO(operands[0]))
539 || CPU_REG_P (REGNO(operands[1])))
540 abort();
541
542 if (optype0 == REGOP || optype1 == REGOP)
543 {
544 /* check for use of clrd????
545 if you ever allow ac4 and ac5 (now we require secondary load)
546 you must check whether
547 you want to load into them or store from them -
548 then dump ac0 into $help$ movce ac4/5 to ac0, do the
549 store from ac0, and restore ac0 - if you can find
550 an unused ac[0-3], use that and you save a store and a load!*/
551
552 if (FPU_REG_P(REGNO(operands[0])))
553 {
554 if (GET_CODE(operands[1]) == CONST_DOUBLE)
555 {
556 union { double d; int i[2]; } u;
557 u.i[0] = CONST_DOUBLE_LOW (operands[1]);
558 u.i[1] = CONST_DOUBLE_HIGH (operands[1]);
559
560 if (u.d == 0.0)
561 return "{clrd|clrf} %0";
562 }
563
564 return "{ldd|movf} %1, %0";
565 }
566
567 if (FPU_REG_P(REGNO(operands[1])))
568 return "{std|movf} %1, %0";
569 }
570
571 /* If one operand is decrementing and one is incrementing
572 decrement the former register explicitly
573 and change that operand into ordinary indexing. */
574
575 if (optype0 == PUSHOP && optype1 == POPOP)
576 {
577 operands[0] = XEXP (XEXP (operands[0], 0), 0);
578 output_asm_insn ("sub $8,%0", operands);
579 operands[0] = gen_rtx (MEM, DImode, operands[0]);
580 optype0 = OFFSOP;
581 }
582 if (optype0 == POPOP && optype1 == PUSHOP)
583 {
584 operands[1] = XEXP (XEXP (operands[1], 0), 0);
585 output_asm_insn ("sub $8,%1", operands);
586 operands[1] = gen_rtx (MEM, SImode, operands[1]);
587 optype1 = OFFSOP;
588 }
589
590 /* If an operand is an unoffsettable memory ref, find a register
591 we can increment temporarily to make it refer to the second word. */
592
593 if (optype0 == MEMOP)
594 addreg0 = find_addr_reg (XEXP (operands[0], 0));
595
596 if (optype1 == MEMOP)
597 addreg1 = find_addr_reg (XEXP (operands[1], 0));
598
599 /* Ok, we can do one word at a time.
600 Normally we do the low-numbered word first,
601 but if either operand is autodecrementing then we
602 do the high-numbered word first.
603
604 In either case, set up in LATEHALF the operands to use
605 for the high-numbered word and in some cases alter the
606 operands in OPERANDS to be suitable for the low-numbered word. */
607
608 if (optype0 == REGOP)
609 latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
610 else if (optype0 == OFFSOP)
611 latehalf[0] = adj_offsettable_operand (operands[0], 4);
612 else
613 latehalf[0] = operands[0];
614
615 if (optype1 == REGOP)
616 latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
617 else if (optype1 == OFFSOP)
618 latehalf[1] = adj_offsettable_operand (operands[1], 4);
619 else if (optype1 == CNSTOP)
620 {
621 if (GET_CODE (operands[1]) == CONST_DOUBLE)
622 {
623 /* floats only. not yet supported!
624
625 -- compute it into PDP float format, - internally,
626 just use IEEE and ignore possible problems ;-)
627
628 we might get away with it !!!! */
629
630 abort();
631
632 #ifndef HOST_WORDS_BIG_ENDIAN
633 latehalf[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
634 operands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
635 #else /* HOST_WORDS_BIG_ENDIAN */
636 latehalf[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
637 operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
638 #endif /* HOST_WORDS_BIG_ENDIAN */
639 }
640 else if (GET_CODE(operands[1]) == CONST_INT)
641 {
642 latehalf[1] = GEN_INT (0);
643 }
644 else
645 abort();
646
647 }
648 else
649 latehalf[1] = operands[1];
650
651 /* If insn is effectively movd N(sp),-(sp) then we will do the
652 high word first. We should use the adjusted operand 1 (which is N+4(sp))
653 for the low word as well, to compensate for the first decrement of sp. */
654 if (optype0 == PUSHOP
655 && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
656 && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
657 operands[1] = latehalf[1];
658
659 /* If one or both operands autodecrementing,
660 do the two words, high-numbered first. */
661
662 /* Likewise, the first move would clobber the source of the second one,
663 do them in the other order. This happens only for registers;
664 such overlap can't happen in memory unless the user explicitly
665 sets it up, and that is an undefined circumstance. */
666
667 if (optype0 == PUSHOP || optype1 == PUSHOP
668 || (optype0 == REGOP && optype1 == REGOP
669 && REGNO (operands[0]) == REGNO (latehalf[1])))
670 {
671 /* Make any unoffsettable addresses point at high-numbered word. */
672 if (addreg0)
673 output_asm_insn ("add $4,%0", &addreg0);
674 if (addreg1)
675 output_asm_insn ("add $4,%0", &addreg1);
676
677 /* Do that word. */
678 output_asm_insn(output_move_double(latehalf), latehalf);
679
680 /* Undo the adds we just did. */
681 if (addreg0)
682 output_asm_insn ("sub $4,%0", &addreg0);
683 if (addreg1)
684 output_asm_insn ("sub $4,%0", &addreg1);
685
686 /* Do low-numbered word. */
687 return output_move_double (operands);
688 }
689
690 /* Normal case: do the two words, low-numbered first. */
691
692 output_asm_insn (output_move_double (operands), operands);
693
694 /* Make any unoffsettable addresses point at high-numbered word. */
695 if (addreg0)
696 output_asm_insn ("add $4,%0", &addreg0);
697 if (addreg1)
698 output_asm_insn ("add $4,%0", &addreg1);
699
700 /* Do that word. */
701 output_asm_insn (output_move_double (latehalf), latehalf);
702
703 /* Undo the adds we just did. */
704 if (addreg0)
705 output_asm_insn ("sub $4,%0", &addreg0);
706 if (addreg1)
707 output_asm_insn ("sub $4,%0", &addreg1);
708
709 return "";
710 }
711
712 \f
713 /* Return a REG that occurs in ADDR with coefficient 1.
714 ADDR can be effectively incremented by incrementing REG. */
715
716 static rtx
717 find_addr_reg (addr)
718 rtx addr;
719 {
720 while (GET_CODE (addr) == PLUS)
721 {
722 if (GET_CODE (XEXP (addr, 0)) == REG)
723 addr = XEXP (addr, 0);
724 if (GET_CODE (XEXP (addr, 1)) == REG)
725 addr = XEXP (addr, 1);
726 if (CONSTANT_P (XEXP (addr, 0)))
727 addr = XEXP (addr, 1);
728 if (CONSTANT_P (XEXP (addr, 1)))
729 addr = XEXP (addr, 0);
730 }
731 if (GET_CODE (addr) == REG)
732 return addr;
733 return 0;
734 }
735 \f
736 /* Output an ascii string. */
737 void
738 output_ascii (file, p, size)
739 FILE *file;
740 char *p;
741 int size;
742 {
743 int i;
744
745 /* This used to output .byte "string", which doesn't work with the UNIX
746 assembler and I think not with DEC ones either. */
747 fprintf (file, "\t.byte ");
748
749 for (i = 0; i < size; i++)
750 {
751 register int c = p[i];
752 if (c < 0)
753 c += 256;
754 fprintf (file, "%o", c);
755 if (i < size - 1)
756 putc (',', file);
757 }
758 putc ('\n', file);
759 }
760
761
762 /* --- stole from out-vax, needs changes */
763
764 void
765 print_operand_address (file, addr)
766 FILE *file;
767 register rtx addr;
768 {
769 register rtx reg1, reg2, breg, ireg;
770 rtx offset;
771
772 retry:
773
774 switch (GET_CODE (addr))
775 {
776 case MEM:
777 if (TARGET_UNIX_ASM)
778 fprintf (file, "*");
779 else
780 fprintf (file, "@");
781 addr = XEXP (addr, 0);
782 goto retry;
783
784 case REG:
785 fprintf (file, "(%s)", reg_names[REGNO (addr)]);
786 break;
787
788 case PRE_DEC:
789 fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]);
790 break;
791
792 case POST_INC:
793 fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]);
794 break;
795
796 case PLUS:
797 reg1 = 0; reg2 = 0;
798 ireg = 0; breg = 0;
799 offset = 0;
800 if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
801 || GET_CODE (XEXP (addr, 0)) == MEM)
802 {
803 offset = XEXP (addr, 0);
804 addr = XEXP (addr, 1);
805 }
806 else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
807 || GET_CODE (XEXP (addr, 1)) == MEM)
808 {
809 offset = XEXP (addr, 1);
810 addr = XEXP (addr, 0);
811 }
812 if (GET_CODE (addr) != PLUS)
813 ;
814 else if (GET_CODE (XEXP (addr, 0)) == MULT)
815 {
816 reg1 = XEXP (addr, 0);
817 addr = XEXP (addr, 1);
818 }
819 else if (GET_CODE (XEXP (addr, 1)) == MULT)
820 {
821 reg1 = XEXP (addr, 1);
822 addr = XEXP (addr, 0);
823 }
824 else if (GET_CODE (XEXP (addr, 0)) == REG)
825 {
826 reg1 = XEXP (addr, 0);
827 addr = XEXP (addr, 1);
828 }
829 else if (GET_CODE (XEXP (addr, 1)) == REG)
830 {
831 reg1 = XEXP (addr, 1);
832 addr = XEXP (addr, 0);
833 }
834 if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
835 {
836 if (reg1 == 0)
837 reg1 = addr;
838 else
839 reg2 = addr;
840 addr = 0;
841 }
842 if (offset != 0)
843 {
844 if (addr != 0) abort ();
845 addr = offset;
846 }
847 if (reg1 != 0 && GET_CODE (reg1) == MULT)
848 {
849 breg = reg2;
850 ireg = reg1;
851 }
852 else if (reg2 != 0 && GET_CODE (reg2) == MULT)
853 {
854 breg = reg1;
855 ireg = reg2;
856 }
857 else if (reg2 != 0 || GET_CODE (addr) == MEM)
858 {
859 breg = reg2;
860 ireg = reg1;
861 }
862 else
863 {
864 breg = reg1;
865 ireg = reg2;
866 }
867 if (addr != 0)
868 output_address (addr);
869 if (breg != 0)
870 {
871 if (GET_CODE (breg) != REG)
872 abort ();
873 fprintf (file, "(%s)", reg_names[REGNO (breg)]);
874 }
875 if (ireg != 0)
876 {
877 if (GET_CODE (ireg) == MULT)
878 ireg = XEXP (ireg, 0);
879 if (GET_CODE (ireg) != REG)
880 abort ();
881 abort();
882 fprintf (file, "[%s]", reg_names[REGNO (ireg)]);
883 }
884 break;
885
886 default:
887 output_addr_const_pdp11 (file, addr);
888 }
889 }
890
891 /* register move costs, indexed by regs */
892
893 static int move_costs[N_REG_CLASSES][N_REG_CLASSES] =
894 {
895 /* NO MUL GEN LFPU NLFPU FPU ALL */
896
897 /* NO */ { 0, 0, 0, 0, 0, 0, 0},
898 /* MUL */ { 0, 2, 2, 10, 22, 22, 22},
899 /* GEN */ { 0, 2, 2, 10, 22, 22, 22},
900 /* LFPU */ { 0, 10, 10, 2, 2, 2, 10},
901 /* NLFPU */ { 0, 22, 22, 2, 2, 2, 22},
902 /* FPU */ { 0, 22, 22, 2, 2, 2, 22},
903 /* ALL */ { 0, 22, 22, 10, 22, 22, 22}
904 } ;
905
906
907 /* -- note that some moves are tremendously expensive,
908 because they require lots of tricks! do we have to
909 charge the costs incurred by secondary reload class
910 -- as we do here with 22 -- or not ? */
911
912 int
913 register_move_cost(c1, c2)
914 enum reg_class c1, c2;
915 {
916 return move_costs[(int)c1][(int)c2];
917 }
918
919 char *
920 output_jump(pos, neg, length)
921 int length;
922 char *pos, *neg;
923 {
924 static int x = 0;
925
926 static char buf[1000];
927
928 #if 0
929 /* currently we don't need this, because the tstdf and cmpdf
930 copy the condition code immediately, and other float operations are not
931 yet recognized as changing the FCC - if so, then the length-cost of all
932 jump insns increases by one, because we have to potentially copy the
933 FCC! */
934 if (cc_status.flags & CC_IN_FPU)
935 output_asm_insn("cfcc", NULL);
936 #endif
937
938 switch (length)
939 {
940 case 1:
941
942 strcpy(buf, pos);
943 strcat(buf, " %l0");
944
945 return buf;
946
947 case 3:
948
949 sprintf(buf, "%s JMP_%d\n\tjmp %%l0\nJMP_%d:", neg, x, x);
950
951 x++;
952
953 return buf;
954
955 default:
956
957 abort();
958 }
959
960 }
961
962 void
963 notice_update_cc_on_set(exp, insn)
964 rtx exp;
965 rtx insn;
966 {
967 if (GET_CODE (SET_DEST (exp)) == CC0)
968 {
969 cc_status.flags = 0;
970 cc_status.value1 = SET_DEST (exp);
971 cc_status.value2 = SET_SRC (exp);
972
973 /*
974 if (GET_MODE(SET_SRC(exp)) == DFmode)
975 cc_status.flags |= CC_IN_FPU;
976 */
977 }
978 else if ((GET_CODE (SET_DEST (exp)) == REG
979 || GET_CODE (SET_DEST (exp)) == MEM)
980 && GET_CODE (SET_SRC (exp)) != PC
981 && (GET_MODE (SET_DEST(exp)) == HImode
982 || GET_MODE (SET_DEST(exp)) == QImode)
983 && (GET_CODE (SET_SRC(exp)) == PLUS
984 || GET_CODE (SET_SRC(exp)) == MINUS
985 || GET_CODE (SET_SRC(exp)) == AND
986 || GET_CODE (SET_SRC(exp)) == IOR
987 || GET_CODE (SET_SRC(exp)) == XOR
988 || GET_CODE (SET_SRC(exp)) == NOT
989 || GET_CODE (SET_SRC(exp)) == NEG
990 || GET_CODE (SET_SRC(exp)) == REG
991 || GET_CODE (SET_SRC(exp)) == MEM))
992 {
993 cc_status.flags = 0;
994 cc_status.value1 = SET_SRC (exp);
995 cc_status.value2 = SET_DEST (exp);
996
997 if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
998 && cc_status.value2
999 && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
1000 cc_status.value2 = 0;
1001 if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM
1002 && cc_status.value2
1003 && GET_CODE (cc_status.value2) == MEM)
1004 cc_status.value2 = 0;
1005 }
1006 else if (GET_CODE (SET_SRC (exp)) == CALL)
1007 {
1008 CC_STATUS_INIT;
1009 }
1010 else if (GET_CODE (SET_DEST (exp)) == REG)
1011 /* what's this ? */
1012 {
1013 if ((cc_status.value1
1014 && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)))
1015 cc_status.value1 = 0;
1016 if ((cc_status.value2
1017 && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)))
1018 cc_status.value2 = 0;
1019 }
1020 else if (SET_DEST(exp) == pc_rtx)
1021 {
1022 /* jump */
1023 }
1024 else /* if (GET_CODE (SET_DEST (exp)) == MEM) */
1025 {
1026 /* the last else is a bit paranoiac, but since nearly all instructions
1027 play with condition codes, it's reasonable! */
1028
1029 CC_STATUS_INIT; /* paranoia*/
1030 }
1031 }
1032
1033
1034 int simple_memory_operand(op, mode)
1035 rtx op;
1036 enum machine_mode mode;
1037 {
1038 rtx addr;
1039
1040 /* Eliminate non-memory operations */
1041 if (GET_CODE (op) != MEM)
1042 return FALSE;
1043
1044 #if 0
1045 /* dword operations really put out 2 instructions, so eliminate them. */
1046 if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
1047 return FALSE;
1048 #endif
1049
1050 /* Decode the address now. */
1051
1052 indirection:
1053
1054 addr = XEXP (op, 0);
1055
1056 switch (GET_CODE (addr))
1057 {
1058 case REG:
1059 /* (R0) - no extra cost */
1060 return 1;
1061
1062 case PRE_DEC:
1063 case POST_INC:
1064 /* -(R0), (R0)+ - cheap! */
1065 return 0;
1066
1067 case MEM:
1068 /* cheap - is encoded in addressing mode info!
1069
1070 -- except for @(R0), which has to be @0(R0) !!! */
1071
1072 if (GET_CODE (XEXP (addr, 0)) == REG)
1073 return 0;
1074
1075 op=addr;
1076 goto indirection;
1077
1078 case CONST_INT:
1079 case LABEL_REF:
1080 case CONST:
1081 case SYMBOL_REF:
1082 /* @#address - extra cost */
1083 return 0;
1084
1085 case PLUS:
1086 /* X(R0) - extra cost */
1087 return 0;
1088
1089 default:
1090 break;
1091 }
1092
1093 return FALSE;
1094 }
1095
1096
1097 /*
1098 * output a block move:
1099 *
1100 * operands[0] ... to
1101 * operands[1] ... from
1102 * operands[2] ... length
1103 * operands[3] ... alignment
1104 * operands[4] ... scratch register
1105 */
1106
1107
1108 char *
1109 output_block_move(operands)
1110 rtx *operands;
1111 {
1112 static int count = 0;
1113 char buf[200];
1114
1115 if (GET_CODE(operands[2]) == CONST_INT
1116 && ! optimize_size)
1117 {
1118 if (INTVAL(operands[2]) < 16
1119 && INTVAL(operands[3]) == 1)
1120 {
1121 register int i;
1122
1123 for (i = 1; i <= INTVAL(operands[2]); i++)
1124 output_asm_insn("movb (%1)+, (%0)+", operands);
1125
1126 return "";
1127 }
1128 else if (INTVAL(operands[2]) < 32)
1129 {
1130 register int i;
1131
1132 for (i = 1; i <= INTVAL(operands[2])/2; i++)
1133 output_asm_insn("mov (%1)+, (%0)+", operands);
1134
1135 /* may I assume that moved quantity is
1136 multiple of alignment ???
1137
1138 I HOPE SO !
1139 */
1140
1141 return "";
1142 }
1143
1144
1145 /* can do other clever things, maybe... */
1146 }
1147
1148 if (CONSTANT_P(operands[2]) )
1149 {
1150 /* just move count to scratch */
1151 output_asm_insn("mov %2, %4", operands);
1152 }
1153 else
1154 {
1155 /* just clobber the register */
1156 operands[4] = operands[2];
1157 }
1158
1159
1160 /* switch over alignment */
1161 switch (INTVAL(operands[3]))
1162 {
1163 case 1:
1164
1165 /*
1166 x:
1167 movb (%1)+, (%0)+
1168
1169 if (TARGET_45)
1170 sob %4,x
1171 else
1172 dec %4
1173 bgt x
1174
1175 */
1176
1177 sprintf(buf, "\nmovestrhi%d:", count);
1178 output_asm_insn(buf, NULL);
1179
1180 output_asm_insn("movb (%1)+, (%0)+", operands);
1181
1182 if (TARGET_45)
1183 {
1184 sprintf(buf, "sob %%4, movestrhi%d", count);
1185 output_asm_insn(buf, operands);
1186 }
1187 else
1188 {
1189 output_asm_insn("dec %4", operands);
1190
1191 sprintf(buf, "bgt movestrhi%d", count);
1192 output_asm_insn(buf, NULL);
1193 }
1194
1195 count ++;
1196 break;
1197
1198 case 2:
1199
1200 /*
1201 asr %4
1202
1203 x:
1204
1205 mov (%1)+, (%0)+
1206
1207 if (TARGET_45)
1208 sob %4, x
1209 else
1210 dec %4
1211 bgt x
1212 */
1213
1214 generate_compact_code:
1215
1216 output_asm_insn("asr %4", operands);
1217
1218 sprintf(buf, "\nmovestrhi%d:", count);
1219 output_asm_insn(buf, NULL);
1220
1221 output_asm_insn("mov (%1)+, (%0)+", operands);
1222
1223 if (TARGET_45)
1224 {
1225 sprintf(buf, "sob %%4, movestrhi%d", count);
1226 output_asm_insn(buf, operands);
1227 }
1228 else
1229 {
1230 output_asm_insn("dec %4", operands);
1231
1232 sprintf(buf, "bgt movestrhi%d", count);
1233 output_asm_insn(buf, NULL);
1234 }
1235
1236 count ++;
1237 break;
1238
1239 case 4:
1240
1241 /*
1242
1243 asr %4
1244 asr %4
1245
1246 x:
1247
1248 mov (%1)+, (%0)+
1249 mov (%1)+, (%0)+
1250
1251 if (TARGET_45)
1252 sob %4, x
1253 else
1254 dec %4
1255 bgt x
1256 */
1257
1258 if (optimize_size)
1259 goto generate_compact_code;
1260
1261 output_asm_insn("asr %4", operands);
1262 output_asm_insn("asr %4", operands);
1263
1264 sprintf(buf, "\nmovestrhi%d:", count);
1265 output_asm_insn(buf, NULL);
1266
1267 output_asm_insn("mov (%1)+, (%0)+", operands);
1268 output_asm_insn("mov (%1)+, (%0)+", operands);
1269
1270 if (TARGET_45)
1271 {
1272 sprintf(buf, "sob %%4, movestrhi%d", count);
1273 output_asm_insn(buf, operands);
1274 }
1275 else
1276 {
1277 output_asm_insn("dec %4", operands);
1278
1279 sprintf(buf, "bgt movestrhi%d", count);
1280 output_asm_insn(buf, NULL);
1281 }
1282
1283 count ++;
1284 break;
1285
1286 default:
1287
1288 /*
1289
1290 asr %4
1291 asr %4
1292 asr %4
1293
1294 x:
1295
1296 mov (%1)+, (%0)+
1297 mov (%1)+, (%0)+
1298 mov (%1)+, (%0)+
1299 mov (%1)+, (%0)+
1300
1301 if (TARGET_45)
1302 sob %4, x
1303 else
1304 dec %4
1305 bgt x
1306 */
1307
1308
1309 if (optimize_size)
1310 goto generate_compact_code;
1311
1312 output_asm_insn("asr %4", operands);
1313 output_asm_insn("asr %4", operands);
1314 output_asm_insn("asr %4", operands);
1315
1316 sprintf(buf, "\nmovestrhi%d:", count);
1317 output_asm_insn(buf, NULL);
1318
1319 output_asm_insn("mov (%1)+, (%0)+", operands);
1320 output_asm_insn("mov (%1)+, (%0)+", operands);
1321 output_asm_insn("mov (%1)+, (%0)+", operands);
1322 output_asm_insn("mov (%1)+, (%0)+", operands);
1323
1324 if (TARGET_45)
1325 {
1326 sprintf(buf, "sob %%4, movestrhi%d", count);
1327 output_asm_insn(buf, operands);
1328 }
1329 else
1330 {
1331 output_asm_insn("dec %4", operands);
1332
1333 sprintf(buf, "bgt movestrhi%d", count);
1334 output_asm_insn(buf, NULL);
1335 }
1336
1337 count ++;
1338 break;
1339
1340 ;
1341
1342 }
1343
1344 return "";
1345 }
1346
1347 /* for future use */
1348 int
1349 comparison_operator_index(op)
1350 rtx op;
1351 {
1352 switch (GET_CODE(op))
1353 {
1354 case NE:
1355 return 0;
1356
1357 case EQ:
1358 return 1;
1359
1360 case GE:
1361 return 2;
1362
1363 case GT:
1364 return 3;
1365
1366 case LE:
1367 return 4;
1368
1369 case LT:
1370 return 5;
1371
1372 case GEU:
1373 return 6;
1374
1375 case GTU:
1376 return 7;
1377
1378 case LEU:
1379 return 8;
1380
1381 case LTU:
1382 return 9;
1383
1384 default:
1385 return -1;
1386 }
1387 }
1388
1389 /* tests whether the rtx is a comparison operator */
1390 int
1391 comp_operator (op, mode)
1392 rtx op;
1393 enum machine_mode mode;
1394 {
1395 return comparison_operator_index(op) >= 0;
1396 }
1397
1398
1399 int
1400 legitimate_address_p (mode, address)
1401 enum machine_mode mode;
1402 rtx address;
1403 {
1404 /* #define REG_OK_STRICT */
1405 GO_IF_LEGITIMATE_ADDRESS(mode, address, win);
1406
1407 return 0;
1408
1409 win:
1410 return 1;
1411
1412 /* #undef REG_OK_STRICT */
1413 }
1414
1415 /* A copy of output_addr_const modified for pdp11 expression syntax.
1416 output_addr_const also gets called for %cDIGIT and %nDIGIT, which we don't
1417 use, and for debugging output, which we don't support with this port either.
1418 So this copy should get called whenever needed.
1419 */
1420 void
1421 output_addr_const_pdp11 (file, x)
1422 FILE *file;
1423 rtx x;
1424 {
1425 char buf[256];
1426
1427 restart:
1428 switch (GET_CODE (x))
1429 {
1430 case PC:
1431 if (flag_pic)
1432 putc ('.', file);
1433 else
1434 abort ();
1435 break;
1436
1437 case SYMBOL_REF:
1438 assemble_name (file, XSTR (x, 0));
1439 break;
1440
1441 case LABEL_REF:
1442 ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0)));
1443 assemble_name (file, buf);
1444 break;
1445
1446 case CODE_LABEL:
1447 ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
1448 assemble_name (file, buf);
1449 break;
1450
1451 case CONST_INT:
1452 /* Should we check for constants which are too big? Maybe cutting
1453 them off to 16 bits is OK? */
1454 fprintf (file, "%ho", (unsigned short) INTVAL (x));
1455 break;
1456
1457 case CONST:
1458 /* This used to output parentheses around the expression,
1459 but that does not work on the 386 (either ATT or BSD assembler). */
1460 output_addr_const_pdp11 (file, XEXP (x, 0));
1461 break;
1462
1463 case CONST_DOUBLE:
1464 if (GET_MODE (x) == VOIDmode)
1465 {
1466 /* We can use %o if the number is one word and positive. */
1467 if (CONST_DOUBLE_HIGH (x))
1468 abort (); /* Should we just silently drop the high part? */
1469 else
1470 fprintf (file, "%ho", (unsigned short) CONST_DOUBLE_LOW (x));
1471 }
1472 else
1473 /* We can't handle floating point constants;
1474 PRINT_OPERAND must handle them. */
1475 output_operand_lossage ("floating constant misused");
1476 break;
1477
1478 case PLUS:
1479 /* Some assemblers need integer constants to appear last (eg masm). */
1480 if (GET_CODE (XEXP (x, 0)) == CONST_INT)
1481 {
1482 output_addr_const_pdp11 (file, XEXP (x, 1));
1483 if (INTVAL (XEXP (x, 0)) >= 0)
1484 fprintf (file, "+");
1485 output_addr_const_pdp11 (file, XEXP (x, 0));
1486 }
1487 else
1488 {
1489 output_addr_const_pdp11 (file, XEXP (x, 0));
1490 if (INTVAL (XEXP (x, 1)) >= 0)
1491 fprintf (file, "+");
1492 output_addr_const_pdp11 (file, XEXP (x, 1));
1493 }
1494 break;
1495
1496 case MINUS:
1497 /* Avoid outputting things like x-x or x+5-x,
1498 since some assemblers can't handle that. */
1499 x = simplify_subtraction (x);
1500 if (GET_CODE (x) != MINUS)
1501 goto restart;
1502
1503 output_addr_const_pdp11 (file, XEXP (x, 0));
1504 fprintf (file, "-");
1505 if (GET_CODE (XEXP (x, 1)) == CONST_INT
1506 && INTVAL (XEXP (x, 1)) < 0)
1507 {
1508 fprintf (file, ASM_OPEN_PAREN);
1509 output_addr_const_pdp11 (file, XEXP (x, 1));
1510 fprintf (file, ASM_CLOSE_PAREN);
1511 }
1512 else
1513 output_addr_const_pdp11 (file, XEXP (x, 1));
1514 break;
1515
1516 case ZERO_EXTEND:
1517 case SIGN_EXTEND:
1518 output_addr_const_pdp11 (file, XEXP (x, 0));
1519 break;
1520
1521 default:
1522 output_operand_lossage ("invalid expression as operand");
1523 }
1524 }