[NDS32] Support PIC and TLS.
authorKuan-Lin Chen <kuanlinchentw@gmail.com>
Sat, 19 May 2018 11:03:20 +0000 (11:03 +0000)
committerChung-Ju Wu <jasonwucj@gcc.gnu.org>
Sat, 19 May 2018 11:03:20 +0000 (11:03 +0000)
gcc/
* config/nds32/constants.md: Add TP_REGNUM constant.
(unspec_element): Add UNSPEC_GOTINIT, UNSPEC_GOT, UNSPEC_GOTOFF,
UNSPEC_PLT, UNSPEC_TLSGD, UNSPEC_TLSLD, UNSPEC_TLSIE, UNSPEC_TLSLE and
UNSPEC_ADD32.
* config/nds32/nds32-doubleword.md: Consider flag_pic.
* config/nds32/nds32-dspext.md (mov<mode>): Expand TLS and PIC cases.
* config/nds32/nds32-predicates.c (nds32_const_unspec_p): New.
* config/nds32/nds32-md-auxiliary.c: Implementation that support TLS
and PIC code generation.
* config/nds32/nds32-protos.h: Declarations that support TLS and PIC
code generation.
* config/nds32/nds32-relax-opt.c: Consider TLS and PIC for relax
optimization.
* config/nds32/nds32.md: Support TLS and PIC.
* config/nds32/nds32.c: Support TLS and PIC.
* config/nds32/nds32.h (nds32_relax_insn_type): New enum type.
* config/nds32/predicates.md (nds32_nonunspec_symbolic_operand): New
predicate.

Co-Authored-By: Chung-Ju Wu <jasonwucj@gmail.com>
From-SVN: r260393

12 files changed:
gcc/ChangeLog
gcc/config/nds32/constants.md
gcc/config/nds32/nds32-doubleword.md
gcc/config/nds32/nds32-dspext.md
gcc/config/nds32/nds32-md-auxiliary.c
gcc/config/nds32/nds32-predicates.c
gcc/config/nds32/nds32-protos.h
gcc/config/nds32/nds32-relax-opt.c
gcc/config/nds32/nds32.c
gcc/config/nds32/nds32.h
gcc/config/nds32/nds32.md
gcc/config/nds32/predicates.md

index a5663e0eea5356c898f4921dbecac9fd60fe1c9e..b905b54f4f60e0d8b572e61228e8cffc24cafaf0 100644 (file)
@@ -1,3 +1,25 @@
+2018-05-19  Kuan-Lin Chen <kuanlinchentw@gmail.com>
+           Chung-Ju Wu  <jasonwucj@gmail.com>
+
+       * config/nds32/constants.md: Add TP_REGNUM constant.
+       (unspec_element): Add UNSPEC_GOTINIT, UNSPEC_GOT, UNSPEC_GOTOFF,
+       UNSPEC_PLT, UNSPEC_TLSGD, UNSPEC_TLSLD, UNSPEC_TLSIE, UNSPEC_TLSLE and
+       UNSPEC_ADD32.
+       * config/nds32/nds32-doubleword.md: Consider flag_pic.
+       * config/nds32/nds32-dspext.md (mov<mode>): Expand TLS and PIC cases.
+       * config/nds32/nds32-predicates.c (nds32_const_unspec_p): New.
+       * config/nds32/nds32-md-auxiliary.c: Implementation that support TLS
+       and PIC code generation.
+       * config/nds32/nds32-protos.h: Declarations that support TLS and PIC
+       code generation.
+       * config/nds32/nds32-relax-opt.c: Consider TLS and PIC for relax
+       optimization.
+       * config/nds32/nds32.md: Support TLS and PIC.
+       * config/nds32/nds32.c: Support TLS and PIC.
+       * config/nds32/nds32.h (nds32_relax_insn_type): New enum type.
+       * config/nds32/predicates.md (nds32_nonunspec_symbolic_operand): New
+       predicate.
+
 2018-05-19  Chung-Ju Wu  <jasonwucj@gmail.com>
 
        * config/nds32/nds32-predicates.c (const_vector_to_hwint): Use machine
index c4cde8d7ebe3eebf259d8bbb9792179854a5c41d..c2994ab386a7e95d5e792bcf5dfd00cb470c4052 100644 (file)
@@ -23,6 +23,7 @@
 (define_constants
   [(R8_REGNUM  8)
    (TA_REGNUM 15)
+   (TP_REGNUM 25)
    (FP_REGNUM 28)
    (GP_REGNUM 29)
    (LP_REGNUM 30)
   UNSPEC_UASTORE_HW
   UNSPEC_UASTORE_W
   UNSPEC_UASTORE_DW
+  UNSPEC_GOTINIT
+  UNSPEC_GOT
+  UNSPEC_GOTOFF
+  UNSPEC_PLT
+  UNSPEC_TLSGD
+  UNSPEC_TLSLD
+  UNSPEC_TLSIE
+  UNSPEC_TLSLE
   UNSPEC_ROUND
   UNSPEC_VEC_COMPARE
   UNSPEC_KHM
@@ -83,6 +92,7 @@
   UNSPEC_LOOP_END
   UNSPEC_TLS_DESC
   UNSPEC_TLS_IE
+  UNSPEC_ADD32
   UNSPEC_ICT
   UNSPEC_KADDH
   UNSPEC_KSUBH
index 4505337c3aa3d46ae200e80587889adb216ee380..7ee6489d0341686c9c8f7cd398a9d082b0bc8221 100644 (file)
 (define_split
   [(set (match_operand:DIDF 0 "register_operand"     "")
        (match_operand:DIDF 1 "const_double_operand" ""))]
-  "reload_completed"
+  "flag_pic || reload_completed"
   [(set (match_dup 2) (match_dup 3))
    (set (match_dup 4) (match_dup 5))]
 {
index 4151353370ddab219a2bc031d53a0f723d664a66..e3ae79c84feb70c4141de19c700d8b9041500932 100644 (file)
       convert_move (operands[0], tmp_rtx, false);
       DONE;
     }
+
+  if (REG_P (operands[0]) && SYMBOLIC_CONST_P (operands[1]))
+    {
+      if (nds32_tls_referenced_p (operands [1]))
+       {
+         nds32_expand_tls_move (operands);
+         DONE;
+       }
+      else if (flag_pic)
+       {
+         nds32_expand_pic_move (operands);
+         DONE;
+       }
+    }
 })
 
 (define_insn "*mov<mode>"
index 99beff6d8288c29cc5af14ee7c8828000cc76cb2..32b14dba6b84a54a590fc0289dc101972164b7c9 100644 (file)
@@ -1364,6 +1364,112 @@ nds32_expand_insv (rtx *operands)
 
 /* ------------------------------------------------------------------------ */
 
+/* Function to generate PC relative jump table.
+   Refer to nds32.md for more details.
+
+   The following is the sample for the case that diff value
+   can be presented in '.short' size.
+
+     addi    $r1, $r1, -(case_lower_bound)
+     slti    $ta, $r1, (case_number)
+     beqz    $ta, .L_skip_label
+
+     la      $ta, .L35             ! get jump table address
+     lh      $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
+     addi    $ta, $r1, $ta
+     jr5     $ta
+
+     ! jump table entry
+   L35:
+     .short  .L25-.L35
+     .short  .L26-.L35
+     .short  .L27-.L35
+     .short  .L28-.L35
+     .short  .L29-.L35
+     .short  .L30-.L35
+     .short  .L31-.L35
+     .short  .L32-.L35
+     .short  .L33-.L35
+     .short  .L34-.L35 */
+const char *
+nds32_output_casesi_pc_relative (rtx *operands)
+{
+  machine_mode mode;
+  rtx diff_vec;
+
+  diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[1])));
+
+  gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+  /* Step C: "t <-- operands[1]".  */
+  if (flag_pic)
+    {
+      output_asm_insn ("sethi\t$ta, hi20(%l1@GOTOFF)", operands);
+      output_asm_insn ("ori\t$ta, $ta, lo12(%l1@GOTOFF)", operands);
+      output_asm_insn ("add\t$ta, $ta, $gp", operands);
+    }
+  else
+    output_asm_insn ("la\t$ta, %l1", operands);
+
+  /* Get the mode of each element in the difference vector.  */
+  mode = GET_MODE (diff_vec);
+
+  /* Step D: "z <-- (mem (plus (operands[0] << m) t))",
+     where m is 0, 1, or 2 to load address-diff value from table.  */
+  switch (mode)
+    {
+    case E_QImode:
+      output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands);
+      break;
+    case E_HImode:
+      output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands);
+      break;
+    case E_SImode:
+      output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Step E: "t <-- z + t".
+     Add table label_ref with address-diff value to
+     obtain target case address.  */
+  output_asm_insn ("add\t$ta, %2, $ta", operands);
+
+  /* Step F: jump to target with register t.  */
+  if (TARGET_16_BIT)
+    return "jr5\t$ta";
+  else
+    return "jr\t$ta";
+}
+
+/* Function to generate normal jump table.  */
+const char *
+nds32_output_casesi (rtx *operands)
+{
+  /* Step C: "t <-- operands[1]".  */
+  if (flag_pic)
+    {
+      output_asm_insn ("sethi\t$ta, hi20(%l1@GOTOFF)", operands);
+      output_asm_insn ("ori\t$ta, $ta, lo12(%l1@GOTOFF)", operands);
+      output_asm_insn ("add\t$ta, $ta, $gp", operands);
+    }
+  else
+    output_asm_insn ("la\t$ta, %l1", operands);
+
+  /* Step D: "z <-- (mem (plus (operands[0] << 2) t))".  */
+  output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
+
+  /* No need to perform Step E, which is only used for
+     pc relative jump table.  */
+
+  /* Step F: jump to target with register z.  */
+  if (TARGET_16_BIT)
+    return "jr5\t%2";
+  else
+    return "jr\t%2";
+}
+
 /* Function to return memory format.  */
 enum nds32_16bit_address_type
 nds32_mem_format (rtx op)
