+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
(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
UNSPEC_LOOP_END
UNSPEC_TLS_DESC
UNSPEC_TLS_IE
+ UNSPEC_ADD32
UNSPEC_ICT
UNSPEC_KADDH
UNSPEC_KSUBH
(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))]
{
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>"
/* ------------------------------------------------------------------------ */
+/* 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)
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 *
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
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)
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");
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:
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)
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 "";
+}
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)
{
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);
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 *);
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. */
#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;
}
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)
df_link *link;
rtx_insn *use_insn = NULL;
rtx group_id;
+ bool valid;
def_record = DF_INSN_DEFS (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++;
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);
}
}
/* 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. */
&& 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
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.
? 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)))
}
}
- 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 ();
}
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. */
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;
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))
{
/* 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
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;
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;
{
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:
}
}
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;
}
{
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;
/* 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;
}
{
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)
{
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);
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:
"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 ();
}
/* 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. */
-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 ());
/* 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. */
-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 ());
#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
{
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")])
+
+;; ----------------------------------------------------------------------------
(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")))