Jakub Jelinek <jj@ultra.linux.cz>
authorRichard Henderson <rth@redhat.com>
Mon, 7 Jun 1999 12:53:56 +0000 (12:53 +0000)
committerRichard Henderson <rth@redhat.com>
Mon, 7 Jun 1999 12:53:56 +0000 (12:53 +0000)
        * config/tc-sparc.c (md_assemble): Fix up setx, support setsw.
        Optimize set if sizeof(bfd_vma) == 64.
        (sparc_ip): Fix sethi - without %hi() it should generate
        R_SPARC_32 reloc, not R_SPARC_HI22.
        (tc_gen_reloc): Handle BFD_RELOC_SPARC22.

gas/ChangeLog
gas/config/tc-sparc.c

index 58118bb..3bbfa3f 100644 (file)
@@ -1,5 +1,13 @@
 1999-06-07  Jakub Jelinek  <jj@ultra.linux.cz>
 
+       * config/tc-sparc.c (md_assemble): Fix up setx, support setsw.
+       Optimize set if sizeof(bfd_vma) == 64.
+       (sparc_ip): Fix sethi - without %hi() it should generate
+       R_SPARC_32 reloc, not R_SPARC_HI22.
+       (tc_gen_reloc): Handle BFD_RELOC_SPARC22.
+
+1999-06-07  Jakub Jelinek  <jj@ultra.linux.cz>
+
        * config/tc-sparc.c (md_begin): Handle native wordsize aliases.
        (s_ncons): New function.
        (native_op_table): New table.
index d483956..7810d61 100644 (file)
@@ -913,6 +913,7 @@ static int special_case;
 /* Bit masks of various insns.  */
 #define NOP_INSN 0x01000000
 #define OR_INSN 0x80100000
+#define XOR_INSN 0x80180000
 #define FMOVS_INSN 0x81A00020
 #define SETHI_INSN 0x01000000
 #define SLLX_INSN 0x81281000
@@ -964,222 +965,295 @@ md_assemble (str)
       as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
     }
 
