gas/
[deliverable/binutils-gdb.git] / gas / config / tc-tic6x.c
index 5e63c8bd2b6572df2ab48658a29673177f76f241..c706158bd787f084c03cab95eaafc64d6ceb7a29 100644 (file)
@@ -24,6 +24,7 @@
 #include "safe-ctype.h"
 #include "subsegs.h"
 #include "opcode/tic6x.h"
+#include "elf/tic6x.h"
 #include "elf32-tic6x.h"
 
 /* Truncate and sign-extend at 32 bits, so that building on a 64-bit
@@ -81,6 +82,15 @@ static unsigned short tic6x_arch_enable = (TIC6X_INSN_C62X
    (architecture, as modified by other options).  */
 static unsigned short tic6x_features;
 
+/* The architecture attribute value, or C6XABI_Tag_CPU_arch_none if
+   not yet set.  */
+static int tic6x_arch_attribute = C6XABI_Tag_CPU_arch_none;
+
+/* Whether any instructions at all have been seen.  Once any
+   instructions have been seen, architecture attributes merge into the
+   previous attribute value rather than replacing it.  */
+static bfd_boolean tic6x_seen_insns = FALSE;
+
 /* The number of registers in each register file supported by the
    current architecture.  */
 static unsigned int tic6x_num_registers;