@@ -2189,77 +2295,6 @@ nds32_output_return (void)
   return "";
 }
 
-/* Function to generate PC relative jump table.
-   Refer to nds32.md for more details.
-
-   The following is the sample for the case that diff value
-   can be presented in '.short' size.
-
-     addi    $r1, $r1, -(case_lower_bound)
-     slti    $ta, $r1, (case_number)
-     beqz    $ta, .L_skip_label
-
-     la      $ta, .L35             ! get jump table address
-     lh      $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
-     addi    $ta, $r1, $ta
-     jr5     $ta
-
-     ! jump table entry
-   L35:
-     .short  .L25-.L35
-     .short  .L26-.L35
-     .short  .L27-.L35
-     .short  .L28-.L35
-     .short  .L29-.L35
-     .short  .L30-.L35
-     .short  .L31-.L35
-     .short  .L32-.L35
-     .short  .L33-.L35
-     .short  .L34-.L35 */
-const char *
-nds32_output_casesi_pc_relative (rtx *operands)
-{
-  machine_mode mode;
-  rtx diff_vec;
-
-  diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[1])));
-
-  gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
-
-  /* Step C: "t <-- operands[1]".  */
-  output_asm_insn ("la\t$ta, %l1", operands);
-
-  /* Get the mode of each element in the difference vector.  */
-  mode = GET_MODE (diff_vec);
-
-  /* Step D: "z <-- (mem (plus (operands[0] << m) t))",
-     where m is 0, 1, or 2 to load address-diff value from table.  */
-  switch (mode)
-    {
-    case E_QImode:
-      output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands);
-      break;
-    case E_HImode:
-      output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands);
-      break;
-    case E_SImode:
-      output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
-      break;
-    default:
-      gcc_unreachable ();
-    }
-
-  /* Step E: "t <-- z + t".
-     Add table label_ref with address-diff value to
-     obtain target case address.  */
-  output_asm_insn ("add\t$ta, %2, $ta", operands);
-
-  /* Step F: jump to target with register t.  */
-  if (TARGET_16_BIT)
-    return "jr5\t$ta";
-  else
-    return "jr\t$ta";
-}
 
 /* output a float load instruction */
 const char *
@@ -2417,25 +2452,6 @@ nds32_output_float_store (rtx *operands)
   return "";
 }
 
-/* Function to generate normal jump table.  */
-const char *
-nds32_output_casesi (rtx *operands)
-{
-  /* Step C: "t <-- operands[1]".  */
-  output_asm_insn ("la\t$ta, %l1", operands);
-
-  /* Step D: "z <-- (mem (plus (operands[0] << 2) t))".  */
-  output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
-
-  /* No need to perform Step E, which is only used for
-     pc relative jump table.  */
-
-  /* Step F: jump to target with register z.  */
-  if (TARGET_16_BIT)
-    return "jr5\t%2";
-  else
-    return "jr\t%2";
-}
 
 /* Auxiliary functions for lwm/smw.  */
 bool
@@ -2973,6 +2989,16 @@ nds32_output_unpkd8 (rtx output, rtx input,
   return "";
 }
 
+/* Return true if SYMBOL_REF X binds locally.  */
+
+static bool
+nds32_symbol_binds_local_p (const_rtx x)
+{
+  return (SYMBOL_REF_DECL (x)
+         ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+         : SYMBOL_REF_LOCAL_P (x));
+}
+
 const char *
 nds32_output_call (rtx insn, rtx *operands, rtx symbol, const char *long_call,
                   const char *call, bool align_p)
@@ -2980,22 +3006,15 @@ nds32_output_call (rtx insn, rtx *operands, rtx symbol, const char *long_call,
   char pattern[100];
   bool noreturn_p;
 
-  if (GET_CODE (symbol) == CONST)
-    {
-      symbol= XEXP (symbol, 0);
-
-      if (GET_CODE (symbol) == PLUS)
-        symbol = XEXP (symbol, 0);
-    }
-
-  gcc_assert (GET_CODE (symbol) == SYMBOL_REF
-             || REG_P (symbol));
-
   if (nds32_long_call_p (symbol))
     strcpy (pattern, long_call);
   else
     strcpy (pattern, call);
 
+  if (flag_pic && CONSTANT_P (symbol)
+      && !nds32_symbol_binds_local_p (symbol))
+    strcat (pattern, "@PLT");
+
   if (align_p)
     strcat (pattern, "\n\t.align 2");
 
@@ -3423,6 +3442,113 @@ symbolic_reference_mentioned_p (rtx op)
   return false;
 }
 
