1/* Subroutines for insn-output.c for Tahoe.
2   Copyright (C) 1989, 1991, 1997 Free Software Foundation, Inc.
3
4This file is part of GNU CC.
5
6GNU CC is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU CC is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU CC; see the file COPYING.  If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.  */
20
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 "output.h"
32#include "insn-attr.h"
33
34/*
35 * File: output-tahoe.c
36 *
37 * Original port made at the University of Buffalo by Devon Bowen,
38 * Dale Wiles and Kevin Zachmann.
39 *
40 * Changes for HCX by Piet van Oostrum,
41 * University of Utrecht, The Netherlands (piet@cs.ruu.nl)
42 *
43 * Speed tweaks by Michael Tiemann (tiemann@lurch.stanford.edu).
44 *
45 * Mail bugs reports or fixes to:	gcc@cs.buffalo.edu
46 */
47
48
49/* On tahoe, you have to go to memory to convert a register
50   from sub-word to word.  */
51
52rtx tahoe_reg_conversion_loc;
53
54int
55extensible_operand (op, mode)
56     rtx op;
57     enum machine_mode mode;
58{
59  if ((GET_CODE (op) == REG
60       || (GET_CODE (op) == SUBREG
61	   && GET_CODE (SUBREG_REG (op)) == REG))
62      && tahoe_reg_conversion_loc == 0)
63    tahoe_reg_conversion_loc = assign_stack_local (SImode, GET_MODE_SIZE (SImode));
64  return general_operand (op, mode);
65}
66
67/* most of the print_operand_address function was taken from the vax	*/
68/* since the modes are basically the same. I had to add a special case,	*/
69/* though, for symbol references with offsets.				*/
70
71print_operand_address (file, addr)
72     FILE *file;
73     register rtx addr;
74{
75  register rtx reg1, reg2, breg, ireg;
76  rtx offset;
77  static char *reg_name[] = REGISTER_NAMES;
78
79 retry:
80  switch (GET_CODE (addr))
81    {
82    case MEM:
83      fprintf (file, "*");
84      addr = XEXP (addr, 0);
85      goto retry;
86
87    case REG:
88      fprintf (file, "(%s)", reg_name [REGNO (addr)]);
89      break;
90
91    case PRE_DEC:
92      fprintf (file, "-(%s)", reg_name [REGNO (XEXP (addr, 0))]);
93      break;
94
95    case POST_INC:
96      fprintf (file, "(%s)+", reg_name [REGNO (XEXP (addr, 0))]);
97      break;
98
99    case PLUS:
100      reg1 = 0;	reg2 = 0;
101      ireg = 0;	breg = 0;
102      offset = 0;
103
104      if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
105	  && GET_CODE (XEXP (addr, 1)) == CONST_INT)
106	output_addr_const (file, addr);
107
108      if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
109	  && GET_CODE (XEXP (addr, 0)) == CONST_INT)
110	output_addr_const (file, addr);
111
112      if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
113	  || GET_CODE (XEXP (addr, 0)) == MEM)
114	{
115	  offset = XEXP (addr, 0);
116	  addr = XEXP (addr, 1);
117	}
118      else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
119	       || GET_CODE (XEXP (addr, 1)) == MEM)
120	{
121	  offset = XEXP (addr, 1);
122	  addr = XEXP (addr, 0);
123	}
124      if (GET_CODE (addr) != PLUS)
125	;
126      else if (GET_CODE (XEXP (addr, 0)) == MULT)
127	{
128	  reg1 = XEXP (addr, 0);
129	  addr = XEXP (addr, 1);
130	}
131      else if (GET_CODE (XEXP (addr, 1)) == MULT)
132	{
133	  reg1 = XEXP (addr, 1);
134	  addr = XEXP (addr, 0);
135	}
136      else if (GET_CODE (XEXP (addr, 0)) == REG)
137	{
138	  reg1 = XEXP (addr, 0);
139	  addr = XEXP (addr, 1);
140	}
141      else if (GET_CODE (XEXP (addr, 1)) == REG)
142	{
143	  reg1 = XEXP (addr, 1);
144	  addr = XEXP (addr, 0);
145	}
146      if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
147	{
148	  if (reg1 == 0)
149	    reg1 = addr;
150	  else
151	    reg2 = addr;
152	  addr = 0;
153	}
154      if (offset != 0)
155	{
156	  if (addr != 0) abort ();
157	  addr = offset;
158	}
159      if (reg1 != 0 && GET_CODE (reg1) == MULT)
160	{
161	  breg = reg2;
162	  ireg = reg1;
163	}
164      else if (reg2 != 0 && GET_CODE (reg2) == MULT)
165	{
166	  breg = reg1;
167	  ireg = reg2;
168	}
169      else if (reg2 != 0 || GET_CODE (addr) == MEM)
170	{
171	  breg = reg2;
172	  ireg = reg1;
173	}
174      else
175	{
176	  breg = reg1;
177	  ireg = reg2;
178	}
179      if (addr != 0)
180	output_address (offset);
181      if (breg != 0)
182	{
183	  if (GET_CODE (breg) != REG)
184	    abort ();
185	  fprintf (file, "(%s)", reg_name[REGNO (breg)]);
186	}
187      if (ireg != 0)
188	{
189	  if (GET_CODE (ireg) == MULT)
190	    ireg = XEXP (ireg, 0);
191	  if (GET_CODE (ireg) != REG)
192	    abort ();
193	  fprintf (file, "[%s]", reg_name[REGNO (ireg)]);
194	}
195      break;
196
197    default:
198      output_addr_const (file, addr);
199    }
200}
201
202/* Do a quick check and find out what the best way to do the */
203/* mini-move is. Could be a push or a move.....		     */
204
205static char *
206singlemove_string (operands)
207     rtx *operands;
208{
209  if (operands[1] == const0_rtx)
210      return "clrl %0";
211  if (push_operand (operands[0], SImode))
212    return "pushl %1";
213  return "movl %1,%0";
214}
215
216/* given the rtx for an address, return true if the given */
217/* register number is used in the address somewhere.	  */
218
219regisused(addr,regnum)
220rtx addr;
221int regnum;
222{
223	if (GET_CODE(addr) == REG)
224		if (REGNO(addr) == regnum)
225			return (1);
226		else
227			return (0);
228
229	if (GET_CODE(addr) == MEM)
230		return regisused(XEXP(addr,0),regnum);
231
232	if ((GET_CODE(addr) == MULT) || (GET_CODE(addr) == PLUS))
233		return ((regisused(XEXP(addr,0),regnum)) ||
234					(regisused(XEXP(addr,1),regnum)));
235
236	return 0;
237}
238
239
240/* Given some rtx, traverse it and return the register used in a */
241/* index. If no index is found, return 0.			 */
242
243rtx
244index_reg(addr)
245rtx addr;
246{
247	rtx temp;
248
249	if (GET_CODE(addr) == MEM)
250		return index_reg(XEXP(addr,0));
251
252	if (GET_CODE(addr) == MULT)
253		if (GET_CODE(XEXP(addr,0)) == REG)
254			return XEXP(addr,0);
255		else
256			return XEXP(addr,1);
257
258	if (GET_CODE(addr) == PLUS)
259		if (temp = index_reg(XEXP(addr,0)))
260			return temp;
261		else
262			return index_reg(XEXP(addr,1));
263
264	return 0;
265}
266
267
268/* simulate the move double by generating two movl's. You have */
269/* to be careful about mixing modes here.		       */
270
271char *
272output_move_double (operands)
273     rtx *operands;
274{
275  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, INDOP, CNSTOP, RNDOP }
276    optype0, optype1;
277  rtx latehalf[2];
278  rtx shftreg0 = 0, shftreg1 = 0;
279  rtx temp0 = 0, temp1 = 0;
280  rtx addreg0 = 0, addreg1 = 0;
281  int dohighfirst = 0;
282
283  /* First classify both operands. */
284
285  if (REG_P (operands[0]))
286    optype0 = REGOP;
287  else if ((GET_CODE(operands[0])==MEM) && (shftreg0=index_reg(operands[0])))
288    optype0 = INDOP;
289  else if (offsettable_memref_p (operands[0]))
290    optype0 = OFFSOP;
291  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) {
292    optype0 = PUSHOP;
293    dohighfirst++;
294  } else if (GET_CODE (operands[0]) == MEM)
295    optype0 = MEMOP;
296  else
297    optype0 = RNDOP;
298
299  if (REG_P (operands[1]))
300    optype1 = REGOP;
301  else if ((GET_CODE(operands[1])==MEM) && (shftreg1=index_reg(operands[1])))
302    optype1 = INDOP;
303  else if (offsettable_memref_p (operands[1]))
304    optype1 = OFFSOP;
305  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
306    optype1 = POPOP;
307  else if (GET_CODE (operands[1]) == MEM)
308    optype1 = MEMOP;
309  else if (CONSTANT_P (operands[1]))
310    optype1 = CNSTOP;
311  else
312    optype1 = RNDOP;
313
314  /* set up for the high byte move for operand zero */
315
316  switch (optype0) {
317
318	/* if it's a register, just use the next highest in the */
319	/* high address move.					*/
320
321	case REGOP  : latehalf[0] = gen_rtx (REG,SImode,REGNO(operands[0])+1);
322		      break;
323
324	/* for an offsettable address, use the gcc function to  */
325	/* modify the operand to get an offset of 4 higher for  */
326	/* the second move.					*/
327
328	case OFFSOP : latehalf[0] = adj_offsettable_operand (operands[0], 4);
329		      break;
330
331	/* if the operand is MEMOP type, it must be a pointer	*/
332	/* to a pointer. So just remember to increase the mem	*/
333	/* location and use the same operand.			*/
334
335	case MEMOP  : latehalf[0] = operands[0];
336		      addreg0 = XEXP(operands[0],0);
337		      break;
338
339	/* if we're dealing with a push instruction, just leave */
340	/* the operand alone since it auto-increments.		*/
341
342	case PUSHOP : latehalf[0] = operands[0];
343		      break;
344
345	/* YUCK! Indexed addressing!! If the address is considered   */
346	/* offsettable, go use the offset in the high part. Otherwise */
347	/* find what exactly is being added to the multiplication. If */
348	/* it's a mem reference, increment that with the high part   */
349	/* being unchanged to cause the shift. If it's a reg, do the */
350	/* same. If you can't identify it, abort. Remember that the  */
351	/* shift register was already set during identification.     */
352
353	case INDOP  : if (offsettable_memref_p(operands[0])) {
354			   latehalf[0] = adj_offsettable_operand(operands[0],4);
355			   break;
356		      }
357
358		      latehalf[0] = operands[0];
359
360		      temp0 = XEXP(XEXP(operands[0],0),0);
361                      if (GET_CODE(temp0) == MULT) {
362			   temp1 = temp0;
363			   temp0 = XEXP(XEXP(operands[0],0),1);
364		      } else {
365			   temp1 = XEXP(XEXP(operands[0],0),1);
366			   if (GET_CODE(temp1) != MULT)
367				abort();
368		      }
369
370		      if (GET_CODE(temp0) == MEM)
371			   addreg0 = temp0;
372		      else if (GET_CODE(temp0) == REG)
373			   addreg0 = temp0;
374		      else
375			   abort();
376
377		      break;
378
379	/* if we don't know the operand type, print a friendly  */
380	/* little error message...   8-)			*/
381
382	case RNDOP  :
383	default     : abort();
384  }
385
386  /* do the same setup for operand one */
387
388  switch (optype1) {
389
390	case REGOP  : latehalf[1] = gen_rtx(REG,SImode,REGNO(operands[1])+1);
391		      break;
392
393	case OFFSOP : latehalf[1] = adj_offsettable_operand (operands[1], 4);
394		      break;
395
396	case MEMOP  : latehalf[1] = operands[1];
397		      addreg1 = XEXP(operands[1],0);
398		      break;
399
400	case POPOP  : latehalf[1] = operands[1];
401		      break;
402
403	case INDOP  : if (offsettable_memref_p(operands[1])) {
404			   latehalf[1] = adj_offsettable_operand(operands[1],4);
405			   break;
406		      }
407
408		      latehalf[1] = operands[1];
409
410		      temp0 = XEXP(XEXP(operands[1],0),0);
411                      if (GET_CODE(temp0) == MULT) {
412			   temp1 = temp0;
413			   temp0 = XEXP(XEXP(operands[1],0),1);
414		      } else {
415			   temp1 = XEXP(XEXP(operands[1],0),1);
416			   if (GET_CODE(temp1) != MULT)
417				abort();
418		      }
419
420		      if (GET_CODE(temp0) == MEM)
421			   addreg1 = temp0;
422		      else if (GET_CODE(temp0) == REG)
423			   addreg1 = temp0;
424		      else
425			   abort();
426
427		      break;
428
429	case CNSTOP :
430	  if (GET_CODE (operands[1]) == CONST_DOUBLE)
431	    split_double (operands[1], &operands[1], &latehalf[1]);
432	  else if (CONSTANT_P (operands[1]))
433	    latehalf[1] = const0_rtx;
434	  else abort ();
435	  break;
436
437	case RNDOP  :
438	default     : abort();
439  }
440
441
442  /* double the register used for shifting in both of the operands */
443  /* but make sure the same register isn't doubled twice!	   */
444
445  if (shftreg0 && shftreg1 && (rtx_equal_p(shftreg0,shftreg1)))
446	output_asm_insn("addl2 %0,%0", &shftreg0);
447  else {
448	if (shftreg0)
449		output_asm_insn("addl2 %0,%0", &shftreg0);
450	if (shftreg1)
451		output_asm_insn("addl2 %0,%0", &shftreg1);
452  }
453
454  /* if the destination is a register and that register is needed in  */
455  /* the source addressing mode, swap the order of the moves since we */
456  /* don't want this destroyed til last. If both regs are used, not   */
457  /* much we can do, so abort. If these becomes a problem, maybe we   */
458  /* can do it on the stack?					      */
459
460  if (GET_CODE(operands[0])==REG && regisused(operands[1],REGNO(operands[0])))
461	if (regisused(latehalf[1],REGNO(latehalf[0])))
462		8;
463	else
464		dohighfirst++;
465
466  /* if we're pushing, do the high address part first. */
467
468  if (dohighfirst) {
469
470	if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
471		output_asm_insn("addl2 $4,%0", &addreg0);
472	else {
473		if (addreg0)
474			output_asm_insn("addl2 $4,%0", &addreg0);
475		if (addreg1)
476			output_asm_insn("addl2 $4,%0", &addreg1);
477	}
478
479	output_asm_insn(singlemove_string(latehalf), latehalf);
480
481	if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
482		output_asm_insn("subl2 $4,%0", &addreg0);
483	else {
484		if (addreg0)
485			output_asm_insn("subl2 $4,%0", &addreg0);
486		if (addreg1)
487			output_asm_insn("subl2 $4,%0", &addreg1);
488	}
489
490	return singlemove_string(operands);
491  }
492
493  output_asm_insn(singlemove_string(operands), operands);
494
495  if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
496	output_asm_insn("addl2 $4,%0", &addreg0);
497  else {
498	if (addreg0)
499		output_asm_insn("addl2 $4,%0", &addreg0);
500	if (addreg1)
501		output_asm_insn("addl2 $4,%0", &addreg1);
502  }
503
504  output_asm_insn(singlemove_string(latehalf), latehalf);
505
506  if (addreg0 && addreg1 && (rtx_equal_p(addreg0,addreg1)))
507	output_asm_insn("subl2 $4,%0", &addreg0);
508  else {
509	if (addreg0)
510		output_asm_insn("subl2 $4,%0", &addreg0);
511	if (addreg1)
512		output_asm_insn("subl2 $4,%0", &addreg1);
513  }
514
515  if (shftreg0 && shftreg1 && (rtx_equal_p(shftreg0,shftreg1)))
516	output_asm_insn("shar $1,%0,%0", &shftreg0);
517  else {
518	if (shftreg0)
519		output_asm_insn("shar $1,%0,%0", &shftreg0);
520	if (shftreg1)
521		output_asm_insn("shar $1,%0,%0", &shftreg1);
522  }
523
524  return "";
525}
526
527
528/* This checks if a zero_extended cmp[bw] can be replaced by a sign_extended
529   cmp[bw]. This can be done if the operand is a constant that fits in a
530   byte/word or a memory operand. Besides that the next instruction must be an
531   unsigned compare. Some of these tests are done by the machine description */
532
533int
534tahoe_cmp_check (insn, op, max)
535rtx insn, op; int max;
536{
537    if (GET_CODE (op) == CONST_INT
538	&& ( INTVAL (op) < 0 || INTVAL (op) > max ))
539	return 0;
540    {
541	register rtx next = NEXT_INSN (insn);
542
543	if ((GET_CODE (next) == JUMP_INSN
544	   || GET_CODE (next) == INSN
545	   || GET_CODE (next) == CALL_INSN))
546	    {
547		next = PATTERN (next);
548		if (GET_CODE (next) == SET
549		    && SET_DEST (next) == pc_rtx
550		    && GET_CODE (SET_SRC (next)) == IF_THEN_ELSE)
551		    switch (GET_CODE (XEXP (SET_SRC (next), 0)))
552			{
553			case EQ:
554			case NE:
555			case LTU:
556			case GTU:
557			case LEU:
558			case GEU:
559			    return 1;
560			}
561	    }
562    }
563    return 0;
564}
565