@@ -105,21 +115,26 @@ static bfd_boolean tic6x_generate_rela = TRUE;
 typedef struct
 {
   const char *arch;
+  int attr;
   unsigned short features;
 } tic6x_arch_table;
 static const tic6x_arch_table tic6x_arches[] =
   {
-    { "c62x", TIC6X_INSN_C62X },
-    { "c64x", TIC6X_INSN_C62X | TIC6X_INSN_C64X },
-    { "c64x+", TIC6X_INSN_C62X | TIC6X_INSN_C64X | TIC6X_INSN_C64XP },
-    { "c67x", TIC6X_INSN_C62X | TIC6X_INSN_C67X },
-    { "c67x+", TIC6X_INSN_C62X | TIC6X_INSN_C67X | TIC6X_INSN_C67XP },
-    { "c674x", (TIC6X_INSN_C62X
-               | TIC6X_INSN_C64X
-               | TIC6X_INSN_C64XP
-               | TIC6X_INSN_C67X
-               | TIC6X_INSN_C67XP
-               | TIC6X_INSN_C674X) }
+    { "c62x", C6XABI_Tag_CPU_arch_C62X, TIC6X_INSN_C62X },
+    { "c64x", C6XABI_Tag_CPU_arch_C64X, TIC6X_INSN_C62X | TIC6X_INSN_C64X },
+    { "c64x+", C6XABI_Tag_CPU_arch_C64XP, (TIC6X_INSN_C62X
+                                          | TIC6X_INSN_C64X
+                                          | TIC6X_INSN_C64XP) },
+    { "c67x", C6XABI_Tag_CPU_arch_C67X, TIC6X_INSN_C62X | TIC6X_INSN_C67X },
+    { "c67x+", C6XABI_Tag_CPU_arch_C67XP, (TIC6X_INSN_C62X
+                                          | TIC6X_INSN_C67X
+                                          | TIC6X_INSN_C67XP) },
+    { "c674x", C6XABI_Tag_CPU_arch_C674X, (TIC6X_INSN_C62X
+                                          | TIC6X_INSN_C64X
+                                          | TIC6X_INSN_C64XP
+                                          | TIC6X_INSN_C67X
+                                          | TIC6X_INSN_C67XP
+                                          | TIC6X_INSN_C674X) }
   };
 
 /* Update the selected architecture based on ARCH, giving an error if
@@ -135,6 +150,12 @@ tic6x_use_arch (const char *arch)
     if (strcmp (arch, tic6x_arches[i].arch) == 0)
       {
        tic6x_arch_enable = tic6x_arches[i].features;
+       if (tic6x_seen_insns)
+         tic6x_arch_attribute
+           = elf32_tic6x_merge_arch_attributes (tic6x_arch_attribute,
+                                                tic6x_arches[i].attr);
+       else
+         tic6x_arch_attribute = tic6x_arches[i].attr;
        return;
       }
 
@@ -295,10 +316,53 @@ s_tic6x_nocmp (int ignored ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Track for each attribute whether it has been set explicitly (and so
+   should not have a default value set by the assembler).  */
+static bfd_boolean tic6x_attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES];
+
+/* Parse a .c6xabi_attribute directive.  */
+
+static void
+s_tic6x_c6xabi_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  int tag = s_vendor_attribute (OBJ_ATTR_PROC);
+
+  if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
+    tic6x_attributes_set_explicitly[tag] = TRUE;
+}
+
+typedef struct
+{
+  const char *name;
+  int tag;
+} tic6x_attribute_table;
+
+static const tic6x_attribute_table tic6x_attributes[] =
+  {
+#define TAG(tag, value) { #tag, tag }
+#include "elf/tic6x-attrs.h"
+#undef TAG
+  };
+
+/* Convert an attribute name to a number.  */
+
+int
+tic6x_convert_symbolic_attribute (const char *name)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (tic6x_attributes); i++)
+    if (strcmp (name, tic6x_attributes[i].name) == 0)
+      return tic6x_attributes[i].tag;
+
+  return -1;
+}
+
 const pseudo_typeS md_pseudo_table[] =
   {
     { "arch", s_tic6x_arch, 0 },
     { "atomic", s_tic6x_atomic, 0 },
+    { "c6xabi_attribute", s_tic6x_c6xabi_attribute, 0 },
     { "noatomic", s_tic6x_noatomic, 0 },
     { "nocmp", s_tic6x_nocmp, 0 },
     { "word", cons, 4 },
@@ -569,6 +633,7 @@ tic6x_cons_align (int n ATTRIBUTE_UNUSED)
   seginfo->tc_segment_info_data.execute_packet_frag = NULL;
   seginfo->tc_segment_info_data.last_insn_lsb = NULL;
   seginfo->tc_segment_info_data.spmask_addr = NULL;
+  seginfo->tc_segment_info_data.func_units_used = 0;
 }
 
 /* Handle an alignment directive.  Return TRUE if the
@@ -1526,6 +1591,25 @@ tic6x_init_fix_data (fixS *fixP)
   fixP->tc_fix_data.fix_adda = FALSE;
 }
 
+/* Return true if the fix can be handled by GAS, false if it must
+   be passed through to the linker.  */
+
+bfd_boolean
+tic6x_fix_adjustable (fixS *fixP)
+{
+  switch (fixP->fx_r_type)
+    {
+      /* Adjust_reloc_syms doesn't know about the GOT.  */
+    case BFD_RELOC_C6000_SBR_GOT_U15_W:
+    case BFD_RELOC_C6000_SBR_GOT_H16_W:
+    case BFD_RELOC_C6000_SBR_GOT_L16_W:
+      return 0;
+
+    default:
+      return 1;
+    }
+}
+
 /* Given the fine-grained form of an operand, return the coarse
    (bit-mask) form.  */
 