+/* Expand PIC code for @GOTOFF and @GOT.
+
+  Example for @GOTOFF:
+
+    la $r0, symbol@GOTOFF
+      -> sethi $ta, hi20(symbol@GOTOFF)
+        ori $ta, $ta, lo12(symbol@GOTOFF)
+        add $r0, $ta, $gp
+
+  Example for @GOT:
+
+    la $r0, symbol@GOT
+      -> sethi $ta, hi20(symbol@GOT)
+        ori $ta, $ta, lo12(symbol@GOT)
+        lw  $r0, [$ta + $gp]
+*/
+rtx
+nds32_legitimize_pic_address (rtx x)
+{
+  rtx addr = x;
+  rtx reg = gen_reg_rtx (Pmode);
+  rtx pat;
+
+  if (GET_CODE (x) == LABEL_REF
+      || (GET_CODE (x) == SYMBOL_REF
+         && (CONSTANT_POOL_ADDRESS_P (x)
+             || SYMBOL_REF_LOCAL_P (x))))
+    {
+      addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_GOTOFF);
+      addr = gen_rtx_CONST (SImode, addr);
+      emit_insn (gen_sethi (reg, addr));
+      emit_insn (gen_lo_sum (reg, reg, addr));
+      x = gen_rtx_PLUS (Pmode, reg, pic_offset_table_rtx);
+    }
+  else if (GET_CODE (x) == SYMBOL_REF)
+    {
+      addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_GOT);
+      addr = gen_rtx_CONST (SImode, addr);
+      emit_insn (gen_sethi (reg, addr));
+      emit_insn (gen_lo_sum (reg, reg, addr));
+
+      x = gen_const_mem (SImode, gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+                                              reg));
+    }
+  else if (GET_CODE (x) == CONST)
+    {
+      /* We don't split constant in expand_pic_move because GOTOFF can combine
+        the addend with the symbol.  */
+      addr = XEXP (x, 0);
+      gcc_assert (GET_CODE (addr) == PLUS);
+
+      rtx op0 = XEXP (addr, 0);
+      rtx op1 = XEXP (addr, 1);
+
+      if ((GET_CODE (op0) == LABEL_REF
+          || (GET_CODE (op0) == SYMBOL_REF
+              && (CONSTANT_POOL_ADDRESS_P (op0)
+                  || SYMBOL_REF_LOCAL_P (op0))))
+         && GET_CODE (op1) == CONST_INT)
+       {
+         pat = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), UNSPEC_GOTOFF);
+         pat = gen_rtx_PLUS (Pmode, pat, op1);
+         pat = gen_rtx_CONST (Pmode, pat);
+         emit_insn (gen_sethi (reg, pat));
+         emit_insn (gen_lo_sum (reg, reg, pat));
+         x = gen_rtx_PLUS (Pmode, reg, pic_offset_table_rtx);
+       }
+      else if (GET_CODE (op0) == SYMBOL_REF
+              && GET_CODE (op1) == CONST_INT)
+       {
+         /* This is a constant offset from a @GOT symbol reference.  */
+         addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, op0), UNSPEC_GOT);
+         addr = gen_rtx_CONST (SImode, addr);
+         emit_insn (gen_sethi (reg, addr));
+         emit_insn (gen_lo_sum (reg, reg, addr));
+         addr = gen_const_mem (SImode, gen_rtx_PLUS (Pmode,
+                                                     pic_offset_table_rtx,
+                                                     reg));
+         emit_move_insn (reg, addr);
+         if (satisfies_constraint_Is15 (op1))
+           x = gen_rtx_PLUS (Pmode, reg, op1);
+         else
+           {
+             rtx tmp_reg = gen_reg_rtx (SImode);
+             emit_insn (gen_movsi (tmp_reg, op1));
+             x = gen_rtx_PLUS (Pmode, reg, tmp_reg);
+           }
+       }
+      else
+       {
+         /* Don't handle this pattern.  */
+         debug_rtx (x);
+         gcc_unreachable ();
+       }
+    }
+  return x;
+}
+
+void
+nds32_expand_pic_move (rtx *operands)
+{
+  rtx src;
+
+  src = nds32_legitimize_pic_address (operands[1]);
+  emit_move_insn (operands[0], src);
+}
+
 /* Expand ICT symbol.
     Example for @ICT and ICT model=large:
 
@@ -3489,6 +3615,129 @@ nds32_long_call_p (rtx symbol)
     return TARGET_CMODEL_LARGE;
 }
 
+/* Return true if X contains a thread-local symbol.  */
+bool
+nds32_tls_referenced_p (rtx x)
+{
+  if (!targetm.have_tls)
+   return false;
+
+  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS)
+    x = XEXP (XEXP (x, 0), 0);
+
+  if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x))
+    return true;
+
+  return false;
+}
+
+/* ADDR contains a thread-local SYMBOL_REF.  Generate code to compute
+   this (thread-local) address.  */
+rtx
+nds32_legitimize_tls_address (rtx x)
+{
+  rtx tmp_reg;
+  rtx tp_reg = gen_rtx_REG (Pmode, TP_REGNUM);
+  rtx pat, insns, reg0;
+
+  if (GET_CODE (x) == SYMBOL_REF)
+    switch (SYMBOL_REF_TLS_MODEL (x))
+      {
+      case TLS_MODEL_GLOBAL_DYNAMIC:
+      case TLS_MODEL_LOCAL_DYNAMIC:
+       /* Emit UNSPEC_TLS_DESC rather than expand rtl directly because spill
+          may destroy the define-use chain anylysis to insert relax_hint.  */
+       if (SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_GLOBAL_DYNAMIC)
+         pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSGD);
+       else
+         pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSLD);
+
+       pat = gen_rtx_CONST (SImode, pat);
+       reg0 = gen_rtx_REG (Pmode, 0);
+       /* If we can confirm all clobber reigsters, it doesn't have to use call
+          instruction.  */
+       insns = emit_call_insn (gen_tls_desc (pat, GEN_INT (0)));
+       use_reg (&CALL_INSN_FUNCTION_USAGE (insns), pic_offset_table_rtx);
+       RTL_CONST_CALL_P (insns) = 1;
+       tmp_reg = gen_reg_rtx (SImode);
+       emit_move_insn (tmp_reg, reg0);
+       x = tmp_reg;
+       break;
+
+      case TLS_MODEL_INITIAL_EXEC:
+       pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSIE);
+       tmp_reg  = gen_reg_rtx (SImode);
+       pat = gen_rtx_CONST (SImode, pat);
+       emit_insn (gen_tls_ie (tmp_reg, pat, GEN_INT (0)));
+       if (flag_pic)
+         emit_use (pic_offset_table_rtx);
+       x = gen_rtx_PLUS (Pmode, tmp_reg, tp_reg);
+       break;
+
+      case TLS_MODEL_LOCAL_EXEC:
+       /* Expand symbol_ref@TPOFF':
+            sethi $ta, hi20(symbol_ref@TPOFF)
+            ori   $ta, $ta, lo12(symbol_ref@TPOFF)
+            add   $r0, $ta, $tp */
+       tmp_reg  = gen_reg_rtx (SImode);
+       pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_TLSLE);
+       pat = gen_rtx_CONST (SImode, pat);
+       emit_insn (gen_sethi (tmp_reg, pat));
+       emit_insn (gen_lo_sum (tmp_reg, tmp_reg, pat));
+       x = gen_rtx_PLUS (Pmode, tmp_reg, tp_reg);
+       break;
+
+      default:
+       gcc_unreachable ();
+      }
+  else if (GET_CODE (x) == CONST)
+    {
+      rtx base, addend;
+      split_const (x, &base, &addend);
+
+      if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_LOCAL_EXEC)
+       {
+         /* Expand symbol_ref@TPOFF':
+            sethi $ta, hi20(symbol_ref@TPOFF + addend)
+            ori   $ta, $ta, lo12(symbol_ref@TPOFF + addend)
+            add   $r0, $ta, $tp */
+         tmp_reg  = gen_reg_rtx (SImode);
+         pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, base), UNSPEC_TLSLE);
+         pat = gen_rtx_PLUS (SImode, pat, addend);
+         pat = gen_rtx_CONST (SImode, pat);
+         emit_insn (gen_sethi (tmp_reg, pat));
+         emit_insn (gen_lo_sum (tmp_reg, tmp_reg, pat));
+         x = gen_rtx_PLUS (Pmode, tmp_reg, tp_reg);
+       }
+    }
+
+  return x;
+}
+
+void
+nds32_expand_tls_move (rtx *operands)
+{
+  rtx src = operands[1];
+  rtx base, addend;
+
+  if (CONSTANT_P (src))
+    split_const (src, &base, &addend);
+
+  if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_LOCAL_EXEC)
+    src = nds32_legitimize_tls_address (src);
+  else
+    {
+      src = nds32_legitimize_tls_address (base);
+      if (addend != const0_rtx)
+       {
+         src = gen_rtx_PLUS (SImode, src, addend);
+         src = force_operand (src, operands[0]);
+       }
+    }
+
+  emit_move_insn (operands[0], src);
+}
+
 void
 nds32_expand_constant (machine_mode mode, HOST_WIDE_INT val,
                       rtx target, rtx source)
