From b26fa4f93b784dc4e8f46d91ca3a5101b2ee5f49 Mon Sep 17 00:00:00 2001 From: Kuan-Lin Chen Date: Sat, 19 May 2018 11:03:20 +0000 Subject: [PATCH] [NDS32] Support PIC and TLS. 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): 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 From-SVN: r260393 --- gcc/ChangeLog | 22 ++ gcc/config/nds32/constants.md | 10 + gcc/config/nds32/nds32-doubleword.md | 2 +- gcc/config/nds32/nds32-dspext.md | 14 + gcc/config/nds32/nds32-md-auxiliary.c | 511 +++++++++++++++++++++----- gcc/config/nds32/nds32-predicates.c | 36 ++ gcc/config/nds32/nds32-protos.h | 25 +- gcc/config/nds32/nds32-relax-opt.c | 245 ++++++++++++ gcc/config/nds32/nds32.c | 185 +++++++++- gcc/config/nds32/nds32.h | 10 + gcc/config/nds32/nds32.md | 129 ++++++- gcc/config/nds32/predicates.md | 6 + 12 files changed, 1058 insertions(+), 137 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a5663e0eea5..b905b54f4f6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,25 @@ +2018-05-19 Kuan-Lin Chen + Chung-Ju Wu + + * 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): 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 * config/nds32/nds32-predicates.c (const_vector_to_hwint): Use machine diff --git a/gcc/config/nds32/constants.md b/gcc/config/nds32/constants.md index c4cde8d7ebe..c2994ab386a 100644 --- a/gcc/config/nds32/constants.md +++ b/gcc/config/nds32/constants.md @@ -23,6 +23,7 @@ (define_constants [(R8_REGNUM 8) (TA_REGNUM 15) + (TP_REGNUM 25) (FP_REGNUM 28) (GP_REGNUM 29) (LP_REGNUM 30) @@ -72,6 +73,14 @@ 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 diff --git a/gcc/config/nds32/nds32-doubleword.md b/gcc/config/nds32/nds32-doubleword.md index 4505337c3aa..7ee6489d034 100644 --- a/gcc/config/nds32/nds32-doubleword.md +++ b/gcc/config/nds32/nds32-doubleword.md @@ -139,7 +139,7 @@ (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))] { diff --git a/gcc/config/nds32/nds32-dspext.md b/gcc/config/nds32/nds32-dspext.md index 4151353370d..e3ae79c84fe 100644 --- a/gcc/config/nds32/nds32-dspext.md +++ b/gcc/config/nds32/nds32-dspext.md @@ -44,6 +44,20 @@ 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" diff --git a/gcc/config/nds32/nds32-md-auxiliary.c b/gcc/config/nds32/nds32-md-auxiliary.c index 99beff6d828..32b14dba6b8 100644 --- a/gcc/config/nds32/nds32-md-auxiliary.c +++ b/gcc/config/nds32/nds32-md-auxiliary.c @@ -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 (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 (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 ""; +} diff --git a/gcc/config/nds32/nds32-predicates.c b/gcc/config/nds32/nds32-predicates.c index 6561828c1d7..ba7770025e2 100644 --- a/gcc/config/nds32/nds32-predicates.c +++ b/gcc/config/nds32/nds32-predicates.c @@ -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) { diff --git a/gcc/config/nds32/nds32-protos.h b/gcc/config/nds32/nds32-protos.h index 4bf82f1f723..55988d5910f 100644 --- a/gcc/config/nds32/nds32-protos.h +++ b/gcc/config/nds32/nds32-protos.h @@ -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. */ diff --git a/gcc/config/nds32/nds32-relax-opt.c b/gcc/config/nds32/nds32-relax-opt.c index 0a878aaff9f..e54bd978c2e 100644 --- a/gcc/config/nds32/nds32-relax-opt.c +++ b/gcc/config/nds32/nds32-relax-opt.c @@ -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 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); } } diff --git a/gcc/config/nds32/nds32.c b/gcc/config/nds32/nds32.c index fe97854fe73..b705ae6a77a 100644 --- a/gcc/config/nds32/nds32.c +++ b/gcc/config/nds32/nds32.c @@ -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 ()); diff --git a/gcc/config/nds32/nds32.h b/gcc/config/nds32/nds32.h index 481c196e30c..90b52d1a286 100644 --- a/gcc/config/nds32/nds32.h +++ b/gcc/config/nds32/nds32.h @@ -36,6 +36,16 @@ #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 { diff --git a/gcc/config/nds32/nds32.md b/gcc/config/nds32/nds32.md index a4f204b0ed2..7e19dcd04f7 100644 --- a/gcc/config/nds32/nds32.md +++ b/gcc/config/nds32/nds32.md @@ -225,6 +225,16 @@ 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; + } } }) @@ -288,8 +298,8 @@ ;; 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") @@ -1555,9 +1565,11 @@ (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))) ])] ) @@ -1641,9 +1653,11 @@ (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))) ])] ) @@ -1731,9 +1745,11 @@ (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))) ])] ) @@ -1793,9 +1809,11 @@ (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))) ])] ) @@ -1993,6 +2011,7 @@ { rtx add_tmp; rtx reg, test; + rtx tmp_reg; /* Step A: "k <-- (plus (operands[0]) (-operands[1]))". */ if (operands[1] != const0_rtx) @@ -2014,9 +2033,14 @@ 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; }) @@ -2052,8 +2076,11 @@ 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)))]) ;; ---------------------------------------------------------------------------- @@ -2129,6 +2156,16 @@ [(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 @@ -2193,3 +2230,57 @@ }) ;; ---------------------------------------------------------------------------- + +;; 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")]) + +;; ---------------------------------------------------------------------------- diff --git a/gcc/config/nds32/predicates.md b/gcc/config/nds32/predicates.md index 59292da8328..ee4cf3cf48e 100644 --- a/gcc/config/nds32/predicates.md +++ b/gcc/config/nds32/predicates.md @@ -44,6 +44,12 @@ (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"))) -- 2.30.2