Back to index

cell-binutils  2.17cvs20070401
tc-spu.c
Go to the documentation of this file.
00001 /* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU)
00002 
00003    Copyright 2006, 2007 Free Software Foundation, Inc.
00004 
00005    This file is part of GAS, the GNU Assembler.
00006 
00007    GAS is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    GAS is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with GAS; see the file COPYING.  If not, write to the Free
00019    Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
00020    02110-1301, USA.  */
00021 
00022 #include "as.h"
00023 #include "safe-ctype.h"
00024 #include "subsegs.h"
00025 #include "dwarf2dbg.h" 
00026 
00027 const struct spu_opcode spu_opcodes[] = {
00028 #define APUOP(TAG,MACFORMAT,OPCODE,MNEMONIC,ASMFORMAT,DEP,PIPE) \
00029        { MACFORMAT, (OPCODE) << (32-11), MNEMONIC, ASMFORMAT },
00030 #define APUOPFB(TAG,MACFORMAT,OPCODE,FB,MNEMONIC,ASMFORMAT,DEP,PIPE) \
00031        { MACFORMAT, ((OPCODE) << (32-11)) | ((FB) << (32-18)), MNEMONIC, ASMFORMAT },
00032 #include "opcode/spu-insns.h"
00033 #undef APUOP
00034 #undef APUOPFB
00035 };
00036 
00037 static const int spu_num_opcodes =
00038   sizeof (spu_opcodes) / sizeof (spu_opcodes[0]);
00039 
00040 #define MAX_RELOCS 2
00041 
00042 struct spu_insn
00043 {
00044   unsigned int opcode;
00045   expressionS exp[MAX_RELOCS];
00046   int reloc_arg[MAX_RELOCS];
00047   int flag[MAX_RELOCS];
00048   enum spu_insns tag;
00049 };
00050 
00051 static const char *get_imm (const char *param, struct spu_insn *insn, int arg);
00052 static const char *get_reg (const char *param, struct spu_insn *insn, int arg,
00053                          int accept_expr);
00054 
00055 static int calcop (struct spu_opcode *format, const char *param,
00056                  struct spu_insn *insn);
00057 
00058 extern char *myname;
00059 static struct hash_control *op_hash = NULL;
00060 
00061 /* These bits should be turned off in the first address of every segment */
00062 int md_seg_align = 7;
00063 
00064 /* These chars start a comment anywhere in a source file (except inside
00065    another comment */
00066 const char comment_chars[] = "#";
00067 
00068 /* These chars only start a comment at the beginning of a line.  */
00069 const char line_comment_chars[] = "#";
00070 
00071 /* gods own line continuation char */
00072 const char line_separator_chars[] = ";";
00073 
00074 /* Chars that can be used to separate mant from exp in floating point nums */
00075 const char EXP_CHARS[] = "eE";
00076 
00077 /* Chars that mean this number is a floating point constant */
00078 /* as in 0f123.456 */
00079 /* or    0H1.234E-12 (see exp chars above) */
00080 const char FLT_CHARS[] = "dDfF";
00081 
00082 const pseudo_typeS md_pseudo_table[] =
00083 {
00084   {"align", s_align_ptwo, 4},
00085   {"def", s_set, 0},
00086   {"dfloat", float_cons, 'd'},
00087   {"ffloat", float_cons, 'f'},
00088   {"global", s_globl, 0},
00089   {"half", cons, 2},
00090   {"bss", s_lcomm_bytes, 1},
00091   {"string", stringer, 1},
00092   {"word", cons, 4},
00093   /* Force set to be treated as an instruction.  */
00094   {"set", NULL, 0},
00095   {".set", s_set, 0},
00096   /* Likewise for eqv.  */
00097   {"eqv", NULL, 0},
00098   {".eqv", s_set, -1},
00099   {"file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0 }, 
00100   {"loc", dwarf2_directive_loc, 0}, 
00101   {0,0,0}
00102 };
00103 
00104 void
00105 md_begin (void)
00106 {
00107   const char *retval = NULL;
00108   int i;
00109 
00110   /* initialize hash table */
00111 
00112   op_hash = hash_new ();
00113 
00114   /* loop until you see the end of the list */
00115 
00116   for (i = 0; i < spu_num_opcodes; i++)
00117     {
00118       /* hash each mnemonic and record its position */
00119 
00120       retval = hash_insert (op_hash, spu_opcodes[i].mnemonic, (PTR)&spu_opcodes[i]);
00121 
00122       if (retval != NULL && strcmp (retval, "exists") != 0)
00123        as_fatal (_("Can't hash instruction '%s':%s"),
00124                 spu_opcodes[i].mnemonic, retval);
00125     }
00126 }
00127 
00128 const char *md_shortopts = "";
00129 struct option md_longopts[] = {
00130 #define OPTION_APUASM (OPTION_MD_BASE)
00131   {"apuasm", no_argument, NULL, OPTION_APUASM},
00132 #define OPTION_DD2 (OPTION_MD_BASE+1)
00133   {"mdd2.0", no_argument, NULL, OPTION_DD2},
00134 #define OPTION_DD1 (OPTION_MD_BASE+2)
00135   {"mdd1.0", no_argument, NULL, OPTION_DD1},
00136 #define OPTION_DD3 (OPTION_MD_BASE+3)
00137   {"mdd3.0", no_argument, NULL, OPTION_DD3},
00138   { NULL, no_argument, NULL, 0 }
00139 };
00140 size_t md_longopts_size = sizeof (md_longopts);
00141 
00142 /* When set (by -apuasm) our assembler emulates the behaviour of apuasm.
00143  * e.g. don't add bias to float conversion and don't right shift
00144  * immediate values. */
00145 static int emulate_apuasm;
00146 
00147 /* Use the dd2.0 instructions set.  The only differences are some new
00148  * register names and the orx insn */
00149 static int use_dd2 = 1;
00150 
00151 int
00152 md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
00153 {
00154   switch (c)
00155     {
00156     case OPTION_APUASM:
00157       emulate_apuasm = 1;
00158       break;
00159     case OPTION_DD3:
00160       use_dd2 = 1;
00161       break;
00162     case OPTION_DD2:
00163       use_dd2 = 1;
00164       break;
00165     case OPTION_DD1:
00166       use_dd2 = 0;
00167       break;
00168     default:
00169       return 0;
00170     }
00171   return 1;
00172 }
00173 
00174 void
00175 md_show_usage (FILE *stream)
00176 {
00177   fputs (_("\
00178 SPU options:\n\
00179   --apuasm             emulate behaviour of apuasm\n"),
00180        stream);
00181 }
00182 
00183 
00184 struct arg_encode {
00185   int size;
00186   int pos;
00187   int rshift;
00188   int lo, hi;
00189   int wlo, whi;
00190   bfd_reloc_code_real_type reloc;
00191 };
00192 
00193 static struct arg_encode arg_encode[A_MAX] = {
00194   {  7,  0, 0,       0,    127,    0,   -1,  0 }, /* A_T */
00195   {  7,  7, 0,       0,    127,    0,   -1,  0 }, /* A_A */
00196   {  7, 14, 0,       0,    127,    0,   -1,  0 }, /* A_B */
00197   {  7, 21, 0,       0,    127,    0,   -1,  0 }, /* A_C */
00198   {  7,  7, 0,       0,    127,    0,   -1,  0 }, /* A_S */
00199   {  7,  7, 0,       0,    127,    0,   -1,  0 }, /* A_H */
00200   {  0,  0, 0,       0,     -1,    0,   -1,  0 }, /* A_P */
00201   {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_S3 */
00202   {  7, 14, 0,     -32,     31,  -31,    0,  BFD_RELOC_SPU_IMM7 }, /* A_S6 */
00203   {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_S7N */
00204   {  7, 14, 0,     -64,     63,  -63,    0,  BFD_RELOC_SPU_IMM7 }, /* A_S7 */
00205   {  8, 14, 0,       0,    127,    0,   -1,  BFD_RELOC_SPU_IMM8 }, /* A_U7A */
00206   {  8, 14, 0,       0,    127,    0,   -1,  BFD_RELOC_SPU_IMM8 }, /* A_U7B */
00207   { 10, 14, 0,    -512,    511, -128,  255,  BFD_RELOC_SPU_IMM10 }, /* A_S10B */
00208   { 10, 14, 0,    -512,    511,    0,   -1,  BFD_RELOC_SPU_IMM10 }, /* A_S10 */
00209   {  2, 23, 9,   -1024,   1023,    0,   -1,  BFD_RELOC_SPU_PCREL9a }, /* A_S11 */
00210   {  2, 14, 9,   -1024,   1023,    0,   -1,  BFD_RELOC_SPU_PCREL9b }, /* A_S11I */
00211   { 10, 14, 4,   -8192,   8191,    0,   -1,  BFD_RELOC_SPU_IMM10W }, /* A_S14 */
00212   { 16,  7, 0,  -32768,  32767,    0,   -1,  BFD_RELOC_SPU_IMM16 }, /* A_S16 */
00213   { 16,  7, 2, -131072, 262143,    0,   -1,  BFD_RELOC_SPU_IMM16W }, /* A_S18 */
00214   { 16,  7, 2, -262144, 262143,    0,   -1,  BFD_RELOC_SPU_PCREL16 }, /* A_R18 */
00215   {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_U3 */
00216   {  7, 14, 0,       0,    127,    0,   31,  BFD_RELOC_SPU_IMM7 }, /* A_U5 */
00217   {  7, 14, 0,       0,    127,    0,   63,  BFD_RELOC_SPU_IMM7 }, /* A_U6 */
00218   {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_U7 */
00219   { 14,  0, 0,       0,  16383,    0,   -1,  0 }, /* A_U14 */
00220   { 16,  7, 0,  -32768,  65535,    0,   -1,  BFD_RELOC_SPU_IMM16 }, /* A_X16 */
00221   { 18,  7, 0,       0, 262143,    0,   -1,  BFD_RELOC_SPU_IMM18 }, /* A_U18 */
00222 };
00223 
00224 /* Some flags for handling errors.  This is very hackish and added after
00225  * the fact. */
00226 static int syntax_error_arg;
00227 static const char *syntax_error_param;
00228 static int syntax_reg;
00229 
00230 static char *
00231 insn_fmt_string (struct spu_opcode *format)
00232 {
00233   static char buf[64];
00234   int len = 0;
00235   int i;
00236 
00237   len += sprintf (&buf[len], "%s\t", format->mnemonic);
00238   for (i = 1; i <= format->arg[0]; i++)
00239     {
00240       int arg = format->arg[i];
00241       char *exp;
00242       if (i > 1 && arg != A_P && format->arg[i-1] != A_P) 
00243        buf[len++] =  ',';
00244       if (arg == A_P)
00245        exp = "(";
00246       else if (arg < A_P)
00247        exp = i == syntax_error_arg ? "REG" : "reg";
00248       else 
00249        exp = i == syntax_error_arg ? "IMM" : "imm";
00250       len += sprintf (&buf[len], "%s", exp);
00251       if (i > 1 && format->arg[i-1] == A_P) 
00252        buf[len++] =  ')';
00253     }
00254   buf[len] = 0;
00255   return buf;
00256 }
00257 
00258 void
00259 md_assemble (char *op)
00260 {
00261   char *param, *thisfrag;
00262   char c;
00263   struct spu_opcode *format;
00264   struct spu_insn insn;
00265   int i;
00266 
00267   assert (op);
00268 
00269   /* skip over instruction to find parameters */
00270 
00271   for (param = op; *param != 0 && !ISSPACE (*param); param++)
00272     ;
00273   c = *param;
00274   *param = 0;
00275 
00276   if (c != 0 && c != '\n')
00277     param++;
00278 
00279   /* try to find the instruction in the hash table */
00280 
00281   if ((format = (struct spu_opcode *) hash_find (op_hash, op)) == NULL)
00282     {
00283       as_bad (_("Invalid mnemonic '%s'"), op);
00284       return;
00285     }
00286 
00287   if (!use_dd2 && strcmp (format->mnemonic, "orx") == 0)
00288     {
00289       as_bad (_("'%s' is only available in DD2.0 or higher."), op);
00290       return;
00291     }
00292 
00293   while (1)
00294     {
00295       /* try parsing this instruction into insn */
00296       for (i = 0; i < MAX_RELOCS; i++)
00297        {
00298          insn.exp[i].X_add_symbol = 0;
00299          insn.exp[i].X_op_symbol = 0;
00300          insn.exp[i].X_add_number = 0;
00301          insn.exp[i].X_op = O_illegal;
00302          insn.reloc_arg[i] = -1;
00303          insn.flag[i] = 0;
00304        }
00305       insn.opcode = format->opcode;
00306       insn.tag = (enum spu_insns) (format - spu_opcodes);
00307 
00308       syntax_error_arg = 0;
00309       syntax_error_param = 0;
00310       syntax_reg = 0;
00311       if (calcop (format, param, &insn))
00312        break;
00313 
00314       /* if it doesn't parse try the next instruction */
00315       if (!strcmp (format[0].mnemonic, format[1].mnemonic))
00316        format++;
00317       else
00318        {
00319          int parg = format[0].arg[syntax_error_arg-1];
00320 
00321          as_fatal (_("Error in argument %d.  Expecting:  \"%s\""),
00322                   syntax_error_arg - (parg == A_P),
00323                   insn_fmt_string (format));
00324          return;
00325        }
00326     }
00327 
00328   if ((syntax_reg & 4)
00329       && ! (insn.tag == M_RDCH
00330            || insn.tag == M_RCHCNT
00331            || insn.tag == M_WRCH))
00332     as_warn (_("Mixing register syntax, with and without '$'."));
00333   if (syntax_error_param)
00334     {
00335       const char *d = syntax_error_param;
00336       while (*d != '$')
00337        d--;
00338       as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d);
00339     }
00340 
00341   /* grow the current frag and plop in the opcode */
00342 
00343   thisfrag = frag_more (4);
00344   md_number_to_chars (thisfrag, insn.opcode, 4);
00345 
00346   /* if this instruction requires labels mark it for later */
00347 
00348   for (i = 0; i < MAX_RELOCS; i++)
00349     if (insn.reloc_arg[i] >= 0) 
00350       {
00351         fixS *fixP;
00352         bfd_reloc_code_real_type reloc = arg_encode[insn.reloc_arg[i]].reloc;
00353        int pcrel = 0;
00354         if (reloc == BFD_RELOC_SPU_PCREL9a
00355            || reloc == BFD_RELOC_SPU_PCREL9b
00356             || reloc == BFD_RELOC_SPU_PCREL16)
00357          pcrel = 1;
00358        if (insn.flag[i] & 1)
00359          reloc = BFD_RELOC_SPU_HI16;
00360        else if (insn.flag[i] & 2)
00361          reloc = BFD_RELOC_SPU_LO16;
00362        fixP = fix_new_exp (frag_now,
00363                          thisfrag - frag_now->fr_literal,
00364                          4,
00365                          &insn.exp[i],
00366                          pcrel,
00367                          reloc);
00368        fixP->tc_fix_data.arg_format = insn.reloc_arg[i];
00369        fixP->tc_fix_data.insn_tag = insn.tag;
00370       }
00371   dwarf2_emit_insn (4);
00372 }
00373 
00374 static int
00375 calcop (struct spu_opcode *format, const char *param, struct spu_insn *insn)
00376 {
00377   int i;
00378   int paren = 0;
00379   int arg;
00380 
00381   for (i = 1; i <= format->arg[0]; i++)
00382     {
00383       arg = format->arg[i];
00384       syntax_error_arg = i;
00385 
00386       while (ISSPACE (*param))
00387         param++;
00388       if (*param == 0 || *param == ',')
00389        return 0;
00390       if (arg < A_P)
00391         param = get_reg (param, insn, arg, 1);
00392       else if (arg > A_P)
00393         param = get_imm (param, insn,  arg);
00394       else if (arg == A_P)
00395        {
00396          paren++;
00397          if ('(' != *param++)
00398            return 0;
00399        }
00400 
00401       if (!param)
00402        return 0;
00403 
00404       while (ISSPACE (*param))
00405         param++;
00406 
00407       if (arg != A_P && paren)
00408        {
00409          paren--;
00410          if (')' != *param++)
00411            return 0;
00412        }
00413       else if (i < format->arg[0]
00414               && format->arg[i] != A_P
00415               && format->arg[i+1] != A_P)
00416        {
00417          if (',' != *param++)
00418            {
00419              syntax_error_arg++;
00420              return 0;
00421            }
00422        }
00423     }
00424   while (ISSPACE (*param))
00425     param++;
00426   return !paren && (*param == 0 || *param == '\n');
00427 }
00428 
00429 struct reg_name {
00430     unsigned int regno;
00431     unsigned int length;
00432     char name[32];
00433 };
00434 
00435 #define REG_NAME(NO,NM) { NO, sizeof (NM) - 1, NM }
00436 
00437 static struct reg_name reg_name[] = {
00438   REG_NAME (0, "lr"),  /* link register */
00439   REG_NAME (1, "sp"),  /* stack pointer */
00440   REG_NAME (0, "rp"),  /* link register */
00441   REG_NAME (127, "fp"),  /* frame pointer */
00442 };
00443 
00444 static struct reg_name sp_reg_name[] = {
00445 };
00446 
00447 static struct reg_name ch_reg_name[] = {
00448   REG_NAME (  0, "SPU_RdEventStat"),
00449   REG_NAME (  1, "SPU_WrEventMask"),
00450   REG_NAME (  2, "SPU_WrEventAck"),
00451   REG_NAME (  3, "SPU_RdSigNotify1"),
00452   REG_NAME (  4, "SPU_RdSigNotify2"),
00453   REG_NAME (  7, "SPU_WrDec"),
00454   REG_NAME (  8, "SPU_RdDec"),
00455   REG_NAME ( 11, "SPU_RdEventMask"), /* DD2.0 only */
00456   REG_NAME ( 13, "SPU_RdMachStat"),
00457   REG_NAME ( 14, "SPU_WrSRR0"),
00458   REG_NAME ( 15, "SPU_RdSRR0"),
00459   REG_NAME ( 28, "SPU_WrOutMbox"),
00460   REG_NAME ( 29, "SPU_RdInMbox"),
00461   REG_NAME ( 30, "SPU_WrOutIntrMbox"),
00462   REG_NAME (  9, "MFC_WrMSSyncReq"),
00463   REG_NAME ( 12, "MFC_RdTagMask"),   /* DD2.0 only */
00464   REG_NAME ( 16, "MFC_LSA"),
00465   REG_NAME ( 17, "MFC_EAH"),
00466   REG_NAME ( 18, "MFC_EAL"),
00467   REG_NAME ( 19, "MFC_Size"),
00468   REG_NAME ( 20, "MFC_TagID"),
00469   REG_NAME ( 21, "MFC_Cmd"),
00470   REG_NAME ( 22, "MFC_WrTagMask"),
00471   REG_NAME ( 23, "MFC_WrTagUpdate"),
00472   REG_NAME ( 24, "MFC_RdTagStat"),
00473   REG_NAME ( 25, "MFC_RdListStallStat"),
00474   REG_NAME ( 26, "MFC_WrListStallAck"),
00475   REG_NAME ( 27, "MFC_RdAtomicStat"),
00476 };
00477 #undef REG_NAME
00478 
00479 static const char *
00480 get_reg (const char *param, struct spu_insn *insn, int arg, int accept_expr)
00481 {
00482   unsigned regno;
00483   int saw_prefix = 0;
00484 
00485   if (*param == '$')
00486     {
00487       saw_prefix = 1;
00488       param++;
00489     }
00490     
00491   if (arg == A_H) /* Channel */
00492     {
00493       if ((param[0] == 'c' || param[0] == 'C')
00494          && (param[1] == 'h' || param[1] == 'H')
00495          && ISDIGIT (param[2]))
00496         param += 2;
00497     }
00498   else if (arg == A_S) /* Special purpose register */
00499     {
00500       if ((param[0] == 's' || param[0] == 'S')
00501          && (param[1] == 'p' || param[1] == 'P')
00502          && ISDIGIT (param[2]))
00503         param += 2;
00504     }
00505 
00506   if (ISDIGIT (*param))
00507     {
00508       regno = 0;
00509       while (ISDIGIT (*param))
00510        regno = regno * 10 + *param++ - '0';
00511     }
00512   else
00513     {
00514       struct reg_name *rn;
00515       unsigned int i, n, l = 0;
00516 
00517       if (arg == A_H) /* Channel */
00518        {
00519          rn = ch_reg_name;
00520          n = sizeof (ch_reg_name) / sizeof (*ch_reg_name);
00521        }
00522       else if (arg == A_S) /* Special purpose register */
00523        {
00524          rn = sp_reg_name;
00525          n = sizeof (sp_reg_name) / sizeof (*sp_reg_name);
00526        }
00527       else
00528        {
00529          rn = reg_name;
00530          n = sizeof (reg_name) / sizeof (*reg_name);
00531        }
00532       regno = 128;
00533       for (i = 0; i < n; i++)
00534        if (rn[i].length > l
00535            && 0 == strncasecmp (param, rn[i].name, rn[i].length))
00536           {
00537            l = rn[i].length;
00538             regno = rn[i].regno;
00539           }
00540       param += l;
00541     }
00542 
00543   if (!use_dd2
00544       && arg == A_H)
00545     {
00546       if (regno == 11)
00547        as_bad (_("'SPU_RdEventMask' (channel 11) is only available in DD2.0 or higher."));
00548       else if (regno == 12)
00549        as_bad (_("'MFC_RdTagMask' (channel 12) is only available in DD2.0 or higher."));
00550     }
00551 
00552   if (regno < 128)
00553     {
00554       insn->opcode |= regno << arg_encode[arg].pos;
00555       if ((!saw_prefix && syntax_reg == 1)
00556          || (saw_prefix && syntax_reg == 2))
00557        syntax_reg |= 4;
00558       syntax_reg |= saw_prefix ? 1 : 2;
00559       return param;
00560     }
00561 
00562   if (accept_expr)
00563     {
00564       char *save_ptr;
00565       expressionS ex;
00566       save_ptr = input_line_pointer;
00567       input_line_pointer = (char *)param;
00568       expression (&ex);
00569       param = input_line_pointer;
00570       input_line_pointer = save_ptr;
00571       if (ex.X_op == O_register || ex.X_op == O_constant)
00572        {
00573          insn->opcode |= ex.X_add_number << arg_encode[arg].pos;
00574          return param;
00575        }
00576     }
00577   return 0;
00578 }
00579 
00580 static const char *
00581 get_imm (const char *param, struct spu_insn *insn, int arg)
00582 {
00583   int val;
00584   char *save_ptr;
00585   int low = 0, high = 0;
00586   int reloc_i = insn->reloc_arg[0] >= 0 ? 1 : 0;
00587 
00588   if (strncmp (param, "%lo(", 4) == 0)
00589     {
00590       param += 3;
00591       low = 1;
00592       as_warn (_("Using old style, %%lo(expr), please change to PPC style, expr@l."));
00593     }
00594   else if (strncmp (param, "%hi(", 4) == 0)
00595     {
00596       param += 3;
00597       high = 1;
00598       as_warn (_("Using old style, %%hi(expr), please change to PPC style, expr@h."));
00599     }
00600   else if (strncmp (param, "%pic(", 5) == 0)
00601     {
00602       /* Currently we expect %pic(expr) == expr, so do nothing here.
00603        * i.e. for code loaded at address 0 $toc will be 0.  */
00604       param += 4;
00605     }
00606       
00607   if (*param == '$')
00608     {
00609       /* Symbols can start with $, but if this symbol matches a register
00610        * name, it's probably a mistake.   The only way to avoid this
00611        * warning is to rename the symbol.  */
00612       struct spu_insn tmp_insn;
00613       const char *np = get_reg (param, &tmp_insn, arg, 0);
00614 
00615       if (np)
00616        syntax_error_param = np;
00617     }
00618       
00619   save_ptr = input_line_pointer;
00620   input_line_pointer = (char *) param;
00621   expression (&insn->exp[reloc_i]);
00622   param = input_line_pointer;
00623   input_line_pointer = save_ptr;
00624 
00625   /* Similar to ppc_elf_suffix in tc-ppc.c.  We have so few cases to
00626    * handle we do it inlined here. */
00627   if (param[0] == '@' && !ISALNUM (param[2]) && param[2] != '@')
00628     {
00629       if (param[1] == 'h' || param[1] == 'H')
00630        {
00631          high = 1;
00632          param += 2;
00633        }
00634       else if (param[1] == 'l' || param[1] == 'L')
00635        {
00636          low = 1;
00637          param += 2;
00638        }
00639     }
00640 
00641   val = insn->exp[reloc_i].X_add_number;
00642 
00643   if (insn->exp[reloc_i].X_op == O_constant)
00644     {
00645       if (emulate_apuasm)
00646        {
00647          /* Convert the value to a format we expect. */ 
00648           val <<= arg_encode[arg].rshift;
00649          if (arg == A_U7A)
00650            val = 173 - val;
00651          else if (arg == A_U7B)
00652            val = 155 - val; 
00653        }
00654 
00655       if (high)
00656        val = val >> 16;
00657       else if (low)
00658        val = val & 0xffff;
00659 
00660       /* Warn about out of range expressions. */
00661       {
00662        int hi = arg_encode[arg].hi;
00663        int lo = arg_encode[arg].lo;
00664        int whi = arg_encode[arg].whi;
00665        int wlo = arg_encode[arg].wlo;
00666 
00667        if (hi > lo && (val < lo || val > hi))
00668          as_fatal (_("Constant expression %d out of range, [%d, %d]."),
00669                   val, lo, hi);
00670        else if (whi > wlo && (val < wlo || val > whi))
00671          as_warn (_("Constant expression %d out of range, [%d, %d]."),
00672                  val, wlo, whi);
00673       }
00674 
00675       if (arg == A_U7A)
00676         val = 173 - val;
00677       else if (arg == A_U7B)
00678         val = 155 - val; 
00679 
00680       /* Branch hints have a split encoding.  Do the bottom part. */
00681       if (arg == A_S11 || arg == A_S11I)
00682        insn->opcode |= ((val >> 2) & 0x7f);
00683 
00684       insn->opcode |= (((val >> arg_encode[arg].rshift)
00685                      & ((1 << arg_encode[arg].size) - 1))
00686                      << arg_encode[arg].pos);
00687       insn->reloc_arg[reloc_i] = -1;
00688       insn->flag[reloc_i] = 0;
00689     }
00690   else
00691     {
00692       insn->reloc_arg[reloc_i] = arg;
00693       if (high)
00694        insn->flag[reloc_i] |= 1;
00695       if (low)
00696        insn->flag[reloc_i] |= 2;
00697     }
00698 
00699   return param;
00700 }
00701 
00702 #define MAX_LITTLENUMS 6
00703 
00704 /* Turn a string in input_line_pointer into a floating point constant of type
00705    type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
00706    emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
00707  */
00708 char *
00709 md_atof (int type, char *litP, int *sizeP)
00710 {
00711   int prec;
00712   LITTLENUM_TYPE words[MAX_LITTLENUMS];
00713   LITTLENUM_TYPE *wordP;
00714   char *t;
00715 
00716   switch (type)
00717     {
00718     case 'f':
00719     case 'F':
00720     case 's':
00721     case 'S':
00722       prec = 2;
00723       break;
00724 
00725     case 'd':
00726     case 'D':
00727     case 'r':
00728     case 'R':
00729       prec = 4;
00730       break;
00731 
00732     case 'x':
00733     case 'X':
00734       prec = 6;
00735       break;
00736 
00737     case 'p':
00738     case 'P':
00739       prec = 6;
00740       break;
00741 
00742     default:
00743       *sizeP = 0;
00744       return _("Bad call to MD_ATOF()");
00745     }
00746   t = atof_ieee (input_line_pointer, type, words);
00747   if (t)
00748     input_line_pointer = t;
00749 
00750   *sizeP = prec * sizeof (LITTLENUM_TYPE);
00751   for (wordP = words; prec--;)
00752     {
00753       md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
00754       litP += sizeof (LITTLENUM_TYPE);
00755     }
00756   return 0;
00757 }
00758 
00759 #ifndef WORKING_DOT_WORD
00760 int md_short_jump_size = 4;
00761 
00762 void
00763 md_create_short_jump (char *ptr,
00764                     addressT from_addr ATTRIBUTE_UNUSED,
00765                     addressT to_addr ATTRIBUTE_UNUSED,
00766                     fragS *frag,
00767                     symbolS *to_symbol)
00768 {
00769   ptr[0] = (char) 0xc0;
00770   ptr[1] = 0x00;
00771   ptr[2] = 0x00;
00772   ptr[3] = 0x00;
00773   fix_new (frag,
00774           ptr - frag->fr_literal,
00775           4,
00776           to_symbol,
00777           (offsetT) 0,
00778           0,
00779           BFD_RELOC_SPU_PCREL16);
00780 }
00781 
00782 int md_long_jump_size = 4;
00783 
00784 void
00785 md_create_long_jump (char *ptr,
00786                    addressT from_addr ATTRIBUTE_UNUSED,
00787                    addressT to_addr ATTRIBUTE_UNUSED,
00788                    fragS *frag,
00789                    symbolS *to_symbol)
00790 {
00791   ptr[0] = (char) 0xc0;
00792   ptr[1] = 0x00;
00793   ptr[2] = 0x00;
00794   ptr[3] = 0x00;
00795   fix_new (frag,
00796           ptr - frag->fr_literal,
00797           4,
00798           to_symbol,
00799           (offsetT) 0,
00800           0,
00801           BFD_RELOC_SPU_PCREL16);
00802 }
00803 #endif
00804 
00805 int
00806 md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
00807                             segT segment_type ATTRIBUTE_UNUSED)
00808 {
00809   as_fatal (_("Relaxation should never occur"));
00810   return -1;
00811 }
00812 
00813 /* If while processing a fixup, a reloc really needs to be created,
00814    then it is done here.  */
00815 
00816 arelent *
00817 tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
00818 {
00819   arelent *reloc;
00820   reloc = (arelent *) xmalloc (sizeof (arelent));
00821   reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
00822   if (fixp->fx_addsy)
00823     *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
00824   else if (fixp->fx_subsy)
00825     *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
00826   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
00827   reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
00828   if (reloc->howto == (reloc_howto_type *) NULL)
00829     {
00830       as_bad_where (fixp->fx_file, fixp->fx_line,
00831                   _("reloc %d not supported by object file format"),
00832                   (int) fixp->fx_r_type);
00833       return NULL;
00834     }
00835   reloc->addend = fixp->fx_addnumber;
00836   return reloc;
00837 }
00838 
00839 /* Round up a section's size to the appropriate boundary.  */
00840 
00841 valueT
00842 md_section_align (segT seg, valueT size)
00843 {
00844   int align = bfd_get_section_alignment (stdoutput, seg);
00845   valueT mask = ((valueT) 1 << align) - 1;
00846 
00847   return (size + mask) & ~mask;
00848 }
00849 
00850 /* Where a PC relative offset is calculated from.  On the spu they
00851    are calculated from the beginning of the branch instruction.  */
00852 
00853 long
00854 md_pcrel_from (fixS *fixp)
00855 {
00856   return fixp->fx_frag->fr_address + fixp->fx_where;
00857 }
00858 
00859 /* Fill in rs_align_code fragments.  */
00860 
00861 void
00862 spu_handle_align (fragS *fragp)
00863 {
00864   static const unsigned char nop_pattern[8] = {
00865     0x40, 0x20, 0x00, 0x00, /* even nop */
00866     0x00, 0x20, 0x00, 0x00, /* odd  nop */
00867   };
00868 
00869   int bytes;
00870   char *p;
00871 
00872   if (fragp->fr_type != rs_align_code)
00873     return;
00874 
00875   bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
00876   p = fragp->fr_literal + fragp->fr_fix;
00877 
00878   if (bytes & 3)
00879     {
00880       int fix = bytes & 3;
00881       memset (p, 0, fix);
00882       p += fix;
00883       bytes -= fix;
00884       fragp->fr_fix += fix;
00885     }
00886   if (bytes & 4)
00887     {
00888       memcpy (p, &nop_pattern[4], 4);
00889       p += 4;
00890       bytes -= 4;
00891       fragp->fr_fix += 4;
00892     }
00893 
00894   memcpy (p, nop_pattern, 8);
00895   fragp->fr_var = 8;
00896 }
00897 
00898 void
00899 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
00900 {
00901   unsigned int res;
00902   valueT val = *valP;
00903   char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
00904 
00905   if (fixP->fx_subsy != (symbolS *) NULL)
00906     {
00907       /* We can't actually support subtracting a symbol.  */
00908       as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
00909     }
00910 
00911   if (fixP->fx_addsy != NULL)
00912     {
00913       if (fixP->fx_pcrel)
00914        {
00915          /* Hack around bfd_install_relocation brain damage.  */
00916          val += fixP->fx_frag->fr_address + fixP->fx_where;
00917 
00918          switch (fixP->fx_r_type)
00919            {
00920            case BFD_RELOC_32:
00921              fixP->fx_r_type = BFD_RELOC_32_PCREL;
00922              break;
00923 
00924            case BFD_RELOC_SPU_PCREL16:
00925            case BFD_RELOC_SPU_PCREL9a:
00926            case BFD_RELOC_SPU_PCREL9b:
00927            case BFD_RELOC_32_PCREL:
00928              break;
00929 
00930            default:
00931              as_bad_where (fixP->fx_file, fixP->fx_line,
00932                          _("expression too complex"));
00933              break;
00934            }
00935        }
00936     }
00937 
00938   fixP->fx_addnumber = val;
00939 
00940   if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
00941     {
00942       fixP->fx_done = 1;
00943       res = 0;
00944       if (fixP->tc_fix_data.arg_format > A_P)
00945        {
00946          int hi = arg_encode[fixP->tc_fix_data.arg_format].hi;
00947          int lo = arg_encode[fixP->tc_fix_data.arg_format].lo;
00948          if (hi > lo && ((offsetT) val < lo || (offsetT) val > hi))
00949            as_bad_where (fixP->fx_file, fixP->fx_line,
00950                        "Relocation doesn't fit. (relocation value = 0x%lx)",
00951                        (long) val);
00952        }
00953 
00954       switch (fixP->fx_r_type)
00955         {
00956         case BFD_RELOC_8:
00957          md_number_to_chars (place, val, 1);
00958          return;
00959 
00960         case BFD_RELOC_16:
00961          md_number_to_chars (place, val, 2);
00962          return;
00963 
00964         case BFD_RELOC_32:
00965          md_number_to_chars (place, val, 4);
00966          return;
00967 
00968         case BFD_RELOC_64:
00969          md_number_to_chars (place, val, 8);
00970          return;
00971 
00972         case BFD_RELOC_SPU_IMM7:
00973           res = (val & 0x7f) << 14;
00974           break;
00975 
00976         case BFD_RELOC_SPU_IMM8:
00977           res = (val & 0xff) << 14;
00978           break;
00979 
00980         case BFD_RELOC_SPU_IMM10:
00981           res = (val & 0x3ff) << 14;
00982           break;
00983 
00984         case BFD_RELOC_SPU_IMM10W:
00985           res = (val & 0x3ff0) << 10;
00986           break;
00987 
00988         case BFD_RELOC_SPU_IMM16:
00989           res = (val & 0xffff) << 7;
00990           break;
00991 
00992         case BFD_RELOC_SPU_IMM16W:
00993           res = (val & 0x3fffc) << 5;
00994           break;
00995 
00996         case BFD_RELOC_SPU_IMM18:
00997           res = (val & 0x3ffff) << 7;
00998           break;
00999 
01000         case BFD_RELOC_SPU_PCREL9a:
01001           res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14);
01002           break;
01003 
01004         case BFD_RELOC_SPU_PCREL9b:
01005           res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5);
01006           break;
01007 
01008         case BFD_RELOC_SPU_PCREL16:
01009           res = (val & 0x3fffc) << 5;
01010           break;
01011 
01012         default:
01013           as_bad_where (fixP->fx_file, fixP->fx_line,
01014                         _("reloc %d not supported by object file format"),
01015                         (int) fixP->fx_r_type);
01016         }
01017 
01018       if (res != 0)
01019         {
01020           place[0] |= (res >> 24) & 0xff;
01021           place[1] |= (res >> 16) & 0xff;
01022           place[2] |= (res >> 8) & 0xff;
01023           place[3] |= (res) & 0xff;
01024         }
01025     }
01026 }