@@ -3553,3 +3802,63 @@ rtx nds32_di_low_part_subreg(rtx reg)
           SImode, reg,
           DImode, low_part_offset);
 }
+
+/* ------------------------------------------------------------------------ */
+
+/* Auxiliary function for output TLS patterns.  */
+
+const char *
+nds32_output_tls_desc (rtx *operands)
+{
+  char pattern[1000];
+
+  if (TARGET_RELAX_HINT)
+    snprintf (pattern, sizeof (pattern),
+             ".relax_hint %%1\n\tsethi $r0, hi20(%%0)\n\t"
+             ".relax_hint %%1\n\tori $r0, $r0, lo12(%%0)\n\t"
+             ".relax_hint %%1\n\tlw $r15, [$r0 + $gp]\n\t"
+             ".relax_hint %%1\n\tadd $r0, $r0, $gp\n\t"
+             ".relax_hint %%1\n\tjral $r15");
+  else
+    snprintf (pattern, sizeof (pattern),
+             "sethi $r0, hi20(%%0)\n\t"
+             "ori $r0, $r0, lo12(%%0)\n\t"
+             "lw $r15, [$r0 + $gp]\n\t"
+             "add $r0, $r0, $gp\n\t"
+             "jral $r15");
+  output_asm_insn (pattern, operands);
+  return "";
+}
+
+const char *
+nds32_output_tls_ie (rtx *operands)
+{
+  char pattern[1000];
+
+  if (flag_pic)
+  {
+      if (TARGET_RELAX_HINT)
+       snprintf (pattern, sizeof (pattern),
+                 ".relax_hint %%2\n\tsethi %%0, hi20(%%1)\n\t"
+                 ".relax_hint %%2\n\tori %%0, %%0, lo12(%%1)\n\t"
+                 ".relax_hint %%2\n\tlw %%0, [%%0 + $gp]");
+      else
+       snprintf (pattern, sizeof (pattern),
+                 "sethi %%0, hi20(%%1)\n\t"
+                 "ori %%0, %%0, lo12(%%1)\n\t"
+                 "lw %%0, [%%0 + $gp]");
+  }
+  else
+    {
+      if (TARGET_RELAX_HINT)
+       snprintf (pattern, sizeof (pattern),
+                 ".relax_hint %%2\n\tsethi %%0, hi20(%%1)\n\t"
+                 ".relax_hint %%2\n\tlwi %%0, [%%0 + lo12(%%1)]");
+      else
+       snprintf (pattern, sizeof (pattern),
+                 "sethi %%0, hi20(%%1)\n\t"
+                 "lwi %%0, [%%0 + lo12(%%1)]");
+    }
+  output_asm_insn (pattern, operands);
+  return "";
+}
index 6561828c1d734ccb9e539f5406bd121df5a4ba5c..ba7770025e2eda389a861effc00fa4b10eb1e390 100644 (file)
@@ -519,6 +519,42 @@ nds32_const_double_range_ok_p (rtx op, machine_mode mode,
   return val >= lower && val < upper;
 }
 
+bool
+nds32_const_unspec_p (rtx x)
+{
+  if (GET_CODE (x) == CONST)
+    {
+      x = XEXP (x, 0);
+
+      if (GET_CODE (x) == PLUS)
+       x = XEXP (x, 0);
+
+      if (GET_CODE (x) == UNSPEC)
+       {
+         switch (XINT (x, 1))
+           {
+           case UNSPEC_GOTINIT:
+           case UNSPEC_GOT:
+           case UNSPEC_GOTOFF:
+           case UNSPEC_PLT:
+           case UNSPEC_TLSGD:
+           case UNSPEC_TLSLD:
+           case UNSPEC_TLSIE:
+           case UNSPEC_TLSLE:
+             return false;
+           default:
+             return true;
+           }
+       }
+    }
+
+  if (GET_CODE (x) == SYMBOL_REF
+      && SYMBOL_REF_TLS_MODEL (x))
+    return false;
+
+  return true;
+}
+
 HOST_WIDE_INT
 const_vector_to_hwint (rtx op)
 {
index 4bf82f1f723d96002a4ec42843a6cebb4d8663cd..55988d5910f0e34d86aa37809d6ea75fa8af3b0a 100644 (file)
@@ -151,6 +151,8 @@ extern int nds32_can_use_bitci_p (int);
 extern bool nds32_const_double_range_ok_p (rtx, machine_mode,
                                           HOST_WIDE_INT, HOST_WIDE_INT);
 
+extern bool nds32_const_unspec_p (rtx x);
+
 /* Auxiliary function for 'Computing the Length of an Insn'.  */
 
 extern int nds32_adjust_insn_length (rtx_insn *, int);
@@ -183,6 +185,26 @@ extern void nds32_expand_float_movcc (rtx *);
 extern enum nds32_expand_result_type nds32_expand_extv (rtx *);
 extern enum nds32_expand_result_type nds32_expand_insv (rtx *);
 
+/* Auxiliary functions for expand PIC instruction.  */
+
+extern void nds32_expand_pic_move (rtx *);
+
+/* Auxiliary functions to legitimize PIC address.  */
+
+extern rtx nds32_legitimize_pic_address (rtx);
+
+/* Auxiliary functions for expand TLS instruction.  */
+
+extern void nds32_expand_tls_move (rtx *);
+
+/* Auxiliary functions to legitimize TLS address.  */
+
+extern rtx nds32_legitimize_tls_address (rtx);
+
+/* Auxiliary functions to identify thread-local symbol.  */
+
+extern bool nds32_tls_referenced_p (rtx);
+
 /* Auxiliary functions for expand ICT instruction.  */
 
 extern void nds32_expand_ict_move (rtx *);
@@ -236,7 +258,8 @@ extern const char *nds32_output_unpkd8 (rtx, rtx, rtx, rtx, bool);
 
 extern const char *nds32_output_call (rtx, rtx *, rtx,
                                      const char *, const char *, bool);
-
+extern const char *nds32_output_tls_desc (rtx *);
+extern const char *nds32_output_tls_ie (rtx *);
 
 /* Auxiliary functions to output stack push/pop instruction.  */
 
index 0a878aaff9fb11f0bd146f31cb44c718f5922466..e54bd978c2e73742f2c69876ec4f5bcac680f8cd 100644 (file)
@@ -52,6 +52,8 @@
 #include "cfgrtl.h"
 #include "tree-pass.h"
 
+using namespace nds32;
+
 /* This is used to create unique relax hint id value.
    The initial value is 0.  */
 static int relax_group_id = 0;
@@ -196,6 +198,110 @@ nds32_ict_const_p (rtx x)
     }
   return FALSE;
 }
