]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gas/config/tc-spu.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / config / tc-spu.c
CommitLineData
e9f53129
AM
1/* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU)
2
d87bef3a 3 Copyright (C) 2006-2023 Free Software Foundation, Inc.
e9f53129
AM
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
ec2655a6 9 the Free Software Foundation; either version 3, or (at your option)
e9f53129
AM
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"
3739860c 25#include "dwarf2dbg.h"
e9f53129
AM
26
27const struct spu_opcode spu_opcodes[] = {
28#define APUOP(TAG,MACFORMAT,OPCODE,MNEMONIC,ASMFORMAT,DEP,PIPE) \
2900e701 29 { MACFORMAT, (OPCODE ## u) << (32-11), MNEMONIC, ASMFORMAT },
e9f53129
AM
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];
7bc3e93c 47 bfd_reloc_code_real_type reloc[MAX_RELOCS];
e9f53129
AM
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);
e9f53129
AM
54static int calcop (struct spu_opcode *format, const char *param,
55 struct spu_insn *insn);
cd4a7468 56static void spu_brinfo (int);
ece5ef60 57static void spu_cons (int);
e9f53129
AM
58
59extern char *myname;
629310ab 60static htab_t op_hash = NULL;
e9f53129
AM
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},
cd4a7468 86 {"brinfo", spu_brinfo, 0},
ece5ef60 87 {"bss", s_lcomm_bytes, 1},
e9f53129
AM
88 {"def", s_set, 0},
89 {"dfloat", float_cons, 'd'},
90 {"ffloat", float_cons, 'f'},
91 {"global", s_globl, 0},
92 {"half", cons, 2},
ece5ef60
AM
93 {"int", spu_cons, 4},
94 {"long", spu_cons, 4},
95 {"quad", spu_cons, 8},
38a57ae7 96 {"string", stringer, 8 + 1},
ece5ef60 97 {"word", spu_cons, 4},
e9f53129
AM
98 /* Force set to be treated as an instruction. */
99 {"set", NULL, 0},
100 {".set", s_set, 0},
cefdba39
AM
101 /* Likewise for eqv. */
102 {"eqv", NULL, 0},
103 {".eqv", s_set, -1},
e9f53129
AM
104 {0,0,0}
105};
106
cd4a7468
AM
107/* Bits plugged into branch instruction offset field. */
108unsigned int brinfo;
109
e9f53129
AM
110void
111md_begin (void)
112{
e9f53129
AM
113 int i;
114
629310ab 115 op_hash = str_htab_create ();
e9f53129 116
fe0e921f
AM
117 /* Hash each mnemonic and record its position. There are
118 duplicates, keep just the first. */
e9f53129 119 for (i = 0; i < spu_num_opcodes; i++)
fe0e921f 120 str_hash_insert (op_hash, spu_opcodes[i].mnemonic, &spu_opcodes[i], 0);
e9f53129
AM
121}
122\f
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
17b9d67d 147md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
e9f53129
AM
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\f
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];
e0471c16 236 const char *exp;
3739860c 237 if (i > 1 && arg != A_P && format->arg[i-1] != A_P)
e9f53129
AM
238 buf[len++] = ',';
239 if (arg == A_P)
240 exp = "(";
241 else if (arg < A_P)
242 exp = i == syntax_error_arg ? "REG" : "reg";
3739860c 243 else
e9f53129
AM
244 exp = i == syntax_error_arg ? "IMM" : "imm";
245 len += sprintf (&buf[len], "%s", exp);
3739860c 246 if (i > 1 && format->arg[i-1] == A_P)
e9f53129
AM
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
9c2799c2 262 gas_assert (op);
e9f53129
AM
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
629310ab 276 if ((format = (struct spu_opcode *) str_hash_find (op_hash, op)) == NULL)
e9f53129
AM
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;
7bc3e93c 298 insn.reloc[i] = BFD_RELOC_NONE;
e9f53129
AM
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--;
e2785c44 333 as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d);
e9f53129
AM
334 }
335
cd4a7468
AM
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
e9f53129
AM
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++)
3739860c 354 if (insn.reloc_arg[i] >= 0)
e9f53129
AM
355 {
356 fixS *fixP;
7bc3e93c 357 bfd_reloc_code_real_type reloc = insn.reloc[i];
e9f53129 358 int pcrel = 0;
ece5ef60 359
7bc3e93c 360 if (reloc == BFD_RELOC_SPU_PCREL9a
e9f53129 361 || reloc == BFD_RELOC_SPU_PCREL9b
7bc3e93c 362 || reloc == BFD_RELOC_SPU_PCREL16)
e9f53129 363 pcrel = 1;
e9f53129
AM
364 fixP = fix_new_exp (frag_now,
365 thisfrag - frag_now->fr_literal,
366 4,
367 &insn.exp[i],
368 pcrel,
369 reloc);
840edabd
AM
370 fixP->tc_fix_data.arg_format = insn.reloc_arg[i];
371 fixP->tc_fix_data.insn_tag = insn.tag;
e9f53129
AM
372 }
373 dwarf2_emit_insn (4);
cd4a7468
AM
374
375 /* .brinfo lasts exactly one instruction. */
376 brinfo = 0;
e9f53129
AM
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)
7bc3e93c 398 param = get_imm (param, insn, arg);
e9f53129
AM
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 }
3739860c 495
e9f53129
AM
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
ece5ef60 593 if (strncasecmp (param, "%lo(", 4) == 0)
e9f53129
AM
594 {
595 param += 3;
596 low = 1;
597 as_warn (_("Using old style, %%lo(expr), please change to PPC style, expr@l."));
598 }
ece5ef60 599 else if (strncasecmp (param, "%hi(", 4) == 0)
e9f53129
AM
600 {
601 param += 3;
602 high = 1;
603 as_warn (_("Using old style, %%hi(expr), please change to PPC style, expr@h."));
604 }
ece5ef60 605 else if (strncasecmp (param, "%pic(", 5) == 0)
e9f53129
AM
606 {
607 /* Currently we expect %pic(expr) == expr, so do nothing here.
ece5ef60 608 i.e. for code loaded at address 0 $toc will be 0. */
e9f53129
AM
609 param += 4;
610 }
3739860c 611
e9f53129
AM
612 if (*param == '$')
613 {
614 /* Symbols can start with $, but if this symbol matches a register
ece5ef60
AM
615 name, it's probably a mistake. The only way to avoid this
616 warning is to rename the symbol. */
e9f53129
AM
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 }
3739860c 623
e9f53129
AM
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
ece5ef60 631 handle we do it inlined here. */
e9f53129
AM
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
e9f53129
AM
646 if (insn->exp[reloc_i].X_op == O_constant)
647 {
ece5ef60
AM
648 val = insn->exp[reloc_i].X_add_number;
649
e9f53129
AM
650 if (emulate_apuasm)
651 {
3739860c 652 /* Convert the value to a format we expect. */
e9f53129
AM
653 val <<= arg_encode[arg].rshift;
654 if (arg == A_U7A)
655 val = 173 - val;
656 else if (arg == A_U7B)
3739860c 657 val = 155 - val;
e9f53129
AM
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)
3739860c 683 val = 155 - val;
e9f53129
AM
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);
e9f53129
AM
692 }
693 else
694 {
695 insn->reloc_arg[reloc_i] = arg;
696 if (high)
7bc3e93c 697 insn->reloc[reloc_i] = BFD_RELOC_SPU_HI16;
ece5ef60 698 else if (low)
7bc3e93c
AM
699 insn->reloc[reloc_i] = BFD_RELOC_SPU_LO16;
700 else
701 insn->reloc[reloc_i] = arg_encode[arg].reloc;
e9f53129
AM
702 }
703
704 return param;
705}
706
6d4af3c2 707const char *
e9f53129
AM
708md_atof (int type, char *litP, int *sizeP)
709{
5b7c81bd 710 return ieee_md_atof (type, litP, sizeP, true);
e9f53129
AM
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
cd4a7468
AM
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
ece5ef60
AM
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 {
e4c2619a
AM
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. */
353ab861
AM
811 deferred_expression (&exp);
812 if ((exp.X_op == O_symbol
813 || exp.X_op == O_constant)
ece5ef60
AM
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
e4c2619a 825 save = input_line_pointer;
ece5ef60
AM
826 expression (&new_exp);
827 if (new_exp.X_op == O_constant)
828 exp.X_add_number += new_exp.X_add_number;
e4c2619a
AM
829 else
830 input_line_pointer = save;
ece5ef60
AM
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
e4c2619a
AM
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 }
ece5ef60
AM
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
e9f53129
AM
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;
add39d23
TS
869 reloc = XNEW (arelent);
870 reloc->sym_ptr_ptr = XNEW (asymbol *);
4bf09429 871 *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
e9f53129
AM
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);
4bf09429
AM
879 free (reloc->sym_ptr_ptr);
880 free (reloc);
e9f53129
AM
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{
fd361982 892 int align = bfd_section_alignment (seg);
e9f53129
AM
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;
cd4a7468 950 unsigned int mask;
e9f53129
AM
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. */
4bf09429 957 as_bad_subtract (fixP);
e9f53129
AM
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
353ab861 989 if (fixP->fx_r_type == BFD_RELOC_SPU_PPU32
8fdcc58d
TS
990 || fixP->fx_r_type == BFD_RELOC_SPU_PPU64
991 || fixP->fx_r_type == BFD_RELOC_SPU_ADD_PIC)
353ab861
AM
992 return;
993
e9f53129
AM
994 if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
995 {
996 fixP->fx_done = 1;
997 res = 0;
cd4a7468 998 mask = 0;
840edabd 999 if (fixP->tc_fix_data.arg_format > A_P)
e9f53129 1000 {
840edabd
AM
1001 int hi = arg_encode[fixP->tc_fix_data.arg_format].hi;
1002 int lo = arg_encode[fixP->tc_fix_data.arg_format].lo;
e9f53129
AM
1003 if (hi > lo && ((offsetT) val < lo || (offsetT) val > hi))
1004 as_bad_where (fixP->fx_file, fixP->fx_line,
20203fb9 1005 _("Relocation doesn't fit. (relocation value = 0x%lx)"),
e9f53129
AM
1006 (long) val);
1007 }
1008
1009 switch (fixP->fx_r_type)
cd4a7468
AM
1010 {
1011 case BFD_RELOC_8:
e9f53129
AM
1012 md_number_to_chars (place, val, 1);
1013 return;
1014
cd4a7468 1015 case BFD_RELOC_16:
e9f53129
AM
1016 md_number_to_chars (place, val, 2);
1017 return;
1018
cd4a7468 1019 case BFD_RELOC_32:
d62f07d0 1020 case BFD_RELOC_32_PCREL:
e9f53129
AM
1021 md_number_to_chars (place, val, 4);
1022 return;
1023
cd4a7468 1024 case BFD_RELOC_64:
e9f53129
AM
1025 md_number_to_chars (place, val, 8);
1026 return;
1027
cd4a7468
AM
1028 case BFD_RELOC_SPU_IMM7:
1029 res = val << 14;
1030 mask = 0x7f << 14;
1031 break;
e9f53129 1032
cd4a7468
AM
1033 case BFD_RELOC_SPU_IMM8:
1034 res = val << 14;
1035 mask = 0xff << 14;
1036 break;
e9f53129 1037
cd4a7468
AM
1038 case BFD_RELOC_SPU_IMM10:
1039 res = val << 14;
1040 mask = 0x3ff << 14;
1041 break;
e9f53129 1042
cd4a7468
AM
1043 case BFD_RELOC_SPU_IMM10W:
1044 res = val << 10;
1045 mask = 0x3ff0 << 10;
1046 break;
e9f53129 1047
cd4a7468
AM
1048 case BFD_RELOC_SPU_IMM16:
1049 res = val << 7;
1050 mask = 0xffff << 7;
1051 break;
e9f53129 1052
cd4a7468
AM
1053 case BFD_RELOC_SPU_IMM16W:
1054 res = val << 5;
1055 mask = 0x3fffc << 5;
1056 break;
e9f53129 1057
cd4a7468
AM
1058 case BFD_RELOC_SPU_IMM18:
1059 res = val << 7;
1060 mask = 0x3ffff << 7;
1061 break;
e9f53129 1062
cd4a7468
AM
1063 case BFD_RELOC_SPU_PCREL9a:
1064 res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14);
1065 mask = (0x1fc >> 2) | (0x600 << 14);
1066 break;
e9f53129 1067
cd4a7468
AM
1068 case BFD_RELOC_SPU_PCREL9b:
1069 res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5);
1070 mask = (0x1fc >> 2) | (0x600 << 5);
1071 break;
e9f53129 1072
cd4a7468
AM
1073 case BFD_RELOC_SPU_PCREL16:
1074 res = val << 5;
1075 mask = 0x3fffc << 5;
1076 break;
e9f53129 1077
d62f07d0 1078 case BFD_RELOC_SPU_HI16:
cd4a7468
AM
1079 res = val >> 9;
1080 mask = 0xffff << 7;
d62f07d0
AM
1081 break;
1082
1083 case BFD_RELOC_SPU_LO16:
cd4a7468
AM
1084 res = val << 7;
1085 mask = 0xffff << 7;
d62f07d0
AM
1086 break;
1087
cd4a7468
AM
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);
e9f53129
AM
1099 }
1100}