Back to index

cell-binutils  2.17cvs20070401
tc-ip2k.c
Go to the documentation of this file.
00001 /* tc-ip2k.c -- Assembler for the Scenix IP2xxx.
00002    Copyright (C) 2000, 2002, 2003, 2005, 2006 Free Software Foundation.
00003 
00004    This file is part of GAS, the GNU Assembler.
00005 
00006    GAS is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    GAS is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with GAS; see the file COPYING.  If not, write to
00018    the Free Software Foundation, 51 Franklin Street - Fifth Floor,
00019    Boston, MA 02110-1301, USA.  */
00020 
00021 #include "as.h"
00022 #include "subsegs.h"     
00023 #include "symcat.h"
00024 #include "opcodes/ip2k-desc.h"
00025 #include "opcodes/ip2k-opc.h"
00026 #include "cgen.h"
00027 #include "elf/common.h"
00028 #include "elf/ip2k.h"
00029 #include "libbfd.h"
00030 
00031 /* Structure to hold all of the different components describing
00032    an individual instruction.  */
00033 typedef struct
00034 {
00035   const CGEN_INSN *  insn;
00036   const CGEN_INSN *  orig_insn;
00037   CGEN_FIELDS        fields;
00038 #if CGEN_INT_INSN_P
00039   CGEN_INSN_INT         buffer [1];
00040 #define INSN_VALUE(buf) (*(buf))
00041 #else
00042   unsigned char         buffer [CGEN_MAX_INSN_SIZE];
00043 #define INSN_VALUE(buf) (buf)
00044 #endif
00045   char *             addr;
00046   fragS *            frag;
00047   int                   num_fixups;
00048   fixS *                fixups [GAS_CGEN_MAX_FIXUPS];
00049   int                   indices [MAX_OPERAND_INSTANCES];
00050 }
00051 ip2k_insn;
00052 
00053 const char comment_chars[]        = ";";
00054 const char line_comment_chars[]   = "#";
00055 const char line_separator_chars[] = ""; 
00056 const char EXP_CHARS[]            = "eE";
00057 const char FLT_CHARS[]            = "dD";
00058 
00059 /* Flag to detect when switching to code section where insn alignment is
00060    implied.  */
00061 static int force_code_align = 0;
00062 
00063 /* Mach selected from command line.  */
00064 static int ip2k_mach = 0;
00065 static unsigned ip2k_mach_bitmask = 0;
00066 
00067 
00068 static void
00069 ip2k_elf_section_rtn (int i)
00070 {
00071   obj_elf_section(i);
00072 
00073   if (force_code_align)
00074     {
00075       /* The s_align_ptwo function expects that we are just after a .align
00076         directive and it will either try and read the align value or stop
00077         if end of line so we must fake it out so it thinks we are at the
00078         end of the line.  */
00079       char *old_input_line_pointer = input_line_pointer;
00080       input_line_pointer = "\n";
00081       s_align_ptwo (1);
00082       force_code_align = 0;
00083       /* Restore.  */
00084       input_line_pointer = old_input_line_pointer;
00085     }
00086 }
00087 
00088 static void
00089 ip2k_elf_section_text (int i)
00090 {
00091   char *old_input_line_pointer;
00092   obj_elf_text(i);
00093 
00094   /* the s_align_ptwo function expects that we are just after a .align
00095      directive and it will either try and read the align value or stop if
00096      end of line so we must fake it out so it thinks we are at the end of
00097      the line.  */
00098   old_input_line_pointer = input_line_pointer;
00099   input_line_pointer = "\n";
00100   s_align_ptwo (1);
00101   force_code_align = 0;
00102   /* Restore.  */
00103   input_line_pointer = old_input_line_pointer;
00104 }
00105 
00106 /* The target specific pseudo-ops which we support.  */
00107 const pseudo_typeS md_pseudo_table[] =
00108 {
00109     { "text",   ip2k_elf_section_text,  0 },
00110     { "sect",   ip2k_elf_section_rtn,   0 },
00111     { NULL,   NULL,                0 }
00112 };
00113 
00114 
00115 
00116 enum options
00117 {
00118   OPTION_CPU_IP2022 = OPTION_MD_BASE,
00119   OPTION_CPU_IP2022EXT
00120 };
00121 
00122 struct option md_longopts[] = 
00123 {
00124   { "mip2022",     no_argument, NULL, OPTION_CPU_IP2022 },
00125   { "mip2022ext",  no_argument, NULL, OPTION_CPU_IP2022EXT },
00126   { NULL,           no_argument, NULL, 0 },
00127 };
00128 size_t md_longopts_size = sizeof (md_longopts);
00129 
00130 const char * md_shortopts = "";
00131 
00132 int
00133 md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED)
00134 {
00135   switch (c)
00136     {
00137     case OPTION_CPU_IP2022:
00138       ip2k_mach = bfd_mach_ip2022;
00139       ip2k_mach_bitmask = 1 << MACH_IP2022;
00140       break;
00141 
00142     case OPTION_CPU_IP2022EXT:
00143       ip2k_mach = bfd_mach_ip2022ext;
00144       ip2k_mach_bitmask = 1 << MACH_IP2022EXT;
00145       break;
00146 
00147     default:
00148       return 0;
00149     }
00150 
00151   return 1;
00152 }
00153 
00154 void
00155 md_show_usage (FILE * stream)
00156 {
00157   fprintf (stream, _("IP2K specific command line options:\n"));
00158   fprintf (stream, _("  -mip2022               restrict to IP2022 insns \n"));
00159   fprintf (stream, _("  -mip2022ext            permit extended IP2022 insn\n"));
00160 }
00161 
00162 
00163 void
00164 md_begin (void)
00165 {
00166   /* Initialize the `cgen' interface.  */
00167   
00168   /* Set the machine number and endian.  */
00169   gas_cgen_cpu_desc = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_MACHS,
00170                                      ip2k_mach_bitmask,
00171                                      CGEN_CPU_OPEN_ENDIAN,
00172                                      CGEN_ENDIAN_BIG,
00173                                      CGEN_CPU_OPEN_END);
00174   ip2k_cgen_init_asm (gas_cgen_cpu_desc);
00175 
00176   /* This is a callback from cgen to gas to parse operands.  */
00177   cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
00178 
00179   /* Set the machine type.  */
00180   bfd_default_set_arch_mach (stdoutput, bfd_arch_ip2k, ip2k_mach);
00181 }
00182 
00183 
00184 void
00185 md_assemble (char * str)
00186 {
00187   ip2k_insn insn;
00188   char * errmsg;
00189 
00190   /* Initialize GAS's cgen interface for a new instruction.  */
00191   gas_cgen_init_parse ();
00192 
00193   insn.insn = ip2k_cgen_assemble_insn
00194       (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
00195 
00196   if (!insn.insn)
00197     {
00198       as_bad ("%s", errmsg);
00199       return;
00200     }
00201 
00202   /* Check for special relocation required by SKIP instructions.  */
00203   if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SKIPA))
00204     /* Unconditional skip has a 1-bit relocation of the current pc, so
00205        that we emit either sb pcl.0 or snb pcl.0 depending on whether
00206        the PCL (pc + 2) >> 1 is odd or even.  */
00207     {
00208       enum cgen_parse_operand_result result_type;
00209       bfd_vma value;
00210       const char *curpc_plus_2 = ".+2";
00211       const char *err;
00212 
00213       err = cgen_parse_address (gas_cgen_cpu_desc, & curpc_plus_2,
00214                             IP2K_OPERAND_ADDR16CJP,
00215                             BFD_RELOC_IP2K_PC_SKIP,
00216                             & result_type, & value);
00217       if (err)
00218        {
00219          as_bad ("%s", err);
00220          return;
00221        }
00222     }
00223 
00224   /* Doesn't really matter what we pass for RELAX_P here.  */
00225   gas_cgen_finish_insn (insn.insn, insn.buffer,
00226                      CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
00227 }
00228 
00229 valueT
00230 md_section_align (segT segment, valueT size)
00231 {
00232   int align = bfd_get_section_alignment (stdoutput, segment);
00233 
00234   return ((size + (1 << align) - 1) & (-1 << align));
00235 }
00236 
00237 
00238 symbolS *
00239 md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
00240 {
00241     return 0;
00242 }
00243 
00244 int
00245 md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
00246                             segT    segment ATTRIBUTE_UNUSED)
00247 {
00248   as_fatal (_("md_estimate_size_before_relax\n"));
00249   return 1;
00250 } 
00251 
00252 
00253 /* *fragP has been relaxed to its final size, and now needs to have
00254    the bytes inside it modified to conform to the new size.
00255 
00256    Called after relaxation is finished.
00257    fragP->fr_type == rs_machine_dependent.
00258    fragP->fr_subtype is the subtype of what the address relaxed to.  */
00259 
00260 void
00261 md_convert_frag (bfd   * abfd  ATTRIBUTE_UNUSED,
00262                segT    sec   ATTRIBUTE_UNUSED,
00263                fragS * fragP ATTRIBUTE_UNUSED)
00264 {
00265 }
00266 
00267 
00268 /* Functions concerning relocs.  */
00269 
00270 long
00271 md_pcrel_from (fixS *fixP)
00272 {
00273   as_fatal (_("md_pcrel_from\n"));
00274 
00275   /* Return the address of the delay slot. */
00276   return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
00277 }
00278 
00279 
00280 /* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
00281    Returns BFD_RELOC_NONE if no reloc type can be found.
00282    *FIXP may be modified if desired.  */
00283 
00284 bfd_reloc_code_real_type
00285 md_cgen_lookup_reloc (const CGEN_INSN *    insn     ATTRIBUTE_UNUSED,
00286                     const CGEN_OPERAND * operand,
00287                     fixS *               fixP     ATTRIBUTE_UNUSED)
00288 {
00289   bfd_reloc_code_real_type result;
00290 
00291   result = BFD_RELOC_NONE;
00292 
00293   switch (operand->type)
00294     {
00295     case IP2K_OPERAND_FR:
00296     case IP2K_OPERAND_ADDR16L:
00297     case IP2K_OPERAND_ADDR16H:
00298     case IP2K_OPERAND_LIT8:
00299       /* These may have been processed at parse time.  */
00300       if (fixP->fx_cgen.opinfo != 0)
00301        result = fixP->fx_cgen.opinfo;
00302       fixP->fx_no_overflow = 1;
00303       break;
00304 
00305     case IP2K_OPERAND_ADDR16CJP:
00306       result = fixP->fx_cgen.opinfo;
00307       if (result == 0 || result == BFD_RELOC_NONE)
00308        result = BFD_RELOC_IP2K_ADDR16CJP;
00309       fixP->fx_no_overflow = 1;
00310       break;
00311 
00312     case IP2K_OPERAND_ADDR16P:
00313       result = BFD_RELOC_IP2K_PAGE3;
00314       fixP->fx_no_overflow = 1;
00315       break;
00316 
00317     default:
00318       result = BFD_RELOC_NONE;
00319       break;
00320     }
00321 
00322   return result;
00323 }
00324 
00325 
00326 /* Write a value out to the object file, using the appropriate endianness.  */
00327 
00328 void
00329 md_number_to_chars (char * buf, valueT val, int n)
00330 {
00331   number_to_chars_bigendian (buf, val, n);
00332 }
00333 
00334 /* Turn a string in input_line_pointer into a floating point constant of type
00335    type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
00336    emitted is stored in *sizeP .  An error message is returned, or NULL on
00337    OK.  */
00338 
00339 /* Equal to MAX_PRECISION in atof-ieee.c  */
00340 #define MAX_LITTLENUMS 6
00341 
00342 char *
00343 md_atof (int type, char * litP, int *  sizeP)
00344 {
00345   int              prec;
00346   LITTLENUM_TYPE   words [MAX_LITTLENUMS];
00347   LITTLENUM_TYPE  *wordP;
00348   char *           t;
00349 
00350   switch (type)
00351     {
00352     case 'f':
00353     case 'F':
00354     case 's':
00355     case 'S':
00356       prec = 2;
00357       break;
00358 
00359     case 'd':
00360     case 'D':
00361     case 'r':
00362     case 'R':
00363       prec = 4;
00364       break;
00365 
00366    /* FIXME: Some targets allow other format chars for bigger sizes here.  */
00367 
00368     default:
00369       * sizeP = 0;
00370       return _("Bad call to md_atof()");
00371     }
00372 
00373   t = atof_ieee (input_line_pointer, type, words);
00374   if (t)
00375     input_line_pointer = t;
00376   * sizeP = prec * sizeof (LITTLENUM_TYPE);
00377 
00378   /* This loops outputs the LITTLENUMs in REVERSE order; in accord with
00379      the ip2k endianness.  */
00380   for (wordP = words; prec--;)
00381     {
00382       md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
00383       litP += sizeof (LITTLENUM_TYPE);
00384     }
00385      
00386   return 0;
00387 }
00388 
00389 
00390 /* See whether we need to force a relocation into the output file.
00391    Force most of them, since the linker's bfd relocation engine
00392    understands range limits better than gas' cgen fixup engine.
00393    Consider the case of a fixup intermediate value being larger than
00394    the instruction it will be eventually encoded within.  */
00395 
00396 int
00397 ip2k_force_relocation (fixS * fix)
00398 {
00399   switch (fix->fx_r_type)
00400     {
00401     case BFD_RELOC_IP2K_FR9:
00402     case BFD_RELOC_IP2K_FR_OFFSET:
00403     case BFD_RELOC_IP2K_BANK:
00404     case BFD_RELOC_IP2K_ADDR16CJP:
00405     case BFD_RELOC_IP2K_PAGE3:
00406     case BFD_RELOC_IP2K_LO8DATA:
00407     case BFD_RELOC_IP2K_HI8DATA:
00408     case BFD_RELOC_IP2K_EX8DATA:
00409     case BFD_RELOC_IP2K_LO8INSN:
00410     case BFD_RELOC_IP2K_HI8INSN:
00411     case BFD_RELOC_IP2K_PC_SKIP:
00412     case BFD_RELOC_IP2K_TEXT:
00413       return 1;
00414 
00415     case BFD_RELOC_16:
00416       if (fix->fx_subsy && S_IS_DEFINED (fix->fx_subsy)
00417          && fix->fx_addsy && S_IS_DEFINED (fix->fx_addsy)
00418          && (S_GET_SEGMENT (fix->fx_addsy)->flags & SEC_CODE))
00419        {
00420          fix->fx_r_type = BFD_RELOC_IP2K_TEXT;
00421          return 0;
00422        }
00423       break;
00424 
00425     default:
00426       break;
00427     }
00428 
00429   return generic_force_reloc (fix);
00430 }
00431 
00432 void
00433 ip2k_apply_fix (fixS *fixP, valueT *valueP, segT seg)
00434 {
00435   if (fixP->fx_r_type == BFD_RELOC_IP2K_TEXT
00436       && ! fixP->fx_addsy
00437       && ! fixP->fx_subsy)
00438     {
00439       *valueP = ((int)(* valueP)) / 2;
00440       fixP->fx_r_type = BFD_RELOC_16;
00441     }
00442   else if (fixP->fx_r_type == BFD_RELOC_UNUSED + IP2K_OPERAND_FR)
00443     {
00444       /* Must be careful when we are fixing up an FR.  We could be
00445         fixing up an offset to (SP) or (DP) in which case we don't
00446         want to step on the top 2 bits of the FR operand.  The
00447         gas_cgen_md_apply_fix doesn't know any better and overwrites
00448         the entire operand.  We counter this by adding the bits
00449         to the new value.  */
00450       char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
00451 
00452       /* Canonical name, since used a lot.  */
00453       CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
00454       CGEN_INSN_INT insn_value
00455        = cgen_get_insn_value (cd, (unsigned char *) where,
00456                             CGEN_INSN_BITSIZE (fixP->fx_cgen.insn));
00457       /* Preserve (DP) or (SP) specification.  */
00458       *valueP += (insn_value & 0x180);
00459     }
00460 
00461   gas_cgen_md_apply_fix (fixP, valueP, seg);
00462 }
00463 
00464 int
00465 ip2k_elf_section_flags (int flags,
00466                      int attr ATTRIBUTE_UNUSED,
00467                      int type ATTRIBUTE_UNUSED)
00468 {
00469   /* This is used to detect when the section changes to an executable section.
00470      This function is called by the elf section processing.  When we note an
00471      executable section specifier we set an internal flag to denote when
00472      word alignment should be forced.  */
00473   if (flags & SEC_CODE)
00474     force_code_align = 1;
00475  
00476   return flags;
00477 }
00478