+
+/* Group the following pattern as relax candidates:
+
+   GOT:
+      sethi    $ra, hi20(sym)
+      ori      $ra, $ra, lo12(sym)
+      lw       $rb, [$ra + $gp]
+
+   GOTOFF, TLSLE:
+      sethi    $ra, hi20(sym)
+      ori      $ra, $ra, lo12(sym)
+      LS       $rb, [$ra + $gp]
+
+   GOTOFF, TLSLE:
+      sethi    $ra, hi20(sym)
+      ori      $ra, $ra, lo12(sym)
+      add      $rb, $ra, $gp($tp)
+
+   Initial GOT table:
+      sethi    $gp,hi20(sym)
+      ori      $gp, $gp, lo12(sym)
+      add5.pc  $gp  */
+
+static auto_vec<rtx_insn *, 32> nds32_group_infos;
+/* Group the PIC and TLS relax candidate instructions for linker.  */
+static bool
+nds32_pic_tls_group (rtx_insn *def_insn,
+                    enum nds32_relax_insn_type relax_type,
+                    int sym_type)
+{
+  df_ref def_record;
+  df_link *link;
+  rtx_insn *use_insn = NULL;
+  rtx pat, new_pat;
+  def_record = DF_INSN_DEFS (def_insn);
+  for (link = DF_REF_CHAIN (def_record); link; link = link->next)
+    {
+      if (!DF_REF_INSN_INFO (link->ref))
+       continue;
+
+      use_insn = DF_REF_INSN (link->ref);
+
+      /* Skip if define insn and use insn not in the same basic block.  */
+      if (!dominated_by_p (CDI_DOMINATORS,
+                          BLOCK_FOR_INSN (use_insn),
+                          BLOCK_FOR_INSN (def_insn)))
+       return FALSE;
+
+      /* Skip if use_insn not active insn.  */
+      if (!active_insn_p (use_insn))
+       return FALSE;
+
+      switch (relax_type)
+       {
+       case RELAX_ORI:
+
+         /* GOTOFF, TLSLE:
+            sethi      $ra, hi20(sym)
+            ori        $ra, $ra, lo12(sym)
+            add        $rb, $ra, $gp($tp)  */
+         if ((sym_type == UNSPEC_TLSLE
+              || sym_type == UNSPEC_GOTOFF)
+             && (recog_memoized (use_insn) == CODE_FOR_addsi3))
+           {
+             pat = XEXP (PATTERN (use_insn), 1);
+             new_pat =
+               gen_rtx_UNSPEC (SImode,
+                               gen_rtvec (2, XEXP (pat, 0), XEXP (pat, 1)),
+                               UNSPEC_ADD32);
+             validate_replace_rtx (pat, new_pat, use_insn);
+             nds32_group_infos.safe_push (use_insn);
+           }
+         else if (nds32_plus_reg_load_store_p (use_insn)
+                  && !nds32_sp_base_or_plus_load_store_p (use_insn))
+           nds32_group_infos.safe_push (use_insn);
+         else
+           return FALSE;
+         break;
+
+       default:
+         return FALSE;
+       }
+    }
+  return TRUE;
+}
+
+static int
+nds32_pic_tls_symbol_type (rtx x)
+{
+  x = XEXP (SET_SRC (PATTERN (x)), 1);
+
+  if (GET_CODE (x) == CONST)
+    {
+      x = XEXP (x, 0);
+
+      if (GET_CODE (x) == PLUS)
+       x = XEXP (x, 0);
+
+      return XINT (x, 1);
+    }
+
+  return XINT (x, 1);
+}
+
 /* Group the relax candidates with group id.  */
 static void
 nds32_group_insns (rtx sethi)
@@ -204,6 +310,7 @@ nds32_group_insns (rtx sethi)
   df_link *link;
   rtx_insn *use_insn = NULL;
   rtx group_id;
+  bool valid;
 
   def_record = DF_INSN_DEFS (sethi);
 
@@ -253,6 +360,132 @@ nds32_group_insns (rtx sethi)
       /* Insert .relax_* directive.  */
       if (active_insn_p (use_insn))
        emit_insn_before (gen_relax_group (group_id), use_insn);
+
+      /* Find ori ra, ra, unspec(symbol) instruction.  */
+      if (use_insn != NULL
+         && recog_memoized (use_insn) == CODE_FOR_lo_sum
+         && !nds32_const_unspec_p (XEXP (SET_SRC (PATTERN (use_insn)), 1)))
+       {
+         int sym_type = nds32_pic_tls_symbol_type (use_insn);
+         valid = nds32_pic_tls_group (use_insn, RELAX_ORI, sym_type);
+
+         /* Insert .relax_* directive.  */
+         while (!nds32_group_infos.is_empty ())
+           {
+             use_insn = nds32_group_infos.pop ();
+             if (valid)
+               emit_insn_before (gen_relax_group (group_id), use_insn);
+           }
+       }
+    }
+
+  relax_group_id++;
+}
+
+/* Convert relax group id in rtl.  */
+
+static void
+nds32_group_tls_insn (rtx insn)
+{
+  rtx pat = PATTERN (insn);
+  rtx unspec_relax_group = XEXP (XVECEXP (pat, 0, 1), 0);
+
+  while (GET_CODE (pat) != SET && GET_CODE (pat) == PARALLEL)
+    {
+      pat = XVECEXP (pat, 0, 0);
+    }
+
+  if (GET_CODE (unspec_relax_group) == UNSPEC
+      && XINT (unspec_relax_group, 1) == UNSPEC_VOLATILE_RELAX_GROUP)
+    {
+      XVECEXP (unspec_relax_group, 0, 0) = GEN_INT (relax_group_id);
+    }
+
+  relax_group_id++;
+}
+
+static bool
+nds32_float_reg_load_store_p (rtx_insn *insn)
+{
+  rtx pat = PATTERN (insn);
+
+  if (get_attr_type (insn) == TYPE_FLOAD
+      && GET_CODE (pat) == SET
+      && (GET_MODE (XEXP (pat, 0)) == SFmode
+         || GET_MODE (XEXP (pat, 0)) == DFmode)
+      && MEM_P (XEXP (pat, 1)))
+    {
+      rtx addr = XEXP (XEXP (pat, 1), 0);
+
+      /* [$ra] */
+      if (REG_P (addr))
+       return true;
+      /* [$ra + offset] */
+      if (GET_CODE (addr) == PLUS
+         && REG_P (XEXP (addr, 0))
+         && CONST_INT_P (XEXP (addr, 1)))
+       return true;
+    }
+  return false;
+}
+
+
+/* Group float load-store instructions:
+   la $ra, symbol
+   flsi $rt, [$ra + offset] */
+
+static void
+nds32_group_float_insns (rtx insn)
+{
+  df_ref def_record, use_record;
+  df_link *link;
+  rtx_insn *use_insn = NULL;
+  rtx group_id;
+
+  def_record = DF_INSN_DEFS (insn);
+
+  for (link = DF_REF_CHAIN (def_record); link; link = link->next)
+    {
+      if (!DF_REF_INSN_INFO (link->ref))
+       continue;
+
+      use_insn = DF_REF_INSN (link->ref);
+
+      /* Skip if define insn and use insn not in the same basic block.  */
+      if (!dominated_by_p (CDI_DOMINATORS,
+                          BLOCK_FOR_INSN (use_insn),
+                          BLOCK_FOR_INSN (insn)))
+       return;
+
+      /* Skip if the low-part used register is from different high-part
+        instructions.  */
+      use_record = DF_INSN_USES (use_insn);
+      if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next)
+       return;
+
+      /* Skip if use_insn not active insn.  */
+      if (!active_insn_p (use_insn))
+       return;
+
+      if (!nds32_float_reg_load_store_p (use_insn)
+         || find_post_update_rtx (use_insn) != -1)
+       return;
+    }
+
+  group_id = GEN_INT (relax_group_id);
+  /* Insert .relax_* directive for insn.  */
+  emit_insn_before (gen_relax_group (group_id), insn);
+
+  /* Scan the use insns and insert the directive.  */
+  for (link = DF_REF_CHAIN (def_record); link; link = link->next)
+    {
+      if (!DF_REF_INSN_INFO (link->ref))
+       continue;
+
+      use_insn = DF_REF_INSN (link->ref);
+
+      /* Insert .relax_* directive.  */
+       emit_insn_before (gen_relax_group (group_id), use_insn);
     }
 
   relax_group_id++;