-  switch (special_case)
-    {
-    case SPECIAL_CASE_NONE:
-      /* normal insn */
-      output_insn (insn, &the_insn);
-      break;
-
-    case SPECIAL_CASE_SET:
-      {
-       int need_hi22_p = 0;
+  for (;;)
+    {    
+      switch (special_case)
+       {
+       case SPECIAL_CASE_NONE:
+         /* normal insn */
+         output_insn (insn, &the_insn);
+         return;
 
-       /* "set" is not defined for negative numbers in v9: it doesn't yield
-          what you expect it to.  */
-       if (SPARC_OPCODE_ARCH_V9_P (max_architecture)
-           && the_insn.exp.X_op == O_constant)
-         {
-           if (the_insn.exp.X_add_number < 0)
-             as_warn (_("set: used with negative number"));
-           else if (the_insn.exp.X_add_number > (offsetT) 0xffffffff)
-             as_warn (_("set: number larger than 4294967295"));
-         }
+       case SPECIAL_CASE_SETSW:
+         if (the_insn.exp.X_op == O_constant)
+           {
+             int low32;
+             if (the_insn.exp.X_add_number < -(offsetT)0x80000000
+                 || the_insn.exp.X_add_number > (offsetT) 0xffffffff)
+               as_warn (_("setsw: number not in -2147483648..4294967295 range"));
+
+             low32 = the_insn.exp.X_add_number;        
+             
+             if (low32 < 0)
+               {
+                 int rd = (the_insn.opcode & RD (~0)) >> 25;
+                 int opc = OR_INSN;
+    
+                 the_insn.reloc = BFD_RELOC_NONE;
+                 /* See if operand is absolute and small; skip sethi if so.  */
+                 if (low32 < -(1 << 12))
+                   {
+                     the_insn.opcode = (SETHI_INSN | RD (rd)
+                                       | (((~the_insn.exp.X_add_number) >> 10) & 0x3fffff));
+                     output_insn (insn, &the_insn);
+                     low32 = 0x1c00 | (low32 & 0x3ff);
+                     opc = RS1 (rd) | XOR_INSN;
+                   }
 
-       /* See if operand is absolute and small; skip sethi if so.  */
-       if (the_insn.exp.X_op != O_constant
-           || the_insn.exp.X_add_number >= (1 << 12)
-           || the_insn.exp.X_add_number < -(1 << 12))
-         {
-           output_insn (insn, &the_insn);
-           need_hi22_p = 1;
-         }
-       /* See if operand has no low-order bits; skip OR if so.  */
-       if (the_insn.exp.X_op != O_constant
-           || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
-           || ! need_hi22_p)
+                 the_insn.opcode = (opc | RD (rd) | IMMED
+                                    | (low32 & 0x1fff));
+                 output_insn (insn, &the_insn);
+                 return;
+               }
+           }
+         /* FALLTHROUGH */
+    
+       case SPECIAL_CASE_SET:
          {
+           int need_hi22_p = 0;
            int rd = (the_insn.opcode & RD (~0)) >> 25;
-           the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
-                              | RD (rd)
-                              | IMMED
-                              | (the_insn.exp.X_add_number
-                                 & (need_hi22_p ? 0x3ff : 0x1fff)));
-           the_insn.reloc = (the_insn.exp.X_op != O_constant
-                             ? BFD_RELOC_LO10
-                             : BFD_RELOC_NONE);
-           output_insn (insn, &the_insn);
-         }
-       break;
-      }
 
-    case SPECIAL_CASE_SETSW:
-      {
-       /* FIXME: Not finished.  */
-       break;
-      }
-
-    case SPECIAL_CASE_SETX:
-      {
-#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
-       int upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
-       int lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
-#undef SIGNEXT32
-       int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
-       int dstreg = (the_insn.opcode & RD (~0)) >> 25;
-       /* Output directly to dst reg if lower 32 bits are all zero.  */
-       int upper_dstreg = (the_insn.exp.X_op == O_constant
-                           && lower32 == 0) ? dstreg : tmpreg;
-       int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
-
-       /* The tmp reg should not be the dst reg.  */
-       if (tmpreg == dstreg)
-         as_warn (_("setx: temporary register same as destination register"));
-
-       /* Reset X_add_number, we've extracted it as upper32/lower32.
-          Otherwise fixup_segment will complain about not being able to
-          write an 8 byte number in a 4 byte field.  */
-       the_insn.exp.X_add_number = 0;
-
-       /* ??? Obviously there are other optimizations we can do
-          (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
-          doing some of these.  Later.  If you do change things, try to
-          change all of this to be table driven as well.  */
-
-       /* What to output depends on the number if it's constant.
-          Compute that first, then output what we've decided upon.  */
-       if (the_insn.exp.X_op != O_constant)
-         need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
-       else
-         {
-           /* Only need hh22 if `or' insn can't handle constant.  */
-           if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
-             need_hh22_p = 1;
-
-           /* Does bottom part (after sethi) have bits?  */
-           if ((need_hh22_p && (upper32 & 0x3ff) != 0)
-               /* No hh22, but does upper32 still have bits we can't set
-                  from lower32?  */
-               || (! need_hh22_p
-                   && upper32 != 0
-                   && (upper32 != -1 || lower32 >= 0)))
-             need_hm10_p = 1;
-
-           /* If the lower half is all zero, we build the upper half directly
-              into the dst reg.  */
-           if (lower32 != 0
-               /* Need lower half if number is zero.  */
-               || (! need_hh22_p && ! need_hm10_p))
+           if (the_insn.exp.X_op == O_constant)
              {
-               /* No need for sethi if `or' insn can handle constant.  */
-               if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
-                   /* Note that we can't use a negative constant in the `or'
-                      insn unless the upper 32 bits are all ones.  */
-                   || (lower32 < 0 && upper32 != -1))
-                 need_hi22_p = 1;
-
-               /* Does bottom part (after sethi) have bits?  */
-               if ((need_hi22_p && (lower32 & 0x3ff) != 0)
-                   /* No sethi.  */
-                   || (! need_hi22_p && (lower32 & 0x1fff) != 0)
-                   /* Need `or' if we didn't set anything else.  */
-                   || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
-                 need_lo10_p = 1;
+               if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+                 {
+                   if (the_insn.exp.X_add_number < 0
+                       || the_insn.exp.X_add_number > (offsetT) 0xffffffff)
+                     as_warn (_("set: number not in 0..4294967295 range"));
+                 }
+               else
+                 {
+                   if (the_insn.exp.X_add_number < (offsetT)-0x80000000
+                       || the_insn.exp.X_add_number > (offsetT) 0xffffffff)
+                     as_warn (_("set: number not in -2147483648..4294967295 range"));
+                   if (the_insn.exp.X_add_number >= (offsetT)0x80000000)
+                     the_insn.exp.X_add_number -= (offsetT)0x100000000;
+                 }
+             }
+    
+           /* See if operand is absolute and small; skip sethi if so.  */
+           if (the_insn.exp.X_op != O_constant
+               || the_insn.exp.X_add_number >= (1 << 12)
+               || the_insn.exp.X_add_number < -(1 << 12))
+             {
+               the_insn.opcode = (SETHI_INSN | RD (rd)
+                                  | ((the_insn.exp.X_add_number >> 10)
+                                     & the_insn.exp.X_op == O_constant ? 0x3fffff : 0));
+               the_insn.reloc = BFD_RELOC_HI22;
+               output_insn (insn, &the_insn);
+               need_hi22_p = 1;
              }
-         }
-
-       if (need_hh22_p)
-         {
-           the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
-                              | ((upper32 >> 10) & 0x3fffff));
-           the_insn.reloc = (the_insn.exp.X_op != O_constant
-                             ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
-           output_insn (insn, &the_insn);
-         }
 
-       if (need_hm10_p)
-         {
-           the_insn.opcode = (OR_INSN
-                              | (need_hh22_p ? RS1 (upper_dstreg) : 0)
-                              | RD (upper_dstreg)
-                              | IMMED
-                              | (upper32
-                                 & (need_hh22_p ? 0x3ff : 0x1fff)));
-           the_insn.reloc = (the_insn.exp.X_op != O_constant
-                             ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
-           output_insn (insn, &the_insn);
-         }
+           /* See if operand has no low-order bits; skip OR if so.  */
+           if (the_insn.exp.X_op != O_constant
+               || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
+               || ! need_hi22_p)
+             {
+               the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
+                                  | RD (rd)
+                                  | IMMED
+                                  | (the_insn.exp.X_add_number
+                                     & (the_insn.exp.X_op != O_constant ? 0 :
+                                        need_hi22_p ? 0x3ff : 0x1fff)));
+               the_insn.reloc = (the_insn.exp.X_op != O_constant
+                                 ? BFD_RELOC_LO10
+                                 : BFD_RELOC_NONE);
+               output_insn (insn, &the_insn);
+             }
 
-       if (need_hi22_p)
-         {
-           the_insn.opcode = (SETHI_INSN | RD (dstreg)
-                              | ((lower32 >> 10) & 0x3fffff));
-           the_insn.reloc = BFD_RELOC_HI22;
-           output_insn (insn, &the_insn);
+           if (special_case == SPECIAL_CASE_SETSW
+               && the_insn.exp.X_op != O_constant)
+             {
+               /* Need to sign extend it.  */
+               the_insn.opcode = (SRA_INSN | RS1 (rd) | RD (rd));
+               the_insn.reloc = BFD_RELOC_NONE;
+               output_insn (insn, &the_insn);
+             }
+           return;
          }
-
-       if (need_lo10_p)
+    
+       case SPECIAL_CASE_SETX:
          {
-           /* FIXME: One nice optimization to do here is to OR the low part
-              with the highpart if hi22 isn't needed and the low part is
-              positive.  */
-           the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
-                              | RD (dstreg)
-                              | IMMED
-                              | (lower32
-                                 & (need_hi22_p ? 0x3ff : 0x1fff)));
-           the_insn.reloc = BFD_RELOC_LO10;
-           output_insn (insn, &the_insn);
-         }
+           int upper32, lower32;
+           int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
+           int dstreg = (the_insn.opcode & RD (~0)) >> 25;
+           int upper_dstreg;
+           int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
+           int need_xor10_p = 0;
+    
+    #define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
+           lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
+           upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
+    #undef SIGNEXT32
+
+           upper_dstreg = tmpreg;
+           /* The tmp reg should not be the dst reg.  */
+           if (tmpreg == dstreg)
+             as_warn (_("setx: temporary register same as destination register"));
+
+           /* ??? Obviously there are other optimizations we can do
+              (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
+              doing some of these.  Later.  If you do change things, try to
+              change all of this to be table driven as well.  */
+    
+           /* What to output depends on the number if it's constant.
+              Compute that first, then output what we've decided upon.  */
+           if (the_insn.exp.X_op != O_constant)
+             {
+               if (sparc_arch_size == 32)
+                 {
+                   /* When arch size is 32, we want setx to be equivalent
+                      to setuw for anything but constants.  */
+                   the_insn.exp.X_add_number &= 0xffffffff;
+                   special_case = SPECIAL_CASE_SET;
+                   continue;
+                 }
+               need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
+               lower32 = 0; upper32 = 0;
+             }
+           else
+             {
+               /* Reset X_add_number, we've extracted it as upper32/lower32.
+                  Otherwise fixup_segment will complain about not being able to
+                  write an 8 byte number in a 4 byte field.  */
+               the_insn.exp.X_add_number = 0;
+    
+               /* Only need hh22 if `or' insn can't handle constant.  */
+               if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
+                 need_hh22_p = 1;
+    
+               /* Does bottom part (after sethi) have bits?  */
+               if ((need_hh22_p && (upper32 & 0x3ff) != 0)
+                   /* No hh22, but does upper32 still have bits we can't set
+                      from lower32?  */
+                   || (! need_hh22_p && upper32 != 0 && upper32 != -1))
+                 need_hm10_p = 1;
+    
+               /* If the lower half is all zero, we build the upper half directly
+                  into the dst reg.  */
+               if (lower32 != 0
+                   /* Need lower half if number is zero or 0xffffffff00000000.  */
+                   || (! need_hh22_p && ! need_hm10_p))
+                 {
+                   /* No need for sethi if `or' insn can handle constant.  */
+                   if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
+                       /* Note that we can't use a negative constant in the `or'
+                          insn unless the upper 32 bits are all ones.  */
+                       || (lower32 < 0 && upper32 != -1)
+                       || (lower32 >= 0 && upper32 == -1))
+                     need_hi22_p = 1;
+                     
+                   if (need_hi22_p && upper32 == -1)
+                     need_xor10_p = 1;
+                   /* Does bottom part (after sethi) have bits?  */
+                   else if ((need_hi22_p && (lower32 & 0x3ff) != 0)
+                       /* No sethi.  */
+                       || (! need_hi22_p && (lower32 & 0x1fff) != 0)
+                       /* Need `or' if we didn't set anything else.  */
+                       || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
+                     need_lo10_p = 1;
+                 }
+               else
+                 /* Output directly to dst reg if lower 32 bits are all
+                    zero.  */
+                 upper_dstreg = dstreg;
+             }
+    
+           if (need_hh22_p)
+             {
+               the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
+                                  | ((upper32 >> 10) & 0x3fffff));
+               the_insn.reloc = (the_insn.exp.X_op != O_constant
+                                 ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
+               output_insn (insn, &the_insn);
+             }
+    
+           if (need_hi22_p)
+             {
+               the_insn.opcode = (SETHI_INSN | RD (dstreg)
+                                  | (((need_xor10_p ? ~lower32 : lower32)
+                                      >> 10) & 0x3fffff));
+               the_insn.reloc = (the_insn.exp.X_op != O_constant
+                                 ? BFD_RELOC_SPARC_LM22 : BFD_RELOC_NONE);
+               output_insn (insn, &the_insn);
+             }
 
-       /* If we needed to build the upper part, shift it into place.  */
-       if (need_hh22_p || need_hm10_p)
-         {
-           the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
-                              | IMMED | 32);
-           the_insn.reloc = BFD_RELOC_NONE;
-           output_insn (insn, &the_insn);
+           if (need_hm10_p)
+             {
+               the_insn.opcode = (OR_INSN
+                                  | (need_hh22_p ? RS1 (upper_dstreg) : 0)
+                                  | RD (upper_dstreg)
+                                  | IMMED
+                                  | (upper32
+                                     & (need_hh22_p ? 0x3ff : 0x1fff)));
+               the_insn.reloc = (the_insn.exp.X_op != O_constant
+                                 ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
+               output_insn (insn, &the_insn);
+             }
+    
+           if (need_lo10_p)
+             {
+               /* FIXME: One nice optimization to do here is to OR the low part
+                  with the highpart if hi22 isn't needed and the low part is
+                  positive.  */
+               the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
+                                  | RD (dstreg)
+                                  | IMMED
+                                  | (lower32
+                                     & (need_hi22_p ? 0x3ff : 0x1fff)));
+               the_insn.reloc = BFD_RELOC_LO10;
+               output_insn (insn, &the_insn);
+             }
+    
+           /* If we needed to build the upper part, shift it into place.  */
+           if (need_hh22_p || need_hm10_p)
+             {
+               the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
+                                  | IMMED | 32);
+               the_insn.reloc = BFD_RELOC_NONE;
+               output_insn (insn, &the_insn);
+             }
+    
+           /* To get -1 in upper32, we do sethi %hi(~x), r; xor r, -0x400 | x, r.  */
+           if (need_xor10_p)
+             {
+               the_insn.opcode = (XOR_INSN | RS1 (dstreg) | RD (dstreg) | IMMED
+                                  | 0x1c00 | (lower32 & 0x3ff));
+               the_insn.reloc = BFD_RELOC_NONE;
+               output_insn (insn, &the_insn);
+             }
+           /* If we needed to build both upper and lower parts, OR them together.  */
+           else if ((need_hh22_p || need_hm10_p)
+                    && (need_hi22_p || need_lo10_p))
+             {
+               the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
+                                  | RD (dstreg));
+               the_insn.reloc = BFD_RELOC_NONE;
+               output_insn (insn, &the_insn);
+             }
+           return;
          }
