@@ -65,6 +65,7 @@ (define_c_enum "unspec" [
UNSPEC_LOAD_FROM_GOT
UNSPEC_PCALAU12I
+ UNSPEC_PCALAU12I_GR
UNSPEC_ORI_L_LO12
UNSPEC_LUI_L_HI20
UNSPEC_LUI_H_LO20
@@ -2297,6 +2298,16 @@ (define_insn "@pcalau12i<mode>"
"pcalau12i\t%0,%%pc_hi20(%1)"
[(set_attr "type" "move")])
+;; @pcalau12i may be used for sibcall so it has a strict constraint. This
+;; allows any general register as the operand.
+(define_insn "@pcalau12i_gr<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P [(match_operand:P 1 "symbolic_operand" "")]
+ UNSPEC_PCALAU12I_GR))]
+ ""
+ "pcalau12i\t%0,%%pc_hi20(%1)"
+ [(set_attr "type" "move")])
+
(define_insn "@ori_l_lo12<mode>"
[(set (match_operand:P 0 "register_operand" "=r")
(unspec:P [(match_operand:P 1 "register_operand" "r")
@@ -3748,6 +3759,117 @@ (define_insn "loongarch_crcc_w_<size>_w"
[(set_attr "type" "unknown")
(set_attr "mode" "<MODE>")])
+;; With normal or medium code models, if the only use of a pc-relative
+;; address is for loading or storing a value, then relying on linker
+;; relaxation is not better than emitting the machine instruction directly.
+;; Even if the la.local pseudo op can be relaxed, we get:
+;;
+;; pcaddi $t0, %pcrel_20(x)
+;; ld.d $t0, $t0, 0
+;;
+;; There are still two instructions, same as using the machine instructions
+;; and explicit relocs:
+;;
+;; pcalau12i $t0, %pc_hi20(x)
+;; ld.d $t0, $t0, %pc_lo12(x)
+;;
+;; And if the pseudo op cannot be relaxed, we'll get a worse result (with
+;; 3 instructions).
+(define_peephole2
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1 "symbolic_pcrel_operand"))
+ (set (match_operand:GPR 2 "register_operand")
+ (mem:GPR (match_dup 0)))]
+ "la_opt_explicit_relocs == EXPLICIT_RELOCS_AUTO \
+ && (TARGET_CMODEL_NORMAL || TARGET_CMODEL_MEDIUM) \
+ && (peep2_reg_dead_p (2, operands[0]) \
+ || REGNO (operands[0]) == REGNO (operands[2]))"
+ [(set (match_dup 2) (mem:GPR (lo_sum:P (match_dup 0) (match_dup 1))))]
+ {
+ emit_insn (gen_pcalau12i_gr<P:mode> (operands[0], operands[1]));
+ })
+
+(define_peephole2
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1 "symbolic_pcrel_operand"))
+ (set (match_operand:GPR 2 "register_operand")
+ (mem:GPR (plus (match_dup 0)
+ (match_operand 3 "const_int_operand"))))]
+ "la_opt_explicit_relocs == EXPLICIT_RELOCS_AUTO \
+ && (TARGET_CMODEL_NORMAL || TARGET_CMODEL_MEDIUM) \
+ && (peep2_reg_dead_p (2, operands[0]) \
+ || REGNO (operands[0]) == REGNO (operands[2]))"
+ [(set (match_dup 2) (mem:GPR (lo_sum:P (match_dup 0) (match_dup 1))))]
+ {
+ operands[1] = plus_constant (Pmode, operands[1], INTVAL (operands[3]));
+ emit_insn (gen_pcalau12i_gr<P:mode> (operands[0], operands[1]));
+ })
+
+(define_peephole2
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1 "symbolic_pcrel_operand"))
+ (set (match_operand:GPR 2 "register_operand")
+ (any_extend:GPR (mem:SUBDI (match_dup 0))))]
+ "la_opt_explicit_relocs == EXPLICIT_RELOCS_AUTO \
+ && (TARGET_CMODEL_NORMAL || TARGET_CMODEL_MEDIUM) \
+ && (peep2_reg_dead_p (2, operands[0]) \
+ || REGNO (operands[0]) == REGNO (operands[2]))"
+ [(set (match_dup 2)
+ (any_extend:GPR (mem:SUBDI (lo_sum:P (match_dup 0)
+ (match_dup 1)))))]
+ {
+ emit_insn (gen_pcalau12i_gr<P:mode> (operands[0], operands[1]));
+ })
+
+(define_peephole2
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1 "symbolic_pcrel_operand"))
+ (set (match_operand:GPR 2 "register_operand")
+ (any_extend:GPR
+ (mem:SUBDI (plus (match_dup 0)
+ (match_operand 3 "const_int_operand")))))]
+ "la_opt_explicit_relocs == EXPLICIT_RELOCS_AUTO \
+ && (TARGET_CMODEL_NORMAL || TARGET_CMODEL_MEDIUM) \
+ && (peep2_reg_dead_p (2, operands[0]) \
+ || REGNO (operands[0]) == REGNO (operands[2]))"
+ [(set (match_dup 2)
+ (any_extend:GPR (mem:SUBDI (lo_sum:P (match_dup 0)
+ (match_dup 1)))))]
+ {
+ operands[1] = plus_constant (Pmode, operands[1], INTVAL (operands[3]));
+ emit_insn (gen_pcalau12i_gr<P:mode> (operands[0], operands[1]));
+ })
+
+(define_peephole2
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1 "symbolic_pcrel_operand"))
+ (set (mem:QHWD (match_dup 0))
+ (match_operand:QHWD 2 "register_operand"))]
+ "la_opt_explicit_relocs == EXPLICIT_RELOCS_AUTO \
+ && (TARGET_CMODEL_NORMAL || TARGET_CMODEL_MEDIUM) \
+ && (peep2_reg_dead_p (2, operands[0])) \
+ && REGNO (operands[0]) != REGNO (operands[2])"
+ [(set (mem:QHWD (lo_sum:P (match_dup 0) (match_dup 1))) (match_dup 2))]
+ {
+ emit_insn (gen_pcalau12i_gr<P:mode> (operands[0], operands[1]));
+ })
+
+(define_peephole2
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1 "symbolic_pcrel_operand"))
+ (set (mem:QHWD (plus (match_dup 0)
+ (match_operand 3 "const_int_operand")))
+ (match_operand:QHWD 2 "register_operand"))]
+ "la_opt_explicit_relocs == EXPLICIT_RELOCS_AUTO \
+ && (TARGET_CMODEL_NORMAL || TARGET_CMODEL_MEDIUM) \
+ && (peep2_reg_dead_p (2, operands[0])) \
+ && REGNO (operands[0]) != REGNO (operands[2])"
+ [(set (mem:QHWD (lo_sum:P (match_dup 0) (match_dup 1))) (match_dup 2))]
+ {
+ operands[1] = plus_constant (Pmode, operands[1], INTVAL (operands[3]));
+ emit_insn (gen_pcalau12i_gr<P:mode> (operands[0], operands[1]));
+ })
+
;; Synchronization instructions.
(include "sync.md")
@@ -563,6 +563,13 @@ (define_predicate "symbolic_operand"
return loongarch_symbolic_constant_p (op, &type);
})
+(define_predicate "symbolic_pcrel_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum loongarch_symbol_type type;
+ return loongarch_symbolic_constant_p (op, &type) && type == SYMBOL_PCREL;
+})
+
(define_predicate "equality_operator"
(match_code "eq,ne"))
new file mode 100644
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=loongarch64 -mabi=lp64d -mexplicit-relocs=auto -fno-section-anchors" } */
+
+#include "explicit-relocs-auto-single-load-store.c"
+
+/* { dg-final { scan-assembler-not "la.local" } } */
new file mode 100644
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=loongarch64 -mabi=lp64d -mexplicit-relocs=auto" } */
+
+long a;
+int b;
+unsigned int c;
+
+long load_a() { return a; }
+long load_b() { return b; }
+long load_c() { return c; }
+void store_a(long x) { a = x; }
+void store_b(int x) { b = x; }
+
+/* { dg-final { scan-assembler-not "la.local" } } */