@@ -285,6 +518,18 @@ nds32_relax_group (void)
                                         SImode)
              && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0)))
            nds32_group_insns (insn);
+         else if (recog_memoized (insn) == CODE_FOR_tls_ie)
+           nds32_group_tls_insn (insn);
+         else if (TARGET_FPU_SINGLE
+                  && recog_memoized (insn) == CODE_FOR_move_addr
+                  && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0)))
+           {
+             nds32_group_float_insns (insn);
+           }
+       }
+      else if (CALL_P (insn) && recog_memoized (insn) == CODE_FOR_tls_desc)
+       {
+         nds32_group_tls_insn (insn);
        }
     }
 
index fe97854fe739ccad1afcf089aada719d547ddcb9..b705ae6a77aac8b24fec62174a66ebd9faf1a973 100644 (file)
@@ -435,7 +435,8 @@ nds32_compute_stack_frame (void)
 
   /* If $gp value is required to be saved on stack, it needs 4 bytes space.
      Check whether we are using PIC code genration.  */
-  cfun->machine->gp_size = (flag_pic) ? 4 : 0;
+  cfun->machine->gp_size =
+    (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) ? 4 : 0;
 
   /* If $lp value is required to be saved on stack, it needs 4 bytes space.
      Check whether $lp is ever live.  */
@@ -520,7 +521,8 @@ nds32_compute_stack_frame (void)
          && cfun->machine->callee_saved_last_fpr_regno == SP_REGNUM
          && !df_regs_ever_live_p (FP_REGNUM)
          && !df_regs_ever_live_p (LP_REGNUM)
-         && cfun->machine->local_size == 0))
+         && cfun->machine->local_size == 0
+         && !flag_pic))
     {
       /* Set this function 'naked_p' and other functions can check this flag.
         Note that in nds32 port, the 'naked_p = 1' JUST means there is no
@@ -1262,6 +1264,32 @@ nds32_emit_stack_v3pop (unsigned Rb,
   REG_NOTES (parallel_insn) = dwarf;
 }
 
+static void
+nds32_emit_load_gp (void)
+{
+  rtx got_symbol, pat;
+
+  /* Initial GLOBAL OFFSET TABLE don't do the scheduling.  */
+  emit_insn (gen_blockage ());
+
+  got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+  /* sethi $gp, _GLOBAL_OFFSET_TABLE_ -8 */
+  pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, got_symbol), UNSPEC_GOTINIT);
+  pat = gen_rtx_CONST (SImode, gen_rtx_PLUS (Pmode, pat, GEN_INT (-8)));
+  emit_insn (gen_sethi (pic_offset_table_rtx,pat));
+
+  /* ori $gp, $gp, _GLOBAL_OFFSET_TABLE_ -4 */
+  pat = gen_rtx_UNSPEC (SImode, gen_rtvec (1, got_symbol), UNSPEC_GOTINIT);
+  pat = gen_rtx_CONST (SImode, gen_rtx_PLUS (Pmode, pat, GEN_INT (-4)));
+  emit_insn (gen_lo_sum (pic_offset_table_rtx, pic_offset_table_rtx, pat));
+
+  /* add5.pc $gp */
+  emit_insn (gen_add_pc (pic_offset_table_rtx, pic_offset_table_rtx));
+
+  /* Initial GLOBAL OFFSET TABLE don't do the scheduling.  */
+  emit_insn (gen_blockage ());
+}
+
 /* Function that may creates more instructions
    for large value on adjusting stack pointer.
 
@@ -2213,6 +2241,26 @@ nds32_asm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
                ? 1
                : 0);
 
+  if (flag_pic)
+    {
+      fprintf (file, "\tsmw.adm\t$r31, [$r31], $r31, 4\n");
+      fprintf (file, "\tsethi\t%s, hi20(_GLOBAL_OFFSET_TABLE_-8)\n",
+                     reg_names [PIC_OFFSET_TABLE_REGNUM]);
+      fprintf (file, "\tori\t%s, %s, lo12(_GLOBAL_OFFSET_TABLE_-4)\n",
+                     reg_names [PIC_OFFSET_TABLE_REGNUM],
+                     reg_names [PIC_OFFSET_TABLE_REGNUM]);
+
+      if (TARGET_ISA_V3)
+       fprintf (file, "\tadd5.pc\t$gp\n");
+      else
+       {
+         fprintf (file, "\tmfusr\t$ta, $pc\n");
+         fprintf (file, "\tadd\t%s, $ta, %s\n",
+                         reg_names [PIC_OFFSET_TABLE_REGNUM],
+                         reg_names [PIC_OFFSET_TABLE_REGNUM]);
+       }
+    }
+
   if (delta != 0)
     {
       if (satisfies_constraint_Is15 (GEN_INT (delta)))
@@ -2237,9 +2285,23 @@ nds32_asm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
        }
     }
 
-  fprintf (file, "\tb\t");
-  assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
-  fprintf (file, "\n");
+  if (flag_pic)
+    {
+      fprintf (file, "\tla\t$ta, ");
+      assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
+      fprintf (file, "@PLT\n");
+      fprintf (file, "\t! epilogue\n");
+      fprintf (file, "\tlwi.bi\t%s, [%s], 4\n",
+              reg_names[PIC_OFFSET_TABLE_REGNUM],
+              reg_names[STACK_POINTER_REGNUM]);
+      fprintf (file, "\tbr\t$ta\n");
+    }
+  else
+    {
+      fprintf (file, "\tb\t");
+      assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
+      fprintf (file, "\n");
+    }
 
   final_end_function ();
 }
@@ -2260,10 +2322,12 @@ nds32_function_ok_for_sibcall (tree decl,
      3. We don't want to apply sibling call optimization for indirect
        sibcall because the pop behavior in epilogue may pollute the
        content of caller-saved regsiter when the register is used for
-       indirect sibcall.  */
+       indirect sibcall.
+     4. In pic mode, it may use some registers for PLT call.  */
   return (!TARGET_V3PUSH
          && (cfun->machine->va_args_size == 0)
-         && decl);
+         && decl
+         && !flag_pic);
 }
 
 /* Determine whether we need to enable warning for function return check.  */
@@ -2579,6 +2643,10 @@ nds32_legitimate_address_p (machine_mode mode, rtx x, bool strict)
 
     case SYMBOL_REF:
       /* (mem (symbol_ref A)) => [symbol_ref] */
+
+      if (flag_pic || SYMBOL_REF_TLS_MODEL (x))
+       return false;
+
       if (TARGET_ICT_MODEL_LARGE && nds32_indirect_call_referenced_p (x))
        return false;
 
@@ -2593,7 +2661,8 @@ nds32_legitimate_address_p (machine_mode mode, rtx x, bool strict)
         the 'symbol_ref' is not a valid address during or after
         LRA/reload phase.  */
       if (TARGET_CMODEL_MEDIUM
-         && NDS32_SYMBOL_REF_RODATA_P (x)
+         && (NDS32_SYMBOL_REF_RODATA_P (x)
+             || CONSTANT_POOL_ADDRESS_P (x))
          && (reload_completed
              || reload_in_progress
              || lra_in_progress))
