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