@@ -1944,10 +1944,10 @@ loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type)
case SYMBOL_TLSGD:
case SYMBOL_TLSLDM:
case SYMBOL_PCREL:
- case SYMBOL_PCREL64:
/* GAS rejects offsets outside the range [-2^31, 2^31-1]. */
return sext_hwi (INTVAL (offset), 32) == INTVAL (offset);
+ case SYMBOL_PCREL64:
case SYMBOL_GOT_DISP:
case SYMBOL_TLS:
return false;
@@ -7450,12 +7450,22 @@ loongarch_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
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_pcrel64 (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_pcrel64 (temp1, fnaddr, temp2));
+ else
+ loongarch_emit_move (temp1, fnaddr);
+
emit_jump_insn (gen_indirect_jump (temp1));
}
@@ -7583,10 +7593,6 @@ loongarch_option_override_internal (struct gcc_options *opts,
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)
@@ -7951,14 +7957,6 @@ loongarch_handle_model_attribute (tree *node, tree name, tree arg, int,
*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)
@@ -85,6 +85,9 @@ (define_c_enum "unspec" [
UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1
UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1
+
+ UNSPEC_MOV_PCREL64
+ UNSPEC_MOV_GOT_DISP
])
(define_c_enum "unspecv" [
@@ -2057,6 +2060,25 @@ (define_expand "movdi"
{
if (loongarch_legitimize_move (DImode, operands[0], operands[1]))
DONE;
+
+ enum loongarch_symbol_type symbol_type;
+ if (loongarch_symbolic_constant_p (operands[1], &symbol_type))
+ {
+ if (symbol_type == SYMBOL_PCREL64)
+ {
+ gcc_assert (can_create_pseudo_p ());
+ emit_insn (gen_movdi_pcrel64 (operands[0], operands[1],
+ gen_reg_rtx (DImode)));
+ DONE;
+ }
+ else if (TARGET_CMODEL_EXTREME && symbol_type == SYMBOL_GOT_DISP)
+ {
+ gcc_assert (can_create_pseudo_p ());
+ emit_insn (gen_movdi_got_disp (operands[0], operands[1],
+ gen_reg_rtx (DImode)));
+ DONE;
+ }
+ }
})
(define_insn_and_split "*movdi_32bit"
@@ -2097,6 +2119,34 @@ (define_insn_and_split "*movdi_64bit"
[(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore")
(set_attr "mode" "DI")])
+;; Use two registers to get the local symbol address.
+;; la.local rd, rt, sym
+
+(define_insn "movdi_pcrel64"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (match_operand:DI 1 "symbolic_pcrel64_operand"))
+ (unspec:DI [(const_int 0)]
+ UNSPEC_MOV_PCREL64)
+ (clobber (match_operand:DI 2 "register_operand" "=&r"))]
+ "TARGET_64BIT"
+ "la.local\t%0,%2,%1"
+ [(set_attr "mode" "DI")
+ (set_attr "length" "5")])
+
+;; Use two registers to get the global symbol address from the got table.
+;; la.global rd, rt, sym
+
+(define_insn "movdi_got_disp"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (match_operand:DI 1 "symbolic_got_operand"))
+ (unspec:DI [(const_int 0)]
+ UNSPEC_MOV_GOT_DISP)
+ (clobber (match_operand:DI 2 "register_operand" "=&r"))]
+ "TARGET_64BIT && TARGET_CMODEL_EXTREME"
+ "la.global\t%0,%2,%1"
+ [(set_attr "mode" "DI")
+ (set_attr "length" "5")])
+
;; 32-bit Integer moves
(define_expand "movsi"
@@ -592,6 +592,20 @@ (define_predicate "mem_simple_ldst_operand"
|| symbolic_pcrel_offset_operand (op, Pmode));
})
+(define_predicate "symbolic_got_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum loongarch_symbol_type type;
+ return loongarch_symbolic_constant_p (op, &type) && type == SYMBOL_GOT_DISP;
+})
+
+(define_predicate "symbolic_pcrel64_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum loongarch_symbol_type type;
+ return loongarch_symbolic_constant_p (op, &type) && type == SYMBOL_PCREL64;
+})
+
(define_predicate "equality_operator"
(match_code "eq,ne"))
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs=none -mcmodel=extreme" } */
+/* { dg-final { scan-assembler "test:.*la.global.*,\\\$r15,g" } } */
+/* { dg-final { scan-assembler "test1:.*la.global.*,\\\$r15,f" } } */
+/* { dg-final { scan-assembler "test2:.*la.local.*,\\\$r15,l" } } */
+
+#include "func-call-extreme-1.c"
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs=none -mcmodel=extreme" } */
+/* { dg-final { scan-assembler "test:.*la.global.*,\\\$r15,g" } } */
+/* { dg-final { scan-assembler "test1:.*la.local.*,\\\$r15,f" } } */
+/* { dg-final { scan-assembler "test2:.*la.local.*,\\\$r15,l" } } */
+
+#include "func-call-extreme-1.c"