@@ -2615,6 +2684,10 @@ nds32_legitimate_address_p (machine_mode mode, rtx x, bool strict)
            {
              /* Now we see the [ + const_addr ] pattern, but we need
                 some further checking.  */
+
+             if (flag_pic)
+               return false;
+
              /* If -mcmodel=large, the 'const_addr' is not a valid address
                 during or after LRA/reload phase.  */
              if (TARGET_CMODEL_LARGE
@@ -2692,9 +2765,18 @@ nds32_legitimate_address_p (machine_mode mode, rtx x, bool strict)
     case LO_SUM:
       /* (mem (lo_sum (reg) (symbol_ref))) */
       /* (mem (lo_sum (reg) (const (plus (symbol_ref) (reg)))) */
+      /* TLS case: (mem (lo_sum (reg) (const (unspec symbol_ref X)))) */
+      /* The LO_SUM is a valid address if and only if we would like to
+        generate 32-bit full address memory access with any of following
+        circumstance:
+          1. -mcmodel=large.
+          2. -mcmodel=medium and the symbol_ref references to rodata.  */
       {
        rtx sym = NULL_RTX;
 
+       if (flag_pic)
+         return false;
+
        if (!REG_P (XEXP (x, 0)))
          return false;
 
@@ -2736,7 +2818,11 @@ nds32_legitimize_address (rtx x,
                          rtx oldx ATTRIBUTE_UNUSED,
                          machine_mode mode ATTRIBUTE_UNUSED)
 {
-  if (TARGET_ICT_MODEL_LARGE && nds32_indirect_call_referenced_p (x))
+  if (nds32_tls_referenced_p (x))
+    x = nds32_legitimize_tls_address (x);
+  else if (flag_pic && SYMBOLIC_CONST_P (x))
+    x = nds32_legitimize_pic_address (x);
+  else if (TARGET_ICT_MODEL_LARGE && nds32_indirect_call_referenced_p (x))
     x = nds32_legitimize_ict_address (x);
 
   return x;
@@ -2766,6 +2852,13 @@ nds32_legitimate_constant_p (machine_mode mode, rtx x)
        {
          switch (XINT (x, 1))
            {
+           case UNSPEC_GOT:
+           case UNSPEC_GOTOFF:
+           case UNSPEC_PLT:
+           case UNSPEC_TLSGD:
+           case UNSPEC_TLSLD:
+           case UNSPEC_TLSIE:
+           case UNSPEC_TLSLE:
            case UNSPEC_ICT:
              return false;
            default:
@@ -2773,6 +2866,12 @@ nds32_legitimate_constant_p (machine_mode mode, rtx x)
            }
        }
       break;
+    case SYMBOL_REF:
+      /* TLS symbols need a call to resolve in
+        precompute_register_parameters.  */
+      if (SYMBOL_REF_TLS_MODEL (x))
+       return false;
+      break;
     default:
       return true;
     }
@@ -2798,6 +2897,14 @@ nds32_delegitimize_address (rtx x)
        {
          switch (XINT (inner, 1))
            {
+           case UNSPEC_GOTINIT:
+           case UNSPEC_GOT:
+           case UNSPEC_GOTOFF:
+           case UNSPEC_PLT:
+           case UNSPEC_TLSGD:
+           case UNSPEC_TLSLD:
+           case UNSPEC_TLSIE:
+           case UNSPEC_TLSLE:
            case UNSPEC_ICT:
              x = XVECEXP (inner, 0, 0);
              break;
@@ -2838,11 +2945,17 @@ nds32_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
       /* We don't want to force symbol as constant pool in .text section,
         because we use the gp-relatived instruction to load in small
         or medium model.  */
-      if (SYMBOL_REF_TLS_MODEL (x)
+      if (flag_pic
+         || SYMBOL_REF_TLS_MODEL (x)
          || TARGET_CMODEL_SMALL
          || TARGET_CMODEL_MEDIUM)
        return true;
       break;
+    case CONST_INT:
+    case CONST_DOUBLE:
+      if (flag_pic && (lra_in_progress || reload_completed))
+       return true;
+      break;
     default:
       return false;
     }
@@ -2986,6 +3099,9 @@ nds32_asm_file_start (void)
 {
   default_file_start ();
 
+  if (flag_pic)
+    fprintf (asm_out_file, "\t.pic\n");
+
   /* Tell assembler which ABI we are using.  */
   fprintf (asm_out_file, "\t! ABI version\n");
   if (TARGET_HARD_FLOAT)
@@ -3102,6 +3218,37 @@ nds32_asm_output_addr_const_extra (FILE *file, rtx x)
     {
       switch (XINT (x, 1))
        {
+       case UNSPEC_GOTINIT:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         break;
+       case UNSPEC_GOTOFF:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@GOTOFF", file);
+         break;
+       case UNSPEC_GOT:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@GOT", file);
+         break;
+       case UNSPEC_PLT:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@PLT", file);
+         break;
+       case UNSPEC_TLSGD:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@TLSDESC", file);
+         break;
+       case UNSPEC_TLSLD:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@TLSDESC", file);
+         break;
+       case UNSPEC_TLSIE:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@GOTTPOFF", file);
+         break;
+       case UNSPEC_TLSLE:
+         output_addr_const (file, XVECEXP (x, 0, 0));
+         fputs ("@TPOFF", file);
+         break;
        case UNSPEC_ICT:
          output_addr_const (file, XVECEXP (x, 0, 0));
          fputs ("@ICT", file);
@@ -3870,6 +4017,9 @@ nds32_option_override (void)
       target_flags &= ~MASK_EXT_PERF2;
       /* Under V3M ISA, we need to strictly disable TARGET_EXT_STRING.  */
       target_flags &= ~MASK_EXT_STRING;
+
+      if (flag_pic)
+       error ("not support -fpic option for v3m toolchain");
     }
 
   /* See if we are using reduced-set registers:
@@ -3903,9 +4053,6 @@ nds32_option_override (void)
               "must be enable '-mext-fpu-sp' or '-mext-fpu-dp'");
     }
 
-  /* Currently, we don't support PIC code generation yet.  */
-  if (flag_pic)
-    sorry ("position-independent code not supported");
 
   nds32_register_passes ();
 }
@@ -4328,7 +4475,7 @@ nds32_expand_prologue (void)
 
   /* If the function is 'naked',
      we do not have to generate prologue code fragment.  */
-  if (cfun->machine->naked_p)
+  if (cfun->machine->naked_p && !flag_pic)
     return;
 
   /* Get callee_first_regno and callee_last_regno.  */
@@ -4457,6 +4604,10 @@ nds32_expand_prologue (void)
                               -1 * sp_adjust);
     }
 
+  /* Emit gp setup instructions for -fpic.  */
+  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+    nds32_emit_load_gp ();
+
   /* Prevent the instruction scheduler from
      moving instructions across the boundary.  */
   emit_insn (gen_blockage ());
@@ -4700,7 +4851,7 @@ nds32_expand_prologue_v3push (void)
 
   /* If the function is 'naked',
      we do not have to generate prologue code fragment.  */
-  if (cfun->machine->naked_p)
+  if (cfun->machine->naked_p && !flag_pic)
     return;
 
   /* Get callee_first_regno and callee_last_regno.  */
@@ -4828,6 +4979,10 @@ nds32_expand_prologue_v3push (void)
                               -1 * sp_adjust);
     }
 
+  /* Emit gp setup instructions for -fpic.  */
+  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+    nds32_emit_load_gp ();
+
   /* Prevent the instruction scheduler from
      moving instructions across the boundary.  */
   emit_insn (gen_blockage ());
index 481c196e30cdc0009918b8dd558565a3c989c961..90b52d1a286535ce863434245f6fae4a2f88345e 100644 (file)
 #define NDS32_SYMBOL_REF_RODATA_P(x) \
   ((SYMBOL_REF_FLAGS (x) & NDS32_SYMBOL_FLAG_RODATA) != 0)
 
+enum nds32_relax_insn_type
+{
+  RELAX_ORI,
+  RELAX_PLT_ADD,
+  RELAX_TLS_ADD_or_LW,
+  RELAX_TLS_ADD_LW,
+  RELAX_TLS_LW_JRAL,
+  RELAX_DONE
+};
+
 /* Classifies expand result for expand helper function.  */
 enum nds32_expand_result_type
 {
index a4f204b0ed24e301c5325da1e0358c7ab13348ad..7e19dcd04f7e249fce90cefe9be05b91fa7309c3 100644 (file)
          nds32_expand_ict_move (operands);
          DONE;
        }
+      else if (nds32_tls_referenced_p (operands [1]))
+       {
+         nds32_expand_tls_move (operands);
+         DONE;
+       }
+      else if (flag_pic)
+       {
+         nds32_expand_pic_move (operands);
+         DONE;
+       }
     }
 })
 
 ;; We use nds32_symbolic_operand to limit that only CONST/SYMBOL_REF/LABEL_REF
 ;; are able to match such instruction template.
 (define_insn "move_addr"
-  [(set (match_operand:SI 0 "register_operand"       "=l, r")
-       (match_operand:SI 1 "nds32_symbolic_operand" " i, i"))]
+  [(set (match_operand:SI 0 "nds32_general_register_operand"   "=l, r")
+       (match_operand:SI 1 "nds32_nonunspec_symbolic_operand" " i, i"))]
   ""
   "la\t%0, %1"
   [(set_attr "type"  "alu")
                     (const_int 2)
                     (const_int 4))
        ;; Alternative 1
-       (if_then_else (match_test "nds32_long_call_p (operands[0])")
-                    (const_int 12)
-                    (const_int 4))
+       (if_then_else (match_test "flag_pic")
+                    (const_int 16)
+                    (if_then_else (match_test "nds32_long_call_p (operands[0])")
+                                  (const_int 12)
+                                  (const_int 4)))
      ])]
 )
 
                     (const_int 2)
                     (const_int 4))
        ;; Alternative 1