-
-       /* If we needed to build both upper and lower parts, OR them together.  */
-       if ((need_hh22_p || need_hm10_p)
-           && (need_hi22_p || need_lo10_p))
+    
+       case SPECIAL_CASE_FDIV:
          {
-           the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
-                              | RD (dstreg));
-           the_insn.reloc = BFD_RELOC_NONE;
+           int rd = (the_insn.opcode >> 25) & 0x1f;
+    
            output_insn (insn, &the_insn);
-         }
-       /* We didn't need both regs, but we may have to sign extend lower32.  */
-       else if (need_hi22_p && upper32 == -1)
-         {
-           the_insn.opcode = (SRA_INSN | RS1 (dstreg) | RD (dstreg)
-                              | IMMED | 0);
-           the_insn.reloc = BFD_RELOC_NONE;
+    
+           /* According to information leaked from Sun, the "fdiv" instructions
+              on early SPARC machines would produce incorrect results sometimes.
+              The workaround is to add an fmovs of the destination register to
+              itself just after the instruction.  This was true on machines
+              with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
+           assert (the_insn.reloc == BFD_RELOC_NONE);
+           the_insn.opcode = FMOVS_INSN | rd | RD (rd);
            output_insn (insn, &the_insn);
+           return;
          }
-       break;
-      }
-
-    case SPECIAL_CASE_FDIV:
-      {
-       int rd = (the_insn.opcode >> 25) & 0x1f;
-
-       output_insn (insn, &the_insn);
-
-       /* According to information leaked from Sun, the "fdiv" instructions
-          on early SPARC machines would produce incorrect results sometimes.
-          The workaround is to add an fmovs of the destination register to
-          itself just after the instruction.  This was true on machines
-          with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
-       assert (the_insn.reloc == BFD_RELOC_NONE);
-       the_insn.opcode = FMOVS_INSN | rd | RD (rd);
-       output_insn (insn, &the_insn);
-       break;
-      }
-
-    default:
-      as_fatal (_("failed special case insn sanity check"));
+    
+       default:
+         as_fatal (_("failed special case insn sanity check"));
+       }
     }
 }
 
@@ -1891,14 +1965,10 @@ sparc_ip (str, pinsn)
                }
              break;
 
-           case '0':           /* 64 bit immediate (setx insn) */
+           case '0':           /* 64 bit immediate (set, setsw, setx insn) */
              the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */
              goto immediate;
 
-           case 'h':           /* high 22 bits */
-             the_insn.reloc = BFD_RELOC_HI22;
-             goto immediate;
-
            case 'l':           /* 22 bit PC relative immediate */
              the_insn.reloc = BFD_RELOC_SPARC_WDISP22;
              the_insn.pcrel = 1;
@@ -1909,6 +1979,7 @@ sparc_ip (str, pinsn)
              the_insn.pcrel = 1;
              goto immediate;
 
+           case 'h':
            case 'n':           /* 22 bit immediate */
              the_insn.reloc = BFD_RELOC_SPARC22;
              goto immediate;
@@ -2968,6 +3039,7 @@ tc_gen_reloc (section, fixp)
     case BFD_RELOC_LO10:
     case BFD_RELOC_32_PCREL_S2:
     case BFD_RELOC_SPARC13:
+    case BFD_RELOC_SPARC22:
     case BFD_RELOC_SPARC_BASE13:
     case BFD_RELOC_SPARC_WDISP16:
     case BFD_RELOC_SPARC_WDISP19: