1/* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU) 2 3 Copyright (C) 2006-2022 Free Software Foundation, Inc. 4 5 This file is part of GAS, the GNU Assembler. 6 7 GAS 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 3, or (at your option) 10 any later version. 11 12 GAS 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 GAS; see the file COPYING. If not, write to the Free 19 Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 20 02110-1301, USA. */ 21 22#include "as.h" 23#include "safe-ctype.h" 24#include "subsegs.h" 25#include "dwarf2dbg.h" 26 27const struct spu_opcode spu_opcodes[] = { 28#define APUOP(TAG,MACFORMAT,OPCODE,MNEMONIC,ASMFORMAT,DEP,PIPE) \ 29 { MACFORMAT, (OPCODE ## u) << (32-11), MNEMONIC, ASMFORMAT }, 30#define APUOPFB(TAG,MACFORMAT,OPCODE,FB,MNEMONIC,ASMFORMAT,DEP,PIPE) \ 31 { MACFORMAT, ((OPCODE) << (32-11)) | ((FB) << (32-18)), MNEMONIC, ASMFORMAT }, 32#include "opcode/spu-insns.h" 33#undef APUOP 34#undef APUOPFB 35}; 36 37static const int spu_num_opcodes = 38 sizeof (spu_opcodes) / sizeof (spu_opcodes[0]); 39 40#define MAX_RELOCS 2 41 42struct spu_insn 43{ 44 unsigned int opcode; 45 expressionS exp[MAX_RELOCS]; 46 int reloc_arg[MAX_RELOCS]; 47 bfd_reloc_code_real_type reloc[MAX_RELOCS]; 48 enum spu_insns tag; 49}; 50 51static const char *get_imm (const char *param, struct spu_insn *insn, int arg); 52static const char *get_reg (const char *param, struct spu_insn *insn, int arg, 53 int accept_expr); 54static int calcop (struct spu_opcode *format, const char *param, 55 struct spu_insn *insn); 56static void spu_brinfo (int); 57static void spu_cons (int); 58 59extern char *myname; 60static htab_t op_hash = NULL; 61 62/* These bits should be turned off in the first address of every segment */ 63int md_seg_align = 7; 64 65/* These chars start a comment anywhere in a source file (except inside 66 another comment */ 67const char comment_chars[] = "#"; 68 69/* These chars only start a comment at the beginning of a line. */ 70const char line_comment_chars[] = "#"; 71 72/* gods own line continuation char */ 73const char line_separator_chars[] = ";"; 74 75/* Chars that can be used to separate mant from exp in floating point nums */ 76const char EXP_CHARS[] = "eE"; 77 78/* Chars that mean this number is a floating point constant */ 79/* as in 0f123.456 */ 80/* or 0H1.234E-12 (see exp chars above) */ 81const char FLT_CHARS[] = "dDfF"; 82 83const pseudo_typeS md_pseudo_table[] = 84{ 85 {"align", s_align_ptwo, 4}, 86 {"brinfo", spu_brinfo, 0}, 87 {"bss", s_lcomm_bytes, 1}, 88 {"def", s_set, 0}, 89 {"dfloat", float_cons, 'd'}, 90 {"ffloat", float_cons, 'f'}, 91 {"global", s_globl, 0}, 92 {"half", cons, 2}, 93 {"int", spu_cons, 4}, 94 {"long", spu_cons, 4}, 95 {"quad", spu_cons, 8}, 96 {"string", stringer, 8 + 1}, 97 {"word", spu_cons, 4}, 98 /* Force set to be treated as an instruction. */ 99 {"set", NULL, 0}, 100 {".set", s_set, 0}, 101 /* Likewise for eqv. */ 102 {"eqv", NULL, 0}, 103 {".eqv", s_set, -1}, 104 {0,0,0} 105}; 106 107/* Bits plugged into branch instruction offset field. */ 108unsigned int brinfo; 109 110void 111md_begin (void) 112{ 113 int i; 114 115 op_hash = str_htab_create (); 116 117 /* Hash each mnemonic and record its position. There are 118 duplicates, keep just the first. */ 119 for (i = 0; i < spu_num_opcodes; i++) 120 str_hash_insert (op_hash, spu_opcodes[i].mnemonic, &spu_opcodes[i], 0); 121} 122 123const char *md_shortopts = ""; 124struct option md_longopts[] = { 125#define OPTION_APUASM (OPTION_MD_BASE) 126 {"apuasm", no_argument, NULL, OPTION_APUASM}, 127#define OPTION_DD2 (OPTION_MD_BASE+1) 128 {"mdd2.0", no_argument, NULL, OPTION_DD2}, 129#define OPTION_DD1 (OPTION_MD_BASE+2) 130 {"mdd1.0", no_argument, NULL, OPTION_DD1}, 131#define OPTION_DD3 (OPTION_MD_BASE+3) 132 {"mdd3.0", no_argument, NULL, OPTION_DD3}, 133 { NULL, no_argument, NULL, 0 } 134}; 135size_t md_longopts_size = sizeof (md_longopts); 136 137/* When set (by -apuasm) our assembler emulates the behaviour of apuasm. 138 * e.g. don't add bias to float conversion and don't right shift 139 * immediate values. */ 140static int emulate_apuasm; 141 142/* Use the dd2.0 instructions set. The only differences are some new 143 * register names and the orx insn */ 144static int use_dd2 = 1; 145 146int 147md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED) 148{ 149 switch (c) 150 { 151 case OPTION_APUASM: 152 emulate_apuasm = 1; 153 break; 154 case OPTION_DD3: 155 use_dd2 = 1; 156 break; 157 case OPTION_DD2: 158 use_dd2 = 1; 159 break; 160 case OPTION_DD1: 161 use_dd2 = 0; 162 break; 163 default: 164 return 0; 165 } 166 return 1; 167} 168 169void 170md_show_usage (FILE *stream) 171{ 172 fputs (_("\ 173SPU options:\n\ 174 --apuasm emulate behaviour of apuasm\n"), 175 stream); 176} 177 178 179struct arg_encode { 180 int size; 181 int pos; 182 int rshift; 183 int lo, hi; 184 int wlo, whi; 185 bfd_reloc_code_real_type reloc; 186}; 187 188static struct arg_encode arg_encode[A_MAX] = { 189 { 7, 0, 0, 0, 127, 0, -1, 0 }, /* A_T */ 190 { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_A */ 191 { 7, 14, 0, 0, 127, 0, -1, 0 }, /* A_B */ 192 { 7, 21, 0, 0, 127, 0, -1, 0 }, /* A_C */ 193 { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_S */ 194 { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_H */ 195 { 0, 0, 0, 0, -1, 0, -1, 0 }, /* A_P */ 196 { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_S3 */ 197 { 7, 14, 0, -32, 31, -31, 0, BFD_RELOC_SPU_IMM7 }, /* A_S6 */ 198 { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_S7N */ 199 { 7, 14, 0, -64, 63, -63, 0, BFD_RELOC_SPU_IMM7 }, /* A_S7 */ 200 { 8, 14, 0, 0, 127, 0, -1, BFD_RELOC_SPU_IMM8 }, /* A_U7A */ 201 { 8, 14, 0, 0, 127, 0, -1, BFD_RELOC_SPU_IMM8 }, /* A_U7B */ 202 { 10, 14, 0, -512, 511, -128, 255, BFD_RELOC_SPU_IMM10 }, /* A_S10B */ 203 { 10, 14, 0, -512, 511, 0, -1, BFD_RELOC_SPU_IMM10 }, /* A_S10 */ 204 { 2, 23, 9, -1024, 1023, 0, -1, BFD_RELOC_SPU_PCREL9a }, /* A_S11 */ 205 { 2, 14, 9, -1024, 1023, 0, -1, BFD_RELOC_SPU_PCREL9b }, /* A_S11I */ 206 { 10, 14, 4, -8192, 8191, 0, -1, BFD_RELOC_SPU_IMM10W }, /* A_S14 */ 207 { 16, 7, 0, -32768, 32767, 0, -1, BFD_RELOC_SPU_IMM16 }, /* A_S16 */ 208 { 16, 7, 2, -131072, 262143, 0, -1, BFD_RELOC_SPU_IMM16W }, /* A_S18 */ 209 { 16, 7, 2, -262144, 262143, 0, -1, BFD_RELOC_SPU_PCREL16 }, /* A_R18 */ 210 { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_U3 */ 211 { 7, 14, 0, 0, 127, 0, 31, BFD_RELOC_SPU_IMM7 }, /* A_U5 */ 212 { 7, 14, 0, 0, 127, 0, 63, BFD_RELOC_SPU_IMM7 }, /* A_U6 */ 213 { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_U7 */ 214 { 14, 0, 0, 0, 16383, 0, -1, 0 }, /* A_U14 */ 215 { 16, 7, 0, -32768, 65535, 0, -1, BFD_RELOC_SPU_IMM16 }, /* A_X16 */ 216 { 18, 7, 0, 0, 262143, 0, -1, BFD_RELOC_SPU_IMM18 }, /* A_U18 */ 217}; 218 219/* Some flags for handling errors. This is very hackish and added after 220 * the fact. */ 221static int syntax_error_arg; 222static const char *syntax_error_param; 223static int syntax_reg; 224 225static char * 226insn_fmt_string (struct spu_opcode *format) 227{ 228 static char buf[64]; 229 int len = 0; 230 int i; 231 232 len += sprintf (&buf[len], "%s\t", format->mnemonic); 233 for (i = 1; i <= format->arg[0]; i++) 234 { 235 int arg = format->arg[i]; 236 const char *exp; 237 if (i > 1 && arg != A_P && format->arg[i-1] != A_P) 238 buf[len++] = ','; 239 if (arg == A_P) 240 exp = "("; 241 else if (arg < A_P) 242 exp = i == syntax_error_arg ? "REG" : "reg"; 243 else 244 exp = i == syntax_error_arg ? "IMM" : "imm"; 245 len += sprintf (&buf[len], "%s", exp); 246 if (i > 1 && format->arg[i-1] == A_P) 247 buf[len++] = ')'; 248 } 249 buf[len] = 0; 250 return buf; 251} 252 253void 254md_assemble (char *op) 255{ 256 char *param, *thisfrag; 257 char c; 258 struct spu_opcode *format; 259 struct spu_insn insn; 260 int i; 261 262 gas_assert (op); 263 264 /* skip over instruction to find parameters */ 265 266 for (param = op; *param != 0 && !ISSPACE (*param); param++) 267 ; 268 c = *param; 269 *param = 0; 270 271 if (c != 0 && c != '\n') 272 param++; 273 274 /* try to find the instruction in the hash table */ 275 276 if ((format = (struct spu_opcode *) str_hash_find (op_hash, op)) == NULL) 277 { 278 as_bad (_("Invalid mnemonic '%s'"), op); 279 return; 280 } 281 282 if (!use_dd2 && strcmp (format->mnemonic, "orx") == 0) 283 { 284 as_bad (_("'%s' is only available in DD2.0 or higher."), op); 285 return; 286 } 287 288 while (1) 289 { 290 /* try parsing this instruction into insn */ 291 for (i = 0; i < MAX_RELOCS; i++) 292 { 293 insn.exp[i].X_add_symbol = 0; 294 insn.exp[i].X_op_symbol = 0; 295 insn.exp[i].X_add_number = 0; 296 insn.exp[i].X_op = O_illegal; 297 insn.reloc_arg[i] = -1; 298 insn.reloc[i] = BFD_RELOC_NONE; 299 } 300 insn.opcode = format->opcode; 301 insn.tag = (enum spu_insns) (format - spu_opcodes); 302 303 syntax_error_arg = 0; 304 syntax_error_param = 0; 305 syntax_reg = 0; 306 if (calcop (format, param, &insn)) 307 break; 308 309 /* if it doesn't parse try the next instruction */ 310 if (!strcmp (format[0].mnemonic, format[1].mnemonic)) 311 format++; 312 else 313 { 314 int parg = format[0].arg[syntax_error_arg-1]; 315 316 as_fatal (_("Error in argument %d. Expecting: \"%s\""), 317 syntax_error_arg - (parg == A_P), 318 insn_fmt_string (format)); 319 return; 320 } 321 } 322 323 if ((syntax_reg & 4) 324 && ! (insn.tag == M_RDCH 325 || insn.tag == M_RCHCNT 326 || insn.tag == M_WRCH)) 327 as_warn (_("Mixing register syntax, with and without '$'.")); 328 if (syntax_error_param) 329 { 330 const char *d = syntax_error_param; 331 while (*d != '$') 332 d--; 333 as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d); 334 } 335 336 if (brinfo != 0 337 && (insn.tag <= M_BRASL 338 || (insn.tag >= M_BRZ && insn.tag <= M_BRHNZ)) 339 && (insn.opcode & 0x7ff80) == 0 340 && (insn.reloc_arg[0] == A_R18 341 || insn.reloc_arg[0] == A_S18 342 || insn.reloc_arg[1] == A_R18 343 || insn.reloc_arg[1] == A_S18)) 344 insn.opcode |= brinfo << 7; 345 346 /* grow the current frag and plop in the opcode */ 347 348 thisfrag = frag_more (4); 349 md_number_to_chars (thisfrag, insn.opcode, 4); 350 351 /* if this instruction requires labels mark it for later */ 352 353 for (i = 0; i < MAX_RELOCS; i++) 354 if (insn.reloc_arg[i] >= 0) 355 { 356 fixS *fixP; 357 bfd_reloc_code_real_type reloc = insn.reloc[i]; 358 int pcrel = 0; 359 360 if (reloc == BFD_RELOC_SPU_PCREL9a 361 || reloc == BFD_RELOC_SPU_PCREL9b 362 || reloc == BFD_RELOC_SPU_PCREL16) 363 pcrel = 1; 364 fixP = fix_new_exp (frag_now, 365 thisfrag - frag_now->fr_literal, 366 4, 367 &insn.exp[i], 368 pcrel, 369 reloc); 370 fixP->tc_fix_data.arg_format = insn.reloc_arg[i]; 371 fixP->tc_fix_data.insn_tag = insn.tag; 372 } 373 dwarf2_emit_insn (4); 374 375 /* .brinfo lasts exactly one instruction. */ 376 brinfo = 0; 377} 378 379static int 380calcop (struct spu_opcode *format, const char *param, struct spu_insn *insn) 381{ 382 int i; 383 int paren = 0; 384 int arg; 385 386 for (i = 1; i <= format->arg[0]; i++) 387 { 388 arg = format->arg[i]; 389 syntax_error_arg = i; 390 391 while (ISSPACE (*param)) 392 param++; 393 if (*param == 0 || *param == ',') 394 return 0; 395 if (arg < A_P) 396 param = get_reg (param, insn, arg, 1); 397 else if (arg > A_P) 398 param = get_imm (param, insn, arg); 399 else if (arg == A_P) 400 { 401 paren++; 402 if ('(' != *param++) 403 return 0; 404 } 405 406 if (!param) 407 return 0; 408 409 while (ISSPACE (*param)) 410 param++; 411 412 if (arg != A_P && paren) 413 { 414 paren--; 415 if (')' != *param++) 416 return 0; 417 } 418 else if (i < format->arg[0] 419 && format->arg[i] != A_P 420 && format->arg[i+1] != A_P) 421 { 422 if (',' != *param++) 423 { 424 syntax_error_arg++; 425 return 0; 426 } 427 } 428 } 429 while (ISSPACE (*param)) 430 param++; 431 return !paren && (*param == 0 || *param == '\n'); 432} 433 434struct reg_name { 435 unsigned int regno; 436 unsigned int length; 437 char name[32]; 438}; 439 440#define REG_NAME(NO,NM) { NO, sizeof (NM) - 1, NM } 441 442static struct reg_name reg_name[] = { 443 REG_NAME (0, "lr"), /* link register */ 444 REG_NAME (1, "sp"), /* stack pointer */ 445 REG_NAME (0, "rp"), /* link register */ 446 REG_NAME (127, "fp"), /* frame pointer */ 447}; 448 449static struct reg_name sp_reg_name[] = { 450}; 451 452static struct reg_name ch_reg_name[] = { 453 REG_NAME ( 0, "SPU_RdEventStat"), 454 REG_NAME ( 1, "SPU_WrEventMask"), 455 REG_NAME ( 2, "SPU_WrEventAck"), 456 REG_NAME ( 3, "SPU_RdSigNotify1"), 457 REG_NAME ( 4, "SPU_RdSigNotify2"), 458 REG_NAME ( 7, "SPU_WrDec"), 459 REG_NAME ( 8, "SPU_RdDec"), 460 REG_NAME ( 11, "SPU_RdEventMask"), /* DD2.0 only */ 461 REG_NAME ( 13, "SPU_RdMachStat"), 462 REG_NAME ( 14, "SPU_WrSRR0"), 463 REG_NAME ( 15, "SPU_RdSRR0"), 464 REG_NAME ( 28, "SPU_WrOutMbox"), 465 REG_NAME ( 29, "SPU_RdInMbox"), 466 REG_NAME ( 30, "SPU_WrOutIntrMbox"), 467 REG_NAME ( 9, "MFC_WrMSSyncReq"), 468 REG_NAME ( 12, "MFC_RdTagMask"), /* DD2.0 only */ 469 REG_NAME ( 16, "MFC_LSA"), 470 REG_NAME ( 17, "MFC_EAH"), 471 REG_NAME ( 18, "MFC_EAL"), 472 REG_NAME ( 19, "MFC_Size"), 473 REG_NAME ( 20, "MFC_TagID"), 474 REG_NAME ( 21, "MFC_Cmd"), 475 REG_NAME ( 22, "MFC_WrTagMask"), 476 REG_NAME ( 23, "MFC_WrTagUpdate"), 477 REG_NAME ( 24, "MFC_RdTagStat"), 478 REG_NAME ( 25, "MFC_RdListStallStat"), 479 REG_NAME ( 26, "MFC_WrListStallAck"), 480 REG_NAME ( 27, "MFC_RdAtomicStat"), 481}; 482#undef REG_NAME 483 484static const char * 485get_reg (const char *param, struct spu_insn *insn, int arg, int accept_expr) 486{ 487 unsigned regno; 488 int saw_prefix = 0; 489 490 if (*param == '$') 491 { 492 saw_prefix = 1; 493 param++; 494 } 495 496 if (arg == A_H) /* Channel */ 497 { 498 if ((param[0] == 'c' || param[0] == 'C') 499 && (param[1] == 'h' || param[1] == 'H') 500 && ISDIGIT (param[2])) 501 param += 2; 502 } 503 else if (arg == A_S) /* Special purpose register */ 504 { 505 if ((param[0] == 's' || param[0] == 'S') 506 && (param[1] == 'p' || param[1] == 'P') 507 && ISDIGIT (param[2])) 508 param += 2; 509 } 510 511 if (ISDIGIT (*param)) 512 { 513 regno = 0; 514 while (ISDIGIT (*param)) 515 regno = regno * 10 + *param++ - '0'; 516 } 517 else 518 { 519 struct reg_name *rn; 520 unsigned int i, n, l = 0; 521 522 if (arg == A_H) /* Channel */ 523 { 524 rn = ch_reg_name; 525 n = sizeof (ch_reg_name) / sizeof (*ch_reg_name); 526 } 527 else if (arg == A_S) /* Special purpose register */ 528 { 529 rn = sp_reg_name; 530 n = sizeof (sp_reg_name) / sizeof (*sp_reg_name); 531 } 532 else 533 { 534 rn = reg_name; 535 n = sizeof (reg_name) / sizeof (*reg_name); 536 } 537 regno = 128; 538 for (i = 0; i < n; i++) 539 if (rn[i].length > l 540 && 0 == strncasecmp (param, rn[i].name, rn[i].length)) 541 { 542 l = rn[i].length; 543 regno = rn[i].regno; 544 } 545 param += l; 546 } 547 548 if (!use_dd2 549 && arg == A_H) 550 { 551 if (regno == 11) 552 as_bad (_("'SPU_RdEventMask' (channel 11) is only available in DD2.0 or higher.")); 553 else if (regno == 12) 554 as_bad (_("'MFC_RdTagMask' (channel 12) is only available in DD2.0 or higher.")); 555 } 556 557 if (regno < 128) 558 { 559 insn->opcode |= regno << arg_encode[arg].pos; 560 if ((!saw_prefix && syntax_reg == 1) 561 || (saw_prefix && syntax_reg == 2)) 562 syntax_reg |= 4; 563 syntax_reg |= saw_prefix ? 1 : 2; 564 return param; 565 } 566 567 if (accept_expr) 568 { 569 char *save_ptr; 570 expressionS ex; 571 save_ptr = input_line_pointer; 572 input_line_pointer = (char *)param; 573 expression (&ex); 574 param = input_line_pointer; 575 input_line_pointer = save_ptr; 576 if (ex.X_op == O_register || ex.X_op == O_constant) 577 { 578 insn->opcode |= ex.X_add_number << arg_encode[arg].pos; 579 return param; 580 } 581 } 582 return 0; 583} 584 585static const char * 586get_imm (const char *param, struct spu_insn *insn, int arg) 587{ 588 int val; 589 char *save_ptr; 590 int low = 0, high = 0; 591 int reloc_i = insn->reloc_arg[0] >= 0 ? 1 : 0; 592 593 if (strncasecmp (param, "%lo(", 4) == 0) 594 { 595 param += 3; 596 low = 1; 597 as_warn (_("Using old style, %%lo(expr), please change to PPC style, expr@l.")); 598 } 599 else if (strncasecmp (param, "%hi(", 4) == 0) 600 { 601 param += 3; 602 high = 1; 603 as_warn (_("Using old style, %%hi(expr), please change to PPC style, expr@h.")); 604 } 605 else if (strncasecmp (param, "%pic(", 5) == 0) 606 { 607 /* Currently we expect %pic(expr) == expr, so do nothing here. 608 i.e. for code loaded at address 0 $toc will be 0. */ 609 param += 4; 610 } 611 612 if (*param == '$') 613 { 614 /* Symbols can start with $, but if this symbol matches a register 615 name, it's probably a mistake. The only way to avoid this 616 warning is to rename the symbol. */ 617 struct spu_insn tmp_insn; 618 const char *np = get_reg (param, &tmp_insn, arg, 0); 619 620 if (np) 621 syntax_error_param = np; 622 } 623 624 save_ptr = input_line_pointer; 625 input_line_pointer = (char *) param; 626 expression (&insn->exp[reloc_i]); 627 param = input_line_pointer; 628 input_line_pointer = save_ptr; 629 630 /* Similar to ppc_elf_suffix in tc-ppc.c. We have so few cases to 631 handle we do it inlined here. */ 632 if (param[0] == '@' && !ISALNUM (param[2]) && param[2] != '@') 633 { 634 if (param[1] == 'h' || param[1] == 'H') 635 { 636 high = 1; 637 param += 2; 638 } 639 else if (param[1] == 'l' || param[1] == 'L') 640 { 641 low = 1; 642 param += 2; 643 } 644 } 645 646 if (insn->exp[reloc_i].X_op == O_constant) 647 { 648 val = insn->exp[reloc_i].X_add_number; 649 650 if (emulate_apuasm) 651 { 652 /* Convert the value to a format we expect. */ 653 val <<= arg_encode[arg].rshift; 654 if (arg == A_U7A) 655 val = 173 - val; 656 else if (arg == A_U7B) 657 val = 155 - val; 658 } 659 660 if (high) 661 val = val >> 16; 662 else if (low) 663 val = val & 0xffff; 664 665 /* Warn about out of range expressions. */ 666 { 667 int hi = arg_encode[arg].hi; 668 int lo = arg_encode[arg].lo; 669 int whi = arg_encode[arg].whi; 670 int wlo = arg_encode[arg].wlo; 671 672 if (hi > lo && (val < lo || val > hi)) 673 as_fatal (_("Constant expression %d out of range, [%d, %d]."), 674 val, lo, hi); 675 else if (whi > wlo && (val < wlo || val > whi)) 676 as_warn (_("Constant expression %d out of range, [%d, %d]."), 677 val, wlo, whi); 678 } 679 680 if (arg == A_U7A) 681 val = 173 - val; 682 else if (arg == A_U7B) 683 val = 155 - val; 684 685 /* Branch hints have a split encoding. Do the bottom part. */ 686 if (arg == A_S11 || arg == A_S11I) 687 insn->opcode |= ((val >> 2) & 0x7f); 688 689 insn->opcode |= (((val >> arg_encode[arg].rshift) 690 & ((1 << arg_encode[arg].size) - 1)) 691 << arg_encode[arg].pos); 692 } 693 else 694 { 695 insn->reloc_arg[reloc_i] = arg; 696 if (high) 697 insn->reloc[reloc_i] = BFD_RELOC_SPU_HI16; 698 else if (low) 699 insn->reloc[reloc_i] = BFD_RELOC_SPU_LO16; 700 else 701 insn->reloc[reloc_i] = arg_encode[arg].reloc; 702 } 703 704 return param; 705} 706 707const char * 708md_atof (int type, char *litP, int *sizeP) 709{ 710 return ieee_md_atof (type, litP, sizeP, true); 711} 712 713#ifndef WORKING_DOT_WORD 714int md_short_jump_size = 4; 715 716void 717md_create_short_jump (char *ptr, 718 addressT from_addr ATTRIBUTE_UNUSED, 719 addressT to_addr ATTRIBUTE_UNUSED, 720 fragS *frag, 721 symbolS *to_symbol) 722{ 723 ptr[0] = (char) 0xc0; 724 ptr[1] = 0x00; 725 ptr[2] = 0x00; 726 ptr[3] = 0x00; 727 fix_new (frag, 728 ptr - frag->fr_literal, 729 4, 730 to_symbol, 731 (offsetT) 0, 732 0, 733 BFD_RELOC_SPU_PCREL16); 734} 735 736int md_long_jump_size = 4; 737 738void 739md_create_long_jump (char *ptr, 740 addressT from_addr ATTRIBUTE_UNUSED, 741 addressT to_addr ATTRIBUTE_UNUSED, 742 fragS *frag, 743 symbolS *to_symbol) 744{ 745 ptr[0] = (char) 0xc0; 746 ptr[1] = 0x00; 747 ptr[2] = 0x00; 748 ptr[3] = 0x00; 749 fix_new (frag, 750 ptr - frag->fr_literal, 751 4, 752 to_symbol, 753 (offsetT) 0, 754 0, 755 BFD_RELOC_SPU_PCREL16); 756} 757#endif 758 759/* Handle .brinfo <priority>,<lrlive>. */ 760static void 761spu_brinfo (int ignore ATTRIBUTE_UNUSED) 762{ 763 addressT priority; 764 addressT lrlive; 765 766 priority = get_absolute_expression (); 767 SKIP_WHITESPACE (); 768 769 lrlive = 0; 770 if (*input_line_pointer == ',') 771 { 772 ++input_line_pointer; 773 lrlive = get_absolute_expression (); 774 } 775 776 if (priority > 0x1fff) 777 { 778 as_bad (_("invalid priority '%lu'"), (unsigned long) priority); 779 priority = 0; 780 } 781 782 if (lrlive > 7) 783 { 784 as_bad (_("invalid lrlive '%lu'"), (unsigned long) lrlive); 785 lrlive = 0; 786 } 787 788 brinfo = (lrlive << 13) | priority; 789 demand_empty_rest_of_line (); 790} 791 792/* Support @ppu on symbols referenced in .int/.long/.word/.quad. */ 793static void 794spu_cons (int nbytes) 795{ 796 expressionS exp; 797 798 if (is_it_end_of_statement ()) 799 { 800 demand_empty_rest_of_line (); 801 return; 802 } 803 804 do 805 { 806 char *save = input_line_pointer; 807 808 /* Use deferred_expression here so that an expression involving 809 a symbol that happens to be defined already as an spu symbol, 810 is not resolved. */ 811 deferred_expression (&exp); 812 if ((exp.X_op == O_symbol 813 || exp.X_op == O_constant) 814 && strncasecmp (input_line_pointer, "@ppu", 4) == 0) 815 { 816 char *p = frag_more (nbytes); 817 enum bfd_reloc_code_real reloc; 818 819 /* Check for identifier@suffix+constant. */ 820 input_line_pointer += 4; 821 if (*input_line_pointer == '-' || *input_line_pointer == '+') 822 { 823 expressionS new_exp; 824 825 save = input_line_pointer; 826 expression (&new_exp); 827 if (new_exp.X_op == O_constant) 828 exp.X_add_number += new_exp.X_add_number; 829 else 830 input_line_pointer = save; 831 } 832 833 reloc = nbytes == 4 ? BFD_RELOC_SPU_PPU32 : BFD_RELOC_SPU_PPU64; 834 fix_new_exp (frag_now, p - frag_now->fr_literal, nbytes, 835 &exp, 0, reloc); 836 } 837 else 838 { 839 /* Don't use deferred_expression for anything else. 840 deferred_expression won't evaulate dot at the point it is 841 used. */ 842 input_line_pointer = save; 843 expression (&exp); 844 emit_expr (&exp, nbytes); 845 } 846 } 847 while (*input_line_pointer++ == ','); 848 849 /* Put terminator back into stream. */ 850 input_line_pointer--; 851 demand_empty_rest_of_line (); 852} 853 854int 855md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, 856 segT segment_type ATTRIBUTE_UNUSED) 857{ 858 as_fatal (_("Relaxation should never occur")); 859 return -1; 860} 861 862/* If while processing a fixup, a reloc really needs to be created, 863 then it is done here. */ 864 865arelent * 866tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp) 867{ 868 arelent *reloc; 869 reloc = XNEW (arelent); 870 reloc->sym_ptr_ptr = XNEW (asymbol *); 871 *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); 872 reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; 873 reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); 874 if (reloc->howto == (reloc_howto_type *) NULL) 875 { 876 as_bad_where (fixp->fx_file, fixp->fx_line, 877 _("reloc %d not supported by object file format"), 878 (int) fixp->fx_r_type); 879 free (reloc->sym_ptr_ptr); 880 free (reloc); 881 return NULL; 882 } 883 reloc->addend = fixp->fx_addnumber; 884 return reloc; 885} 886 887/* Round up a section's size to the appropriate boundary. */ 888 889valueT 890md_section_align (segT seg, valueT size) 891{ 892 int align = bfd_section_alignment (seg); 893 valueT mask = ((valueT) 1 << align) - 1; 894 895 return (size + mask) & ~mask; 896} 897 898/* Where a PC relative offset is calculated from. On the spu they 899 are calculated from the beginning of the branch instruction. */ 900 901long 902md_pcrel_from (fixS *fixp) 903{ 904 return fixp->fx_frag->fr_address + fixp->fx_where; 905} 906 907/* Fill in rs_align_code fragments. */ 908 909void 910spu_handle_align (fragS *fragp) 911{ 912 static const unsigned char nop_pattern[8] = { 913 0x40, 0x20, 0x00, 0x00, /* even nop */ 914 0x00, 0x20, 0x00, 0x00, /* odd nop */ 915 }; 916 917 int bytes; 918 char *p; 919 920 if (fragp->fr_type != rs_align_code) 921 return; 922 923 bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; 924 p = fragp->fr_literal + fragp->fr_fix; 925 926 if (bytes & 3) 927 { 928 int fix = bytes & 3; 929 memset (p, 0, fix); 930 p += fix; 931 bytes -= fix; 932 fragp->fr_fix += fix; 933 } 934 if (bytes & 4) 935 { 936 memcpy (p, &nop_pattern[4], 4); 937 p += 4; 938 bytes -= 4; 939 fragp->fr_fix += 4; 940 } 941 942 memcpy (p, nop_pattern, 8); 943 fragp->fr_var = 8; 944} 945 946void 947md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) 948{ 949 unsigned int res; 950 unsigned int mask; 951 valueT val = *valP; 952 char *place = fixP->fx_where + fixP->fx_frag->fr_literal; 953 954 if (fixP->fx_subsy != (symbolS *) NULL) 955 { 956 /* We can't actually support subtracting a symbol. */ 957 as_bad_subtract (fixP); 958 } 959 960 if (fixP->fx_addsy != NULL) 961 { 962 if (fixP->fx_pcrel) 963 { 964 /* Hack around bfd_install_relocation brain damage. */ 965 val += fixP->fx_frag->fr_address + fixP->fx_where; 966 967 switch (fixP->fx_r_type) 968 { 969 case BFD_RELOC_32: 970 fixP->fx_r_type = BFD_RELOC_32_PCREL; 971 break; 972 973 case BFD_RELOC_SPU_PCREL16: 974 case BFD_RELOC_SPU_PCREL9a: 975 case BFD_RELOC_SPU_PCREL9b: 976 case BFD_RELOC_32_PCREL: 977 break; 978 979 default: 980 as_bad_where (fixP->fx_file, fixP->fx_line, 981 _("expression too complex")); 982 break; 983 } 984 } 985 } 986 987 fixP->fx_addnumber = val; 988 989 if (fixP->fx_r_type == BFD_RELOC_SPU_PPU32 990 || fixP->fx_r_type == BFD_RELOC_SPU_PPU64 991 || fixP->fx_r_type == BFD_RELOC_SPU_ADD_PIC) 992 return; 993 994 if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) 995 { 996 fixP->fx_done = 1; 997 res = 0; 998 mask = 0; 999 if (fixP->tc_fix_data.arg_format > A_P) 1000 { 1001 int hi = arg_encode[fixP->tc_fix_data.arg_format].hi; 1002 int lo = arg_encode[fixP->tc_fix_data.arg_format].lo; 1003 if (hi > lo && ((offsetT) val < lo || (offsetT) val > hi)) 1004 as_bad_where (fixP->fx_file, fixP->fx_line, 1005 _("Relocation doesn't fit. (relocation value = 0x%lx)"), 1006 (long) val); 1007 } 1008 1009 switch (fixP->fx_r_type) 1010 { 1011 case BFD_RELOC_8: 1012 md_number_to_chars (place, val, 1); 1013 return; 1014 1015 case BFD_RELOC_16: 1016 md_number_to_chars (place, val, 2); 1017 return; 1018 1019 case BFD_RELOC_32: 1020 case BFD_RELOC_32_PCREL: 1021 md_number_to_chars (place, val, 4); 1022 return; 1023 1024 case BFD_RELOC_64: 1025 md_number_to_chars (place, val, 8); 1026 return; 1027 1028 case BFD_RELOC_SPU_IMM7: 1029 res = val << 14; 1030 mask = 0x7f << 14; 1031 break; 1032 1033 case BFD_RELOC_SPU_IMM8: 1034 res = val << 14; 1035 mask = 0xff << 14; 1036 break; 1037 1038 case BFD_RELOC_SPU_IMM10: 1039 res = val << 14; 1040 mask = 0x3ff << 14; 1041 break; 1042 1043 case BFD_RELOC_SPU_IMM10W: 1044 res = val << 10; 1045 mask = 0x3ff0 << 10; 1046 break; 1047 1048 case BFD_RELOC_SPU_IMM16: 1049 res = val << 7; 1050 mask = 0xffff << 7; 1051 break; 1052 1053 case BFD_RELOC_SPU_IMM16W: 1054 res = val << 5; 1055 mask = 0x3fffc << 5; 1056 break; 1057 1058 case BFD_RELOC_SPU_IMM18: 1059 res = val << 7; 1060 mask = 0x3ffff << 7; 1061 break; 1062 1063 case BFD_RELOC_SPU_PCREL9a: 1064 res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14); 1065 mask = (0x1fc >> 2) | (0x600 << 14); 1066 break; 1067 1068 case BFD_RELOC_SPU_PCREL9b: 1069 res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5); 1070 mask = (0x1fc >> 2) | (0x600 << 5); 1071 break; 1072 1073 case BFD_RELOC_SPU_PCREL16: 1074 res = val << 5; 1075 mask = 0x3fffc << 5; 1076 break; 1077 1078 case BFD_RELOC_SPU_HI16: 1079 res = val >> 9; 1080 mask = 0xffff << 7; 1081 break; 1082 1083 case BFD_RELOC_SPU_LO16: 1084 res = val << 7; 1085 mask = 0xffff << 7; 1086 break; 1087 1088 default: 1089 as_bad_where (fixP->fx_file, fixP->fx_line, 1090 _("reloc %d not supported by object file format"), 1091 (int) fixP->fx_r_type); 1092 } 1093 1094 res &= mask; 1095 place[0] = (place[0] & (~mask >> 24)) | ((res >> 24) & 0xff); 1096 place[1] = (place[1] & (~mask >> 16)) | ((res >> 16) & 0xff); 1097 place[2] = (place[2] & (~mask >> 8)) | ((res >> 8) & 0xff); 1098 place[3] = (place[3] & ~mask) | (res & 0xff); 1099 } 1100} 1101