-       (if_then_else (match_test "nds32_long_call_p (operands[1])")
-                    (const_int 12)
-                    (const_int 4))
+       (if_then_else (match_test "flag_pic")
+                    (const_int 16)
+                    (if_then_else (match_test "nds32_long_call_p (operands[1])")
+                                  (const_int 12)
+                                  (const_int 4)))
      ])]
 )
 
                     (const_int 2)
                     (const_int 4))
        ;; Alternative 1
-       (if_then_else (match_test "nds32_long_call_p (operands[0])")
-                    (const_int 12)
-                    (const_int 4))
+       (if_then_else (match_test "flag_pic")
+                    (const_int 16)
+                    (if_then_else (match_test "nds32_long_call_p (operands[0])")
+                                  (const_int 12)
+                                  (const_int 4)))
      ])]
 )
 
                     (const_int 2)
                     (const_int 4))
        ;; Alternative 1
-       (if_then_else (match_test "nds32_long_call_p (operands[1])")
-                    (const_int 12)
-                    (const_int 4))
+       (if_then_else (match_test "flag_pic")
+                    (const_int 16)
+                    (if_then_else (match_test "nds32_long_call_p (operands[1])")
+                                  (const_int 12)
+                                  (const_int 4)))
      ])]
 )
 
 {
   rtx add_tmp;
   rtx reg, test;
+  rtx tmp_reg;
 
   /* Step A: "k <-- (plus (operands[0]) (-operands[1]))".  */
   if (operands[1] != const0_rtx)
   emit_jump_insn (gen_cbranchsi4 (test, operands[0], operands[2],
                                  operands[4]));
 
-  /* Step C, D, E, and F, using another temporary register.  */
-  rtx tmp = gen_reg_rtx (SImode);
-  emit_jump_insn (gen_casesi_internal (operands[0], operands[3], tmp));
+  tmp_reg = gen_reg_rtx (SImode);
+  /* Step C, D, E, and F, using another temporary register tmp_reg.  */
+  if (flag_pic)
+    emit_use (pic_offset_table_rtx);
+
+  emit_jump_insn (gen_casesi_internal (operands[0],
+                                      operands[3],
+                                      tmp_reg));
   DONE;
 })
 
   else
     return nds32_output_casesi (operands);
 }
-  [(set_attr "length" "20")
-   (set_attr "type" "branch")])
+  [(set_attr "type" "branch")
+   (set (attr "length")
+       (if_then_else (match_test "flag_pic")
+                     (const_int 28)
+                     (const_int 20)))])
 
 ;; ----------------------------------------------------------------------------
 
   [(set_attr "length" "0")]
 )
 
+;; Add pc
+(define_insn "add_pc"
+  [(set (match_operand:SI 0 "register_operand"          "=r")
+       (plus:SI (match_operand:SI 1 "register_operand"  "0")
+                (pc)))]
+  "flag_pic"
+  "add5.pc\t%0"
+  [(set_attr "type"    "alu")
+   (set_attr "length"    "4")]
+)
 ;; ----------------------------------------------------------------------------
 
 ;; Patterns for exception handling
 })
 
 ;; ----------------------------------------------------------------------------
+
+;; Patterns for TLS.
+;; The following two tls patterns don't be expanded directly because the
+;; intermediate value may be spilled into the stack.  As a result, it is
+;; hard to analyze the define-use chain in the relax_opt pass.
+
+
+;; There is a unspec operand to record RELAX_GROUP number because each
+;; emitted instruction need a relax_hint above it.
+(define_insn "tls_desc"
+  [(set (reg:SI 0)
+       (call (unspec_volatile:SI [(match_operand:SI 0 "nds32_symbolic_operand" "i")] UNSPEC_TLS_DESC)
+             (const_int 1)))
+   (use (unspec [(match_operand:SI 1 "immediate_operand" "i")] UNSPEC_VOLATILE_RELAX_GROUP))
+   (use (reg:SI GP_REGNUM))
+   (clobber (reg:SI LP_REGNUM))
+   (clobber (reg:SI TA_REGNUM))]
+  ""
+  {
+    return nds32_output_tls_desc (operands);
+  }
+  [(set_attr "length" "20")
+   (set_attr "type" "branch")]
+)
+
+;; There is a unspec operand to record RELAX_GROUP number because each
+;; emitted instruction need a relax_hint above it.
+(define_insn "tls_ie"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (unspec:SI [(match_operand:SI 1 "nds32_symbolic_operand" "i")] UNSPEC_TLS_IE))
+   (use (unspec [(match_operand:SI 2 "immediate_operand" "i")] UNSPEC_VOLATILE_RELAX_GROUP))
+   (use (reg:SI GP_REGNUM))]
+  ""
+  {
+    return nds32_output_tls_ie (operands);
+  }
+  [(set (attr "length") (if_then_else (match_test "flag_pic")
+                                     (const_int 12)
+                                     (const_int 8)))
+   (set_attr "type" "misc")]
+)
+
+;; The pattern is for some relaxation groups that have to keep addsi3 in 32-bit mode.
+(define_insn "addsi3_32bit"
+  [(set (match_operand:SI 0 "register_operand"             "=r")
+       (unspec:SI [(match_operand:SI 1 "register_operand" "%r")
+                   (match_operand:SI 2 "register_operand" " r")] UNSPEC_ADD32))]
+  ""
+  "add\t%0, %1, %2";
+  [(set_attr "type"    "alu")
+   (set_attr "length"  "4")
+   (set_attr "feature" "v1")])
+
+;; ----------------------------------------------------------------------------
index 59292da8328814418387cdc8e06920730cefb7fd..ee4cf3cf48efa0729fb1a21a373d320d2d6c783e 100644 (file)
        (match_test "!(TARGET_ICT_MODEL_LARGE
                      && nds32_indirect_call_referenced_p (op))")))
 
+(define_predicate "nds32_nonunspec_symbolic_operand"
+  (and (match_code "const,symbol_ref,label_ref")
+       (match_test "!flag_pic && nds32_const_unspec_p (op)
+                   && !(TARGET_ICT_MODEL_LARGE
+                        && nds32_indirect_call_referenced_p (op))")))
+
 (define_predicate "nds32_reg_constant_operand"
   (ior (match_operand 0 "register_operand")
        (match_operand 0 "const_int_operand")))