@@ -2419,6 +2503,7 @@ tic6x_try_encode (tic6x_opcode_id id, tic6x_operand *operands,
 
          if (opct->variable_fields[fld].coding_method == tic6x_coding_fstg)
            {
+             int i, t;
              if (operands[opno].value.exp.X_add_number < 0
                  || (operands[opno].value.exp.X_add_number
                      >= (1 << (fldd->width - fcyc_bits))))
@@ -2429,7 +2514,13 @@ tic6x_try_encode (tic6x_opcode_id id, tic6x_operand *operands,
                  *ok = FALSE;
                  return 0;
                }
-             value = operands[opno].value.exp.X_add_number << fcyc_bits;
+             value = operands[opno].value.exp.X_add_number;
+             for (t = 0, i = fcyc_bits; i < fldd->width; i++)
+               {
+                 t = (t << 1) | (value & 1);
+                 value >>= 1;
+               }
+             value = t << fcyc_bits;
            }
          else
            {
@@ -2587,6 +2678,16 @@ md_assemble (char *str)
   if (p == str)
     abort ();
 
+  /* Now an instruction has been seen, architecture attributes from
+     .arch directives merge with rather than overriding the previous
+     value.  */
+  tic6x_seen_insns = TRUE;
+  /* If no .arch directives or -march options have been seen, we are
+     assessing instruction validity based on the C674X default, so set
+     the attribute accordingly.  */
+  if (tic6x_arch_attribute == C6XABI_Tag_CPU_arch_none)
+    tic6x_arch_attribute = C6XABI_Tag_CPU_arch_C674X;
+
   /* Reset global settings for parallel bars and predicates now to
      avoid extra errors if there are problems with this opcode.  */
   this_line_parallel = tic6x_line_parallel;
@@ -3081,6 +3182,7 @@ md_assemble (char *str)
       tic6x_label_list *l;
 
       seginfo->tc_segment_info_data.spmask_addr = NULL;
+      seginfo->tc_segment_info_data.func_units_used = 0;
 
       /* Start a new frag for this execute packet.  */
       if (frag_now_fix () != 0)
@@ -3110,6 +3212,18 @@ md_assemble (char *str)
        = tic6x_can_cross_fp_boundary;
     }
 
+  if (func_unit_base != tic6x_func_unit_nfu)
+    {
+      unsigned int func_unit_enc;
+
+      func_unit_enc = tic6x_encode_spmask (func_unit_base, func_unit_side);
+
+      if (seginfo->tc_segment_info_data.func_units_used & func_unit_enc)
+       as_bad (_("functional unit already used in this execute packet"));
+
+      seginfo->tc_segment_info_data.func_units_used |= func_unit_enc;
+    }
+
   if (opct->flags & TIC6X_FLAG_SPLOOP)
     {
       if (seginfo->tc_segment_info_data.sploop_ii)
@@ -3760,6 +3874,29 @@ tic6x_frag_init (fragS *fragp)
   fragp->tc_frag_data.can_cross_fp_boundary = FALSE;
 }
 
+/* Set an attribute if it has not already been set by the user.  */
+
+static void
+tic6x_set_attribute_int (int tag, int value)
+{
+  if (tag < 1
+      || tag >= NUM_KNOWN_OBJ_ATTRIBUTES)
+    abort ();
+  if (!tic6x_attributes_set_explicitly[tag])
+    bfd_elf_add_proc_attr_int (stdoutput, tag, value);
+}
+
+/* Set object attributes deduced from the input file and command line
+   rather than given explicitly.  */
+static void
+tic6x_set_attributes (void)
+{
+  if (tic6x_arch_attribute == C6XABI_Tag_CPU_arch_none)
+    tic6x_arch_attribute = C6XABI_Tag_CPU_arch_C674X;
+
+  tic6x_set_attribute_int (Tag_C6XABI_Tag_CPU_arch, tic6x_arch_attribute);
+}
+
 /* Do machine-dependent manipulations of the frag chains after all
    input has been read and before the machine-independent sizing and
    relaxing.  */
@@ -3767,6 +3904,9 @@ tic6x_frag_init (fragS *fragp)
 void
 tic6x_end (void)
 {
+  /* Set object attributes at this point if not explicitly set.  */
+  tic6x_set_attributes ();
+
   /* Meeting alignment requirements may require inserting NOPs in
      parallel in execute packets earlier in the segment.  Future
      16-bit instruction generation involves whole-segment optimization
This page took 0.028726 seconds and 4 git commands to generate.