1/* Output routines for Sunplus S+CORE processor 2 Copyright (C) 2005 Free Software Foundation, Inc. 3 Contributed by Sunnorth. 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it 8 under the terms of the GNU General Public License as published 9 by the Free Software Foundation; either version 2, or (at your 10 option) any later version. 11 12 GCC is distributed in the hope that it will be useful, but WITHOUT 13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 15 License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING. If not, write to 19 the Free Software Foundation, 51 Franklin Street, Fifth Floor, 20 Boston, MA 02110-1301, USA. */ 21 22#include "config.h" 23#include "system.h" 24#include "coretypes.h" 25#include "tm.h" 26#include <signal.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-attr.h" 34#include "recog.h" 35#include "toplev.h" 36#include "output.h" 37#include "tree.h" 38#include "function.h" 39#include "expr.h" 40#include "optabs.h" 41#include "flags.h" 42#include "reload.h" 43#include "tm_p.h" 44#include "ggc.h" 45#include "gstab.h" 46#include "hashtab.h" 47#include "debug.h" 48#include "target.h" 49#include "target-def.h" 50#include "integrate.h" 51#include "langhooks.h" 52#include "cfglayout.h" 53#include "score-mdaux.h" 54 55#define GR_REG_CLASS_P(C) ((C) == G16_REGS || (C) == G32_REGS) 56#define SP_REG_CLASS_P(C) \ 57 ((C) == CN_REG || (C) == LC_REG || (C) == SC_REG || (C) == SP_REGS) 58#define CP_REG_CLASS_P(C) \ 59 ((C) == CP1_REGS || (C) == CP2_REGS || (C) == CP3_REGS || (C) == CPA_REGS) 60#define CE_REG_CLASS_P(C) \ 61 ((C) == HI_REG || (C) == LO_REG || (C) == CE_REGS) 62 63static int score_arg_partial_bytes (const CUMULATIVE_ARGS *, 64 enum machine_mode, tree, int); 65 66static int score_symbol_insns (enum score_symbol_type); 67 68static int score_address_insns (rtx, enum machine_mode); 69 70static bool score_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *); 71 72static int score_address_cost (rtx); 73 74#undef TARGET_ASM_FILE_START 75#define TARGET_ASM_FILE_START th_asm_file_start 76 77#undef TARGET_ASM_FILE_END 78#define TARGET_ASM_FILE_END th_asm_file_end 79 80#undef TARGET_ASM_FUNCTION_PROLOGUE 81#define TARGET_ASM_FUNCTION_PROLOGUE th_function_prologue 82 83#undef TARGET_ASM_FUNCTION_EPILOGUE 84#define TARGET_ASM_FUNCTION_EPILOGUE th_function_epilogue 85 86#undef TARGET_SCHED_ISSUE_RATE 87#define TARGET_SCHED_ISSUE_RATE th_issue_rate 88 89#undef TARGET_ASM_SELECT_RTX_SECTION 90#define TARGET_ASM_SELECT_RTX_SECTION th_select_rtx_section 91 92#undef TARGET_IN_SMALL_DATA_P 93#define TARGET_IN_SMALL_DATA_P th_in_small_data_p 94 95#undef TARGET_FUNCTION_OK_FOR_SIBCALL 96#define TARGET_FUNCTION_OK_FOR_SIBCALL th_function_ok_for_sibcall 97 98#undef TARGET_STRICT_ARGUMENT_NAMING 99#define TARGET_STRICT_ARGUMENT_NAMING th_strict_argument_naming 100 101#undef TARGET_ASM_OUTPUT_MI_THUNK 102#define TARGET_ASM_OUTPUT_MI_THUNK th_output_mi_thunk 103 104#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK 105#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true 106 107#undef TARGET_PROMOTE_FUNCTION_ARGS 108#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true 109 110#undef TARGET_PROMOTE_FUNCTION_RETURN 111#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true 112 113#undef TARGET_PROMOTE_PROTOTYPES 114#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true 115 116#undef TARGET_MUST_PASS_IN_STACK 117#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size 118 119#undef TARGET_ARG_PARTIAL_BYTES 120#define TARGET_ARG_PARTIAL_BYTES score_arg_partial_bytes 121 122#undef TARGET_PASS_BY_REFERENCE 123#define TARGET_PASS_BY_REFERENCE score_pass_by_reference 124 125#undef TARGET_RETURN_IN_MEMORY 126#define TARGET_RETURN_IN_MEMORY score_return_in_memory 127 128#undef TARGET_RTX_COSTS 129#define TARGET_RTX_COSTS score_rtx_costs 130 131#undef TARGET_ADDRESS_COST 132#define TARGET_ADDRESS_COST score_address_cost 133 134#undef TARGET_DEFAULT_TARGET_FLAGS 135#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT 136 137/* Implement TARGET_RETURN_IN_MEMORY. In S+core, 138 small structures are returned in a register. 139 Objects with varying size must still be returned in memory. */ 140static bool 141score_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED) 142{ 143 return ((TYPE_MODE (type) == BLKmode) 144 || (int_size_in_bytes (type) > 2 * UNITS_PER_WORD) 145 || (int_size_in_bytes (type) == -1)); 146} 147 148/* Return nonzero when an argument must be passed by reference. */ 149static bool 150score_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, 151 enum machine_mode mode, tree type, 152 bool named ATTRIBUTE_UNUSED) 153{ 154 /* If we have a variable-sized parameter, we have no choice. */ 155 return targetm.calls.must_pass_in_stack (mode, type); 156} 157 158/* Return a legitimate address for REG + OFFSET. */ 159static rtx 160score_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset) 161{ 162 if (!IMM_IN_RANGE (offset, 15, 1)) 163 { 164 reg = expand_simple_binop (GET_MODE (reg), PLUS, 165 gen_int_mode (offset & 0xffffc000, 166 GET_MODE (reg)), 167 reg, NULL, 0, OPTAB_WIDEN); 168 offset &= 0x3fff; 169 } 170 171 return plus_constant (reg, offset); 172} 173 174/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text 175 in order to avoid duplicating too much logic from elsewhere. */ 176static void 177th_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, 178 HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, 179 tree function) 180{ 181 rtx this, temp1, temp2, insn, fnaddr; 182 183 /* Pretend to be a post-reload pass while generating rtl. */ 184 no_new_pseudos = 1; 185 reload_completed = 1; 186 reset_block_changes (); 187 188 /* We need two temporary registers in some cases. */ 189 temp1 = gen_rtx_REG (Pmode, 8); 190 temp2 = gen_rtx_REG (Pmode, 9); 191 192 /* Find out which register contains the "this" pointer. */ 193 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) 194 this = gen_rtx_REG (Pmode, ARG_REG_FIRST + 1); 195 else 196 this = gen_rtx_REG (Pmode, ARG_REG_FIRST); 197 198 /* Add DELTA to THIS. */ 199 if (delta != 0) 200 { 201 rtx offset = GEN_INT (delta); 202 if (!CONST_OK_FOR_LETTER_P (delta, 'L')) 203 { 204 emit_move_insn (temp1, offset); 205 offset = temp1; 206 } 207 emit_insn (gen_add3_insn (this, this, offset)); 208 } 209 210 /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */ 211 if (vcall_offset != 0) 212 { 213 rtx addr; 214 215 /* Set TEMP1 to *THIS. */ 216 emit_move_insn (temp1, gen_rtx_MEM (Pmode, this)); 217 218 /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */ 219 addr = score_add_offset (temp2, temp1, vcall_offset); 220 221 /* Load the offset and add it to THIS. */ 222 emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr)); 223 emit_insn (gen_add3_insn (this, this, temp1)); 224 } 225 226 /* Jump to the target function. */ 227 fnaddr = XEXP (DECL_RTL (function), 0); 228 insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx)); 229 SIBLING_CALL_P (insn) = 1; 230 231 /* Run just enough of rest_of_compilation. This sequence was 232 "borrowed" from alpha.c. */ 233 insn = get_insns (); 234 insn_locators_initialize (); 235 split_all_insns_noflow (); 236 shorten_branches (insn); 237 final_start_function (insn, file, 1); 238 final (insn, file, 1); 239 final_end_function (); 240 241 /* Clean up the vars set above. Note that final_end_function resets 242 the global pointer for us. */ 243 reload_completed = 0; 244 no_new_pseudos = 0; 245} 246 247/* Implement TARGET_STRICT_ARGUMENT_NAMING. */ 248static bool 249th_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED) 250{ 251 return true; 252} 253 254/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ 255static bool 256th_function_ok_for_sibcall (ATTRIBUTE_UNUSED tree decl, 257 ATTRIBUTE_UNUSED tree exp) 258{ 259 return true; 260} 261 262struct score_arg_info 263{ 264 /* The argument's size, in bytes. */ 265 unsigned int num_bytes; 266 267 /* The number of words passed in registers, rounded up. */ 268 unsigned int reg_words; 269 270 /* The offset of the first register from GP_ARG_FIRST or FP_ARG_FIRST, 271 or ARG_REG_NUM if the argument is passed entirely on the stack. */ 272 unsigned int reg_offset; 273 274 /* The number of words that must be passed on the stack, rounded up. */ 275 unsigned int stack_words; 276 277 /* The offset from the start of the stack overflow area of the argument's 278 first stack word. Only meaningful when STACK_WORDS is nonzero. */ 279 unsigned int stack_offset; 280}; 281 282/* Fill INFO with information about a single argument. CUM is the 283 cumulative state for earlier arguments. MODE is the mode of this 284 argument and TYPE is its type (if known). NAMED is true if this 285 is a named (fixed) argument rather than a variable one. */ 286static void 287classify_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode, 288 tree type, int named, struct score_arg_info *info) 289{ 290 int even_reg_p; 291 unsigned int num_words, max_regs; 292 293 even_reg_p = 0; 294 if (GET_MODE_CLASS (mode) == MODE_INT 295 || GET_MODE_CLASS (mode) == MODE_FLOAT) 296 even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD); 297 else 298 if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD && named) 299 even_reg_p = 1; 300 301 if (TARGET_MUST_PASS_IN_STACK (mode, type)) 302 info->reg_offset = ARG_REG_NUM; 303 else 304 { 305 info->reg_offset = cum->num_gprs; 306 if (even_reg_p) 307 info->reg_offset += info->reg_offset & 1; 308 } 309 310 if (mode == BLKmode) 311 info->num_bytes = int_size_in_bytes (type); 312 else 313 info->num_bytes = GET_MODE_SIZE (mode); 314 315 num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; 316 max_regs = ARG_REG_NUM - info->reg_offset; 317 318 /* Partition the argument between registers and stack. */ 319 info->reg_words = MIN (num_words, max_regs); 320 info->stack_words = num_words - info->reg_words; 321 322 /* The alignment applied to registers is also applied to stack arguments. */ 323 if (info->stack_words) 324 { 325 info->stack_offset = cum->stack_words; 326 if (even_reg_p) 327 info->stack_offset += info->stack_offset & 1; 328 } 329} 330 331/* Set up the stack and frame (if desired) for the function. */ 332static void 333th_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) 334{ 335 const char *fnname; 336 struct score_frame_info *f = mda_cached_frame (); 337 HOST_WIDE_INT tsize = f->total_size; 338 339 fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); 340 if (!flag_inhibit_size_directive) 341 { 342 fputs ("\t.ent\t", file); 343 assemble_name (file, fnname); 344 fputs ("\n", file); 345 } 346 assemble_name (file, fnname); 347 fputs (":\n", file); 348 349 if (!flag_inhibit_size_directive) 350 { 351 fprintf (file, 352 "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s, %d\t\t" 353 "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d" 354 ", args= " HOST_WIDE_INT_PRINT_DEC 355 ", gp= " HOST_WIDE_INT_PRINT_DEC "\n", 356 (reg_names[(frame_pointer_needed) 357 ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]), 358 tsize, 359 reg_names[RA_REGNUM], 360 current_function_is_leaf ? 1 : 0, 361 f->var_size, 362 f->num_gp, 363 f->args_size, 364 f->cprestore_size); 365 366 fprintf(file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n", 367 f->mask, 368 (f->gp_sp_offset - f->total_size)); 369 } 370} 371 372/* Do any necessary cleanup after a function to restore stack, frame, 373 and regs. */ 374static void 375th_function_epilogue (FILE *file, 376 HOST_WIDE_INT size ATTRIBUTE_UNUSED) 377{ 378 if (!flag_inhibit_size_directive) 379 { 380 const char *fnname; 381 fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); 382 fputs ("\t.end\t", file); 383 assemble_name (file, fnname); 384 fputs ("\n", file); 385 } 386} 387 388/* Implement TARGET_SCHED_ISSUE_RATE. */ 389static int 390th_issue_rate (void) 391{ 392 return 1; 393} 394 395/* Returns true if X contains a SYMBOL_REF. */ 396static bool 397symbolic_expression_p (rtx x) 398{ 399 if (GET_CODE (x) == SYMBOL_REF) 400 return true; 401 402 if (GET_CODE (x) == CONST) 403 return symbolic_expression_p (XEXP (x, 0)); 404 405 if (UNARY_P (x)) 406 return symbolic_expression_p (XEXP (x, 0)); 407 408 if (ARITHMETIC_P (x)) 409 return (symbolic_expression_p (XEXP (x, 0)) 410 || symbolic_expression_p (XEXP (x, 1))); 411 412 return false; 413} 414 415/* Choose the section to use for the constant rtx expression X that has 416 mode MODE. */ 417static section * 418th_select_rtx_section (enum machine_mode mode, rtx x, 419 unsigned HOST_WIDE_INT align) 420{ 421 if (GET_MODE_SIZE (mode) <= SCORE_SDATA_MAX) 422 return get_named_section (0, ".sdata", 0); 423 else if (flag_pic && symbolic_expression_p (x)) 424 return get_named_section (0, ".data.rel.ro", 3); 425 else 426 return mergeable_constant_section (mode, align, 0); 427} 428 429/* Implement TARGET_IN_SMALL_DATA_P. */ 430static bool 431th_in_small_data_p (tree decl) 432{ 433 HOST_WIDE_INT size; 434 435 if (TREE_CODE (decl) == STRING_CST 436 || TREE_CODE (decl) == FUNCTION_DECL) 437 return false; 438 439 if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0) 440 { 441 const char *name; 442 name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); 443 if (strcmp (name, ".sdata") != 0 444 && strcmp (name, ".sbss") != 0) 445 return true; 446 if (!DECL_EXTERNAL (decl)) 447 return false; 448 } 449 size = int_size_in_bytes (TREE_TYPE (decl)); 450 return (size > 0 && size <= SCORE_SDATA_MAX); 451} 452 453/* Implement TARGET_ASM_FILE_START. */ 454static void 455th_asm_file_start (void) 456{ 457 default_file_start (); 458 fprintf (asm_out_file, ASM_COMMENT_START 459 "GCC for S+core %s \n", SCORE_GCC_VERSION); 460 461 if (flag_pic) 462 fprintf (asm_out_file, "\t.set pic\n"); 463} 464 465/* Implement TARGET_ASM_FILE_END. When using assembler macros, emit 466 .externs for any small-data variables that turned out to be external. */ 467struct extern_list *extern_head = 0; 468 469static void 470th_asm_file_end (void) 471{ 472 tree name_tree; 473 struct extern_list *p; 474 if (extern_head) 475 { 476 fputs ("\n", asm_out_file); 477 for (p = extern_head; p != 0; p = p->next) 478 { 479 name_tree = get_identifier (p->name); 480 if (!TREE_ASM_WRITTEN (name_tree) 481 && TREE_SYMBOL_REFERENCED (name_tree)) 482 { 483 TREE_ASM_WRITTEN (name_tree) = 1; 484 fputs ("\t.extern\t", asm_out_file); 485 assemble_name (asm_out_file, p->name); 486 fprintf (asm_out_file, ", %d\n", p->size); 487 } 488 } 489 } 490} 491 492static unsigned int sdata_max; 493 494int 495score_sdata_max (void) 496{ 497 return sdata_max; 498} 499 500/* default 0 = NO_REGS */ 501enum reg_class score_char_to_class[256]; 502 503/* Implement OVERRIDE_OPTIONS macro. */ 504void 505score_override_options (void) 506{ 507 flag_pic = false; 508 if (!flag_pic) 509 sdata_max = g_switch_set ? g_switch_value : DEFAULT_SDATA_MAX; 510 else 511 { 512 sdata_max = 0; 513 if (g_switch_set && (g_switch_value != 0)) 514 warning (0, "-fPIC and -G are incompatible"); 515 } 516 517 score_char_to_class['d'] = G32_REGS; 518 score_char_to_class['e'] = G16_REGS; 519 score_char_to_class['t'] = T32_REGS; 520 521 score_char_to_class['h'] = HI_REG; 522 score_char_to_class['l'] = LO_REG; 523 score_char_to_class['x'] = CE_REGS; 524 525 score_char_to_class['q'] = CN_REG; 526 score_char_to_class['y'] = LC_REG; 527 score_char_to_class['z'] = SC_REG; 528 score_char_to_class['a'] = SP_REGS; 529 530 score_char_to_class['c'] = CR_REGS; 531 532 score_char_to_class['b'] = CP1_REGS; 533 score_char_to_class['f'] = CP2_REGS; 534 score_char_to_class['i'] = CP3_REGS; 535 score_char_to_class['j'] = CPA_REGS; 536} 537 538/* Implement REGNO_REG_CLASS macro. */ 539int 540score_reg_class (int regno) 541{ 542 int c; 543 gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER); 544 545 if (regno == FRAME_POINTER_REGNUM 546 || regno == ARG_POINTER_REGNUM) 547 return ALL_REGS; 548 549 for (c = 0; c < N_REG_CLASSES; c++) 550 if (TEST_HARD_REG_BIT (reg_class_contents[c], regno)) 551 return c; 552 553 return NO_REGS; 554} 555 556/* Implement PREFERRED_RELOAD_CLASS macro. */ 557enum reg_class 558score_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class) 559{ 560 if (reg_class_subset_p (G16_REGS, class)) 561 return G16_REGS; 562 if (reg_class_subset_p (G32_REGS, class)) 563 return G32_REGS; 564 return class; 565} 566 567/* Implement SECONDARY_INPUT_RELOAD_CLASS 568 and SECONDARY_OUTPUT_RELOAD_CLASS macro. */ 569enum reg_class 570score_secondary_reload_class (enum reg_class class, 571 enum machine_mode mode ATTRIBUTE_UNUSED, 572 rtx x) 573{ 574 int regno = -1; 575 if (GET_CODE (x) == REG || GET_CODE(x) == SUBREG) 576 regno = true_regnum (x); 577 578 if (!GR_REG_CLASS_P (class)) 579 return GP_REG_P (regno) ? NO_REGS : G32_REGS; 580 return NO_REGS; 581} 582 583/* Implement CONST_OK_FOR_LETTER_P macro. */ 584/* imm constraints 585 I imm16 << 16 586 J uimm5 587 K uimm16 588 L simm16 589 M uimm14 590 N simm14 */ 591int 592score_const_ok_for_letter_p (HOST_WIDE_INT value, char c) 593{ 594 switch (c) 595 { 596 case 'I': return ((value & 0xffff) == 0); 597 case 'J': return IMM_IN_RANGE (value, 5, 0); 598 case 'K': return IMM_IN_RANGE (value, 16, 0); 599 case 'L': return IMM_IN_RANGE (value, 16, 1); 600 case 'M': return IMM_IN_RANGE (value, 14, 0); 601 case 'N': return IMM_IN_RANGE (value, 14, 1); 602 default : return 0; 603 } 604} 605 606/* Implement EXTRA_CONSTRAINT macro. */ 607/* Z symbol_ref */ 608int 609score_extra_constraint (rtx op, char c) 610{ 611 switch (c) 612 { 613 case 'Z': 614 return GET_CODE (op) == SYMBOL_REF; 615 default: 616 gcc_unreachable (); 617 } 618} 619 620/* Return truth value on whether or not a given hard register 621 can support a given mode. */ 622int 623score_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) 624{ 625 int size = GET_MODE_SIZE (mode); 626 enum mode_class class = GET_MODE_CLASS (mode); 627 628 if (class == MODE_CC) 629 return regno == CC_REGNUM; 630 else if (regno == FRAME_POINTER_REGNUM 631 || regno == ARG_POINTER_REGNUM) 632 return class == MODE_INT; 633 else if (GP_REG_P (regno)) 634 /* ((regno <= (GP_REG_LAST- HARD_REGNO_NREGS (dummy, mode)) + 1) */ 635 return !(regno & 1) || (size <= UNITS_PER_WORD); 636 else if (CE_REG_P (regno)) 637 return (class == MODE_INT 638 && ((size <= UNITS_PER_WORD) 639 || (regno == CE_REG_FIRST && size == 2 * UNITS_PER_WORD))); 640 else 641 return (class == MODE_INT) && (size <= UNITS_PER_WORD); 642} 643 644/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame 645 pointer or argument pointer. TO is either the stack pointer or 646 hard frame pointer. */ 647HOST_WIDE_INT 648score_initial_elimination_offset (int from, 649 int to ATTRIBUTE_UNUSED) 650{ 651 struct score_frame_info *f = mda_compute_frame_size (get_frame_size ()); 652 switch (from) 653 { 654 case ARG_POINTER_REGNUM: 655 return f->total_size; 656 case FRAME_POINTER_REGNUM: 657 return 0; 658 default: 659 gcc_unreachable (); 660 } 661} 662 663/* Argument support functions. */ 664 665/* Initialize CUMULATIVE_ARGS for a function. */ 666void 667score_init_cumulative_args (CUMULATIVE_ARGS *cum, 668 tree fntype ATTRIBUTE_UNUSED, 669 rtx libname ATTRIBUTE_UNUSED) 670{ 671 memset (cum, 0, sizeof (CUMULATIVE_ARGS)); 672} 673 674/* Implement FUNCTION_ARG_ADVANCE macro. */ 675void 676score_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, 677 tree type, int named) 678{ 679 struct score_arg_info info; 680 classify_arg (cum, mode, type, named, &info); 681 cum->num_gprs = info.reg_offset + info.reg_words; 682 if (info.stack_words > 0) 683 cum->stack_words = info.stack_offset + info.stack_words; 684 cum->arg_number++; 685} 686 687/* Implement TARGET_ARG_PARTIAL_BYTES macro. */ 688static int 689score_arg_partial_bytes (const CUMULATIVE_ARGS *cum, 690 enum machine_mode mode, tree type, int named) 691{ 692 struct score_arg_info info; 693 classify_arg (cum, mode, type, named, &info); 694 return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0; 695} 696 697/* Implement FUNCTION_ARG macro. */ 698rtx 699score_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode, 700 tree type, int named) 701{ 702 struct score_arg_info info; 703 704 if (mode == VOIDmode || !named) 705 return 0; 706 707 classify_arg (cum, mode, type, named, &info); 708 709 if (info.reg_offset == ARG_REG_NUM) 710 return 0; 711 712 if (!info.stack_words) 713 return gen_rtx_REG (mode, ARG_REG_FIRST + info.reg_offset); 714 else 715 { 716 rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words)); 717 unsigned int i, part_offset = 0; 718 for (i = 0; i < info.reg_words; i++) 719 { 720 rtx reg; 721 reg = gen_rtx_REG (SImode, ARG_REG_FIRST + info.reg_offset + i); 722 XVECEXP (ret, 0, i) = gen_rtx_EXPR_LIST (SImode, reg, 723 GEN_INT (part_offset)); 724 part_offset += UNITS_PER_WORD; 725 } 726 return ret; 727 } 728} 729 730/* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, 731 VALTYPE is the return type and MODE is VOIDmode. For libcalls, 732 VALTYPE is null and MODE is the mode of the return value. */ 733rtx 734score_function_value (tree valtype, tree func ATTRIBUTE_UNUSED, 735 enum machine_mode mode) 736{ 737 if (valtype) 738 { 739 int unsignedp; 740 mode = TYPE_MODE (valtype); 741 unsignedp = TYPE_UNSIGNED (valtype); 742 mode = promote_mode (valtype, mode, &unsignedp, 1); 743 } 744 return gen_rtx_REG (mode, RT_REGNUM); 745} 746 747/* Implement INITIALIZE_TRAMPOLINE macro. */ 748void 749score_initialize_trampoline (rtx ADDR, rtx FUNC, rtx CHAIN) 750{ 751#define FFCACHE "_flush_cache" 752#define CODE_SIZE (TRAMPOLINE_INSNS * UNITS_PER_WORD) 753 754 unsigned int tramp[TRAMPOLINE_INSNS] = { 755 0x8103bc56, /* mv r8, r3 */ 756 0x9000bc05, /* bl 0x0x8 */ 757 0xc1238000 | (CODE_SIZE - 8), /* lw r9, &func */ 758 0xc0038000 759 | (STATIC_CHAIN_REGNUM << 21) 760 | (CODE_SIZE - 4), /* lw static chain reg, &chain */ 761 0x8068bc56, /* mv r3, r8 */ 762 0x8009bc08, /* br r9 */ 763 0x0, 764 0x0, 765 }; 766 rtx pfunc, pchain; 767 int i; 768 769 for (i = 0; i < TRAMPOLINE_INSNS; i++) 770 emit_move_insn (gen_rtx_MEM (ptr_mode, plus_constant (ADDR, i << 2)), 771 GEN_INT (tramp[i])); 772 773 pfunc = plus_constant (ADDR, CODE_SIZE); 774 pchain = plus_constant (ADDR, CODE_SIZE + GET_MODE_SIZE (ptr_mode)); 775 776 emit_move_insn (gen_rtx_MEM (ptr_mode, pfunc), FUNC); 777 emit_move_insn (gen_rtx_MEM (ptr_mode, pchain), CHAIN); 778 emit_library_call (gen_rtx_SYMBOL_REF (Pmode, FFCACHE), 779 0, VOIDmode, 2, 780 ADDR, Pmode, 781 GEN_INT (TRAMPOLINE_SIZE), SImode); 782#undef FFCACHE 783#undef CODE_SIZE 784} 785 786/* This function is used to implement REG_MODE_OK_FOR_BASE_P macro. */ 787int 788score_regno_mode_ok_for_base_p (int regno, int strict) 789{ 790 if (regno >= FIRST_PSEUDO_REGISTER) 791 { 792 if (!strict) 793 return 1; 794 regno = reg_renumber[regno]; 795 } 796 if (regno == ARG_POINTER_REGNUM 797 || regno == FRAME_POINTER_REGNUM) 798 return 1; 799 return GP_REG_P (regno); 800} 801 802/* Implement GO_IF_LEGITIMATE_ADDRESS macro. */ 803int 804score_address_p (enum machine_mode mode, rtx x, int strict) 805{ 806 struct score_address_info addr; 807 808 return mda_classify_address (&addr, mode, x, strict); 809} 810 811/* Copy VALUE to a register and return that register. If new psuedos 812 are allowed, copy it into a new register, otherwise use DEST. */ 813static rtx 814score_force_temporary (rtx dest, rtx value) 815{ 816 if (!no_new_pseudos) 817 return force_reg (Pmode, value); 818 else 819 { 820 emit_move_insn (copy_rtx (dest), value); 821 return dest; 822 } 823} 824 825/* Return a LO_SUM expression for ADDR. TEMP is as for score_force_temporary 826 and is used to load the high part into a register. */ 827static rtx 828score_split_symbol (rtx temp, rtx addr) 829{ 830 rtx high = score_force_temporary (temp, 831 gen_rtx_HIGH (Pmode, copy_rtx (addr))); 832 return gen_rtx_LO_SUM (Pmode, high, addr); 833} 834 835/* This function is used to implement LEGITIMIZE_ADDRESS. If *XLOC can 836 be legitimized in a way that the generic machinery might not expect, 837 put the new address in *XLOC and return true. */ 838int 839score_legitimize_address (rtx *xloc) 840{ 841 enum score_symbol_type symbol_type; 842 843 if (mda_symbolic_constant_p (*xloc, &symbol_type) 844 && symbol_type == SYMBOL_GENERAL) 845 { 846 *xloc = score_split_symbol (0, *xloc); 847 return 1; 848 } 849 850 if (GET_CODE (*xloc) == PLUS 851 && GET_CODE (XEXP (*xloc, 1)) == CONST_INT) 852 { 853 rtx reg = XEXP (*xloc, 0); 854 if (!mda_valid_base_register_p (reg, 0)) 855 reg = copy_to_mode_reg (Pmode, reg); 856 *xloc = score_add_offset (NULL, reg, INTVAL (XEXP (*xloc, 1))); 857 return 1; 858 } 859 return 0; 860} 861 862/* Return a number assessing the cost of moving a register in class 863 FROM to class TO. */ 864int 865score_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, 866 enum reg_class from, enum reg_class to) 867{ 868 if (GR_REG_CLASS_P (from)) 869 { 870 if (GR_REG_CLASS_P (to)) 871 return 2; 872 else if (SP_REG_CLASS_P (to)) 873 return 4; 874 else if (CP_REG_CLASS_P (to)) 875 return 5; 876 else if (CE_REG_CLASS_P (to)) 877 return 6; 878 } 879 if (GR_REG_CLASS_P (to)) 880 { 881 if (GR_REG_CLASS_P (from)) 882 return 2; 883 else if (SP_REG_CLASS_P (from)) 884 return 4; 885 else if (CP_REG_CLASS_P (from)) 886 return 5; 887 else if (CE_REG_CLASS_P (from)) 888 return 6; 889 } 890 return 12; 891} 892 893/* Return the number of instructions needed to load a symbol of the 894 given type into a register. */ 895static int 896score_symbol_insns (enum score_symbol_type type) 897{ 898 switch (type) 899 { 900 case SYMBOL_GENERAL: 901 return 2; 902 903 case SYMBOL_SMALL_DATA: 904 return 1; 905 } 906 907 gcc_unreachable (); 908} 909 910/* Return the number of instructions needed to load or store a value 911 of mode MODE at X. Return 0 if X isn't valid for MODE. */ 912static int 913score_address_insns (rtx x, enum machine_mode mode) 914{ 915 struct score_address_info addr; 916 int factor; 917 918 if (mode == BLKmode) 919 factor = 1; 920 else 921 factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; 922 923 if (mda_classify_address (&addr, mode, x, false)) 924 switch (addr.type) 925 { 926 case ADD_REG: 927 case ADD_CONST_INT: 928 return factor; 929 930 case ADD_SYMBOLIC: 931 return factor * score_symbol_insns (addr.symbol_type); 932 } 933 return 0; 934} 935 936/* Implement TARGET_RTX_COSTS macro. */ 937static bool 938score_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code, 939 int *total) 940{ 941 enum machine_mode mode = GET_MODE (x); 942 943 switch (code) 944 { 945 case CONST_INT: 946 if (outer_code == SET) 947 { 948 if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I') 949 || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')) 950 *total = COSTS_N_INSNS (1); 951 else 952 *total = COSTS_N_INSNS (2); 953 } 954 else if (outer_code == PLUS || outer_code == MINUS) 955 { 956 if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'N')) 957 *total = 0; 958 else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I') 959 || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')) 960 *total = 1; 961 else 962 *total = COSTS_N_INSNS (2); 963 } 964 else if (outer_code == AND || outer_code == IOR) 965 { 966 if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'M')) 967 *total = 0; 968 else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I') 969 || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')) 970 *total = 1; 971 else 972 *total = COSTS_N_INSNS (2); 973 } 974 else 975 { 976 *total = 0; 977 } 978 return true; 979 980 case CONST: 981 case SYMBOL_REF: 982 case LABEL_REF: 983 case CONST_DOUBLE: 984 *total = COSTS_N_INSNS (2); 985 return true; 986 987 case MEM: 988 { 989 /* If the address is legitimate, return the number of 990 instructions it needs, otherwise use the default handling. */ 991 int n = score_address_insns (XEXP (x, 0), GET_MODE (x)); 992 if (n > 0) 993 { 994 *total = COSTS_N_INSNS (n + 1); 995 return true; 996 } 997 return false; 998 } 999 1000 case FFS: 1001 *total = COSTS_N_INSNS (6); 1002 return true; 1003 1004 case NOT: 1005 *total = COSTS_N_INSNS (1); 1006 return true; 1007 1008 case AND: 1009 case IOR: 1010 case XOR: 1011 if (mode == DImode) 1012 { 1013 *total = COSTS_N_INSNS (2); 1014 return true; 1015 } 1016 return false; 1017 1018 case ASHIFT: 1019 case ASHIFTRT: 1020 case LSHIFTRT: 1021 if (mode == DImode) 1022 { 1023 *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) 1024 ? 4 : 12); 1025 return true; 1026 } 1027 return false; 1028 1029 case ABS: 1030 *total = COSTS_N_INSNS (4); 1031 return true; 1032 1033 case PLUS: 1034 case MINUS: 1035 if (mode == DImode) 1036 { 1037 *total = COSTS_N_INSNS (4); 1038 return true; 1039 } 1040 *total = COSTS_N_INSNS (1); 1041 return true; 1042 1043 case NEG: 1044 if (mode == DImode) 1045 { 1046 *total = COSTS_N_INSNS (4); 1047 return true; 1048 } 1049 return false; 1050 1051 case MULT: 1052 *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (12); 1053 return true; 1054 1055 case DIV: 1056 case MOD: 1057 case UDIV: 1058 case UMOD: 1059 *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (33); 1060 return true; 1061 1062 case SIGN_EXTEND: 1063 case ZERO_EXTEND: 1064 switch (GET_MODE (XEXP (x, 0))) 1065 { 1066 case QImode: 1067 case HImode: 1068 if (GET_CODE (XEXP (x, 0)) == MEM) 1069 { 1070 *total = COSTS_N_INSNS (2); 1071 1072 if (!TARGET_LITTLE_ENDIAN && 1073 side_effects_p (XEXP (XEXP (x, 0), 0))) 1074 *total = 100; 1075 } 1076 else 1077 *total = COSTS_N_INSNS (1); 1078 break; 1079 1080 default: 1081 *total = COSTS_N_INSNS (1); 1082 break; 1083 } 1084 return true; 1085 1086 default: 1087 return false; 1088 } 1089} 1090 1091/* Implement TARGET_ADDRESS_COST macro. */ 1092int 1093score_address_cost (rtx addr) 1094{ 1095 return score_address_insns (addr, SImode); 1096} 1097 1098/* Implement ASM_OUTPUT_EXTERNAL macro. */ 1099int 1100score_output_external (FILE *file ATTRIBUTE_UNUSED, 1101 tree decl, const char *name) 1102{ 1103 register struct extern_list *p; 1104 1105 if (th_in_small_data_p (decl)) 1106 { 1107 p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); 1108 p->next = extern_head; 1109 p->name = name; 1110 p->size = int_size_in_bytes (TREE_TYPE (decl)); 1111 extern_head = p; 1112 } 1113 return 0; 1114} 1115 1116/* Output format asm string. */ 1117void 1118score_declare_object (FILE *stream, const char *name, 1119 const char *directive, const char *fmt, ...) 1120{ 1121 va_list ap; 1122 fputs (directive, stream); 1123 assemble_name (stream, name); 1124 va_start (ap, fmt); 1125 vfprintf (stream, fmt, ap); 1126 va_end (ap); 1127} 1128 1129/* Implement RETURN_ADDR_RTX. Note, we do not support moving 1130 back to a previous frame. */ 1131rtx 1132score_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) 1133{ 1134 if (count != 0) 1135 return const0_rtx; 1136 return get_hard_reg_initial_val (Pmode, RA_REGNUM); 1137} 1138 1139/* Implement PRINT_OPERAND macro. */ 1140/* Score-specific operand codes: 1141 '[' print .set nor1 directive 1142 ']' print .set r1 directive 1143 'U' print hi part of a CONST_INT rtx 1144 'E' print log2(v) 1145 'F' print log2(~v) 1146 'D' print SFmode const double 1147 'S' selectively print "!" if operand is 15bit instruction accessible 1148 'V' print "v!" if operand is 15bit instruction accessible, or "lfh!" 1149 'L' low part of DImode reg operand 1150 'H' high part of DImode reg operand 1151 'C' print part of opcode for a branch condition. */ 1152void 1153score_print_operand (FILE *file, rtx op, int c) 1154{ 1155 enum rtx_code code = -1; 1156 if (!PRINT_OPERAND_PUNCT_VALID_P (c)) 1157 code = GET_CODE (op); 1158 1159 if (c == '[') 1160 { 1161 fprintf (file, ".set r1\n"); 1162 } 1163 else if (c == ']') 1164 { 1165 fprintf (file, "\n\t.set nor1"); 1166 } 1167 else if (c == 'U') 1168 { 1169 gcc_assert (code == CONST_INT); 1170 fprintf (file, HOST_WIDE_INT_PRINT_HEX, 1171 (INTVAL (op) >> 16) & 0xffff); 1172 } 1173 else if (c == 'D') 1174 { 1175 if (GET_CODE (op) == CONST_DOUBLE) 1176 { 1177 rtx temp = gen_lowpart (SImode, op); 1178 gcc_assert (GET_MODE (op) == SFmode); 1179 fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (temp) & 0xffffffff); 1180 } 1181 else 1182 output_addr_const (file, op); 1183 } 1184 else if (c == 'S') 1185 { 1186 gcc_assert (code == REG); 1187 if (G16_REG_P (REGNO (op))) 1188 fprintf (file, "!"); 1189 } 1190 else if (c == 'V') 1191 { 1192 gcc_assert (code == REG); 1193 fprintf (file, G16_REG_P (REGNO (op)) ? "v!" : "lfh!"); 1194 } 1195 else if (c == 'C') 1196 { 1197 enum machine_mode mode = GET_MODE (XEXP (op, 0)); 1198 1199 switch (code) 1200 { 1201 case EQ: fputs ("eq", file); break; 1202 case NE: fputs ("ne", file); break; 1203 case GT: fputs ("gt", file); break; 1204 case GE: fputs (mode != CCmode ? "pl" : "ge", file); break; 1205 case LT: fputs (mode != CCmode ? "mi" : "lt", file); break; 1206 case LE: fputs ("le", file); break; 1207 case GTU: fputs ("gtu", file); break; 1208 case GEU: fputs ("cs", file); break; 1209 case LTU: fputs ("cc", file); break; 1210 case LEU: fputs ("leu", file); break; 1211 default: 1212 output_operand_lossage ("invalid operand for code: '%c'", code); 1213 } 1214 } 1215 else if (c == 'E') 1216 { 1217 unsigned HOST_WIDE_INT i; 1218 unsigned HOST_WIDE_INT pow2mask = 1; 1219 unsigned HOST_WIDE_INT val; 1220 1221 val = INTVAL (op); 1222 for (i = 0; i < 32; i++) 1223 { 1224 if (val == pow2mask) 1225 break; 1226 pow2mask <<= 1; 1227 } 1228 gcc_assert (i < 32); 1229 fprintf (file, HOST_WIDE_INT_PRINT_HEX, i); 1230 } 1231 else if (c == 'F') 1232 { 1233 unsigned HOST_WIDE_INT i; 1234 unsigned HOST_WIDE_INT pow2mask = 1; 1235 unsigned HOST_WIDE_INT val; 1236 1237 val = ~INTVAL (op); 1238 for (i = 0; i < 32; i++) 1239 { 1240 if (val == pow2mask) 1241 break; 1242 pow2mask <<= 1; 1243 } 1244 gcc_assert (i < 32); 1245 fprintf (file, HOST_WIDE_INT_PRINT_HEX, i); 1246 } 1247 else if (code == REG) 1248 { 1249 int regnum = REGNO (op); 1250 if ((c == 'H' && !WORDS_BIG_ENDIAN) 1251 || (c == 'L' && WORDS_BIG_ENDIAN)) 1252 regnum ++; 1253 fprintf (file, "%s", reg_names[regnum]); 1254 } 1255 else 1256 { 1257 switch (code) 1258 { 1259 case MEM: 1260 score_print_operand_address (file, op); 1261 break; 1262 default: 1263 output_addr_const (file, op); 1264 } 1265 } 1266} 1267 1268/* Implement PRINT_OPERAND_ADDRESS macro. */ 1269void 1270score_print_operand_address (FILE *file, rtx x) 1271{ 1272 struct score_address_info addr; 1273 enum rtx_code code = GET_CODE (x); 1274 enum machine_mode mode = GET_MODE (x); 1275 1276 if (code == MEM) 1277 x = XEXP (x, 0); 1278 1279 if (mda_classify_address (&addr, mode, x, true)) 1280 { 1281 switch (addr.type) 1282 { 1283 case ADD_REG: 1284 { 1285 switch (addr.code) 1286 { 1287 case PRE_DEC: 1288 fprintf (file, "[%s,-%ld]+", reg_names[REGNO (addr.reg)], 1289 INTVAL (addr.offset)); 1290 break; 1291 case POST_DEC: 1292 fprintf (file, "[%s]+,-%ld", reg_names[REGNO (addr.reg)], 1293 INTVAL (addr.offset)); 1294 break; 1295 case PRE_INC: 1296 fprintf (file, "[%s, %ld]+", reg_names[REGNO (addr.reg)], 1297 INTVAL (addr.offset)); 1298 break; 1299 case POST_INC: 1300 fprintf (file, "[%s]+, %ld", reg_names[REGNO (addr.reg)], 1301 INTVAL (addr.offset)); 1302 break; 1303 default: 1304 fprintf (file, "[%s,%ld]", reg_names[REGNO (addr.reg)], 1305 INTVAL (addr.offset)); 1306 break; 1307 } 1308 } 1309 return; 1310 case ADD_CONST_INT: 1311 case ADD_SYMBOLIC: 1312 output_addr_const (file, x); 1313 return; 1314 } 1315 } 1316 print_rtl (stderr, x); 1317 gcc_unreachable (); 1318} 1319 1320/* Implement SELECT_CC_MODE macro. */ 1321enum machine_mode 1322score_select_cc_mode (enum rtx_code op, rtx x, rtx y) 1323{ 1324 if ((op == EQ || op == NE || op == LT || op == GE) 1325 && y == const0_rtx 1326 && GET_MODE (x) == SImode) 1327 { 1328 switch (GET_CODE (x)) 1329 { 1330 case PLUS: 1331 case MINUS: 1332 case NEG: 1333 case AND: 1334 case IOR: 1335 case XOR: 1336 case NOT: 1337 case ASHIFT: 1338 case LSHIFTRT: 1339 case ASHIFTRT: 1340 return CC_NZmode; 1341 1342 case SIGN_EXTEND: 1343 case ZERO_EXTEND: 1344 case ROTATE: 1345 case ROTATERT: 1346 return (op == LT || op == GE) ? CC_Nmode : CCmode; 1347 1348 default: 1349 return CCmode; 1350 } 1351 } 1352 1353 if ((op == EQ || op == NE) 1354 && (GET_CODE (y) == NEG) 1355 && register_operand (XEXP (y, 0), SImode) 1356 && register_operand (x, SImode)) 1357 { 1358 return CC_NZmode; 1359 } 1360 1361 return CCmode; 1362} 1363 1364struct gcc_target targetm = TARGET_INITIALIZER; 1365