relocations. */
switch (*symbol_type)
{
- case SYMBOL_PCREL:
case SYMBOL_PCREL64:
+ /* When the code model is extreme, the non-zero offset situation
+ has not been handled well, so it is disabled here now. */
+ if (!loongarch_explicit_relocs_p (SYMBOL_PCREL64))
+ return false;
+ /* fall through */
+ case SYMBOL_PCREL:
/* GAS rejects offsets outside the range [-2^31, 2^31-1]. */
return sext_hwi (INTVAL (offset), 32) == INTVAL (offset);
/* Load an entry for a TLS access. */
static rtx
-loongarch_load_tls (rtx dest, rtx sym)
+loongarch_load_tls (rtx dest, rtx sym, enum loongarch_symbol_type type)
{
- return gen_load_tls (Pmode, dest, sym);
+ /* TLS LE gets a 32 or 64 bit offset here, so one register can do it. */
+ if (type == SYMBOL_TLS_LE)
+ return gen_load_tls (Pmode, dest, sym);
+
+ return loongarch_symbol_extreme_p (type)
+ ? gen_movdi_symbolic_off64 (dest, sym, gen_reg_rtx (DImode))
+ : gen_load_tls (Pmode, dest, sym);
}
/* Return an instruction sequence that calls __tls_get_addr. SYM is
if (TARGET_CMODEL_EXTREME)
{
- gcc_assert (TARGET_EXPLICIT_RELOCS);
-
rtx tmp1 = gen_reg_rtx (Pmode);
emit_insn (gen_tls_low (Pmode, tmp1, gen_rtx_REG (Pmode, 0), loc));
emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loc));
emit_insn (gen_tls_low (Pmode, a0, high, loc));
}
else
- emit_insn (loongarch_load_tls (a0, loc));
+ emit_insn (loongarch_load_tls (a0, loc, type));
if (flag_plt)
{
case CMODEL_EXTREME:
{
- gcc_assert (TARGET_EXPLICIT_RELOCS);
-
- rtx tmp1 = gen_reg_rtx (Pmode);
- rtx high = gen_reg_rtx (Pmode);
-
- loongarch_emit_move (high,
- gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
- loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
- gen_rtx_REG (Pmode, 0),
- loongarch_tls_symbol));
- emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
- emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
- loongarch_emit_move (dest,
- gen_rtx_MEM (Pmode,
- gen_rtx_PLUS (Pmode,
- high, tmp1)));
+ if (loongarch_explicit_relocs_p (SYMBOL_GOT_DISP))
+ {
+ rtx tmp1 = gen_reg_rtx (Pmode);
+ rtx high = gen_reg_rtx (Pmode);
+
+ loongarch_emit_move (high,
+ gen_rtx_HIGH (Pmode,
+ loongarch_tls_symbol));
+ loongarch_emit_move (tmp1,
+ gen_rtx_LO_SUM (Pmode,
+ gen_rtx_REG (Pmode, 0),
+ loongarch_tls_symbol));
+ emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
+ emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
+ loongarch_emit_move (dest,
+ gen_rtx_MEM (Pmode,
+ gen_rtx_PLUS (Pmode,
+ high, tmp1)));
+ }
+ else
+ emit_insn (gen_movdi_symbolic_off64 (dest, loongarch_tls_symbol,
+ gen_reg_rtx (DImode)));
}
break;
if (TARGET_CMODEL_EXTREME)
{
- gcc_assert (TARGET_EXPLICIT_RELOCS);
-
rtx tmp3 = gen_reg_rtx (Pmode);
emit_insn (gen_tls_low (Pmode, tmp3,
gen_rtx_REG (Pmode, 0), tmp2));
emit_insn (gen_ld_from_got (Pmode, tmp1, high, tmp2));
}
else
- emit_insn (loongarch_load_tls (tmp1, tmp2));
+ emit_insn (loongarch_load_tls (tmp1, tmp2, SYMBOL_TLS_IE));
emit_insn (gen_add3_insn (dest, tmp1, tp));
}
break;
if (TARGET_CMODEL_EXTREME)
{
- gcc_assert (TARGET_EXPLICIT_RELOCS);
-
emit_insn (gen_lui_h_lo20 (tmp1, tmp1, tmp2));
emit_insn (gen_lui_h_hi12 (tmp1, tmp1, tmp2));
}
}
else
- emit_insn (loongarch_load_tls (tmp1, tmp2));
+ emit_insn (loongarch_load_tls (tmp1, tmp2, SYMBOL_TLS_LE));
emit_insn (gen_add3_insn (dest, tmp1, tp));
}
break;
return x;
}
-static bool
+bool
loongarch_symbol_extreme_p (enum loongarch_symbol_type type)
{
switch (type)
return true;
}
+ /* Obtain the address of the symbol through the macro instruction
+ of two registers. */
+ enum loongarch_symbol_type symbol_type;
+ if (TARGET_64BIT && register_operand (dest, mode)
+ && loongarch_symbolic_constant_p (src, &symbol_type)
+ && loongarch_symbol_extreme_p (symbol_type))
+ {
+ gcc_assert (can_create_pseudo_p ());
+ rtx tmp_reg = gen_reg_rtx (DImode);
+ emit_insn (gen_movdi_symbolic_off64 (dest, src, tmp_reg));
+ set_unique_reg_note (get_last_insn (), REG_UNUSED, tmp_reg);
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, src);
+ return true;
+ }
+
return false;
}
allowed, otherwise load the address into a register first. */
if (use_sibcall_p)
{
- insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+ if (TARGET_CMODEL_EXTREME)
+ {
+ emit_insn (gen_movdi_symbolic_off64 (temp1, fnaddr, temp2));
+ insn = emit_call_insn (gen_sibcall_internal (temp1, const0_rtx));
+ }
+ else
+ insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
SIBLING_CALL_P (insn) = 1;
}
else
{
- loongarch_emit_move (temp1, fnaddr);
+ if (TARGET_CMODEL_EXTREME)
+ emit_insn (gen_movdi_symbolic_off64 (temp1, fnaddr, temp2));
+ else
+ loongarch_emit_move (temp1, fnaddr);
+
emit_jump_insn (gen_indirect_jump (temp1));
}
switch (la_target.cmodel)
{
case CMODEL_EXTREME:
- if (la_opt_explicit_relocs == EXPLICIT_RELOCS_NONE)
- error ("code model %qs is not compatible with %s",
- "extreme", "-mexplicit-relocs=none");
-
if (opts->x_flag_plt)
{
if (global_options_set.x_flag_plt)
*no_add_attrs = true;
return NULL_TREE;
}
- if (la_opt_explicit_relocs == EXPLICIT_RELOCS_NONE)
- {
- error_at (DECL_SOURCE_LOCATION (decl),
- "%qE attribute is not compatible with %s", name,
- "-mexplicit-relocs=none");
- *no_add_attrs = true;
- return NULL_TREE;
- }
arg = TREE_VALUE (arg);
if (TREE_CODE (arg) != STRING_CST)
UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1
UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1
+
+ UNSPEC_LOAD_SYMBOL_OFFSET64
])
(define_c_enum "unspecv" [
[(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore")
(set_attr "mode" "DI")])
+;; Use two registers to get the global symbol address from the got table.
+;; la.global rd, rt, sym
+
+(define_insn_and_split "movdi_symbolic_off64"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (match_operand:DI 1 "symbolic_off64_or_reg_operand" "Yd,r"))
+ (unspec:DI [(const_int 0)]
+ UNSPEC_LOAD_SYMBOL_OFFSET64)
+ (clobber (match_operand:DI 2 "register_operand" "=&r,r"))]
+ "TARGET_64BIT && TARGET_CMODEL_EXTREME"
+{
+ if (which_alternative == 1)
+ return "#";
+
+ enum loongarch_symbol_type symbol_type;
+ gcc_assert (loongarch_symbolic_constant_p (operands[1], &symbol_type));
+
+ switch (symbol_type)
+ {
+ case SYMBOL_PCREL64:
+ return "la.local\t%0,%2,%1";
+ case SYMBOL_GOT_DISP:
+ return "la.global\t%0,%2,%1";
+ case SYMBOL_TLS_IE:
+ return "la.tls.ie\t%0,%2,%1";
+ case SYMBOL_TLSGD:
+ return "la.tls.gd\t%0,%2,%1";
+ case SYMBOL_TLSLDM:
+ return "la.tls.ld\t%0,%2,%1";
+
+ default:
+ gcc_unreachable ();
+ }
+}
+ "&& REG_P (operands[1]) && find_reg_note (insn, REG_UNUSED, operands[2]) != 0"
+ [(set (match_dup 0) (match_dup 1))]
+ ""
+ [(set_attr "mode" "DI")
+ (set_attr "insn_count" "5")])
+
;; 32-bit Integer moves
(define_expand "movsi"
}
}
[(set_attr "mode" "<MODE>")
- (set_attr "insn_count" "2")])
+ (set (attr "insn_count")
+ (if_then_else
+ (match_test "TARGET_CMODEL_EXTREME")
+ (const_int 4)
+ (const_int 2)))])
;; Move operand 1 to the high word of operand 0 using movgr2frh.w, preserving the
;; value in the low word.