[2/5] LoongArch: Use explicit relocs for GOT access when -mexplicit-relocs=auto and LTO during a final link with linker plugin
Checks
Commit Message
If we are performing LTO for a final link and linker plugin is enabled,
then we are sure any GOT access may resolve to a symbol out of the link
unit (otherwise the linker plugin will tell us the symbol should be
resolved locally and we'll use PC-relative access instead).
Produce machine instructions with explicit relocs instead of la.global
for better scheduling.
gcc/ChangeLog:
* config/loongarch/loongarch-protos.h
(loongarch_explicit_relocs_p): Declare new function.
* config/loongarch/loongarch.cc (loongarch_explicit_relocs_p):
Implement.
(loongarch_symbol_insns): Call loongarch_explicit_relocs_p for
SYMBOL_GOT_DISP, instead of using TARGET_EXPLICIT_RELOCS.
(loongarch_split_symbol): Call loongarch_explicit_relocs_p for
deciding if return early, instead of using
TARGET_EXPLICIT_RELOCS.
(loongarch_output_move): CAll loongarch_explicit_relocs_p
instead of using TARGET_EXPLICIT_RELOCS.
* config/loongarch/loongarch.md (*low<mode>): Remove
TARGET_EXPLICIT_RELOCS from insn condition.
(@ld_from_got<mode>): Likewise.
* config/loongarch/predicates.md (move_operand): Call
loongarch_explicit_relocs_p instead of using
TARGET_EXPLICIT_RELOCS.
gcc/testsuite/ChangeLog:
* gcc.target/loongarch/explicit-relocs-auto-lto.c: New test.
---
gcc/config/loongarch/loongarch-protos.h | 1 +
gcc/config/loongarch/loongarch.cc | 34 +++++++++++++++----
gcc/config/loongarch/loongarch.md | 4 +--
gcc/config/loongarch/predicates.md | 8 ++---
.../loongarch/explicit-relocs-auto-lto.c | 26 ++++++++++++++
5 files changed, 59 insertions(+), 14 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/loongarch/explicit-relocs-auto-lto.c
Comments
/* snip */
> +/* If -mexplicit-relocs=auto, we use machine operations with reloc hints
> + for cases where the linker is unable to relax so we can schedule the
> + machine operations, otherwise use an assembler pseudo-op so the
> + assembler will generate R_LARCH_RELAX. */
> +
> +bool
> +loongarch_explicit_relocs_p (enum loongarch_symbol_type type)
> +{
> + if (la_opt_explicit_relocs != EXPLICIT_RELOCS_AUTO)
> + return la_opt_explicit_relocs == EXPLICIT_RELOCS_ALWAYS;
> +
> + /* If we are performing LTO for a final link, and we have the linker
> + plugin so we know the resolution of the symbols, then all GOT
> + references are binding to external symbols or preemptable symbols.
> + So the linker cannot relax them. */
> + return (in_lto_p
> + && !flag_incremental_link
I don’t quite understand this condition "!flag_incremental_link". Can
you explain it? Others LGTM.
Thanks.
> + && HAVE_LTO_PLUGIN == 2
> + && (!global_options_set.x_flag_use_linker_plugin
> + || global_options.x_flag_use_linker_plugin)
> + && type == SYMBOL_GOT_DISP);
> +}
> +
On Sat, 2023-10-21 at 15:32 +0800, chenglulu wrote:
> > + /* If we are performing LTO for a final link, and we have the linker
> > + plugin so we know the resolution of the symbols, then all GOT
> > + references are binding to external symbols or preemptable symbols.
> > + So the linker cannot relax them. */
> > + return (in_lto_p
> > + && !flag_incremental_link
>
> I don’t quite understand this condition "!flag_incremental_link". Can
> you explain it? Others LGTM.
>
> Thanks.
If we have two (or several) .o files containing LTO bytecode, GCC
supports "LTO incremental linking" with:
gcc a.o b.o -o ab.o -O2 -flto -flinker-output=nolto-rel
The resulted ab.o will include data and code in a.o and b.o, but it
contains machine code instead of LTO bytecode. Now if ab.o refers to an
external symbol c, the linker may relax "la.global c" to "la.local c"
(if ab.o is linked together with another file c.o which contains the
definition of c) or not. As we cannot exclude the possibility of a
relaxation on la.global for incremental linking, just emit la.global and
let the linker to do the correct thing.
在 2023/10/21 下午4:42, Xi Ruoyao 写道:
> On Sat, 2023-10-21 at 15:32 +0800, chenglulu wrote:
>>> + /* If we are performing LTO for a final link, and we have the linker
>>> + plugin so we know the resolution of the symbols, then all GOT
>>> + references are binding to external symbols or preemptable symbols.
>>> + So the linker cannot relax them. */
>>> + return (in_lto_p
>>> + && !flag_incremental_link
>> I don’t quite understand this condition "!flag_incremental_link". Can
>> you explain it? Others LGTM.
>>
>> Thanks.
> If we have two (or several) .o files containing LTO bytecode, GCC
> supports "LTO incremental linking" with:
>
> gcc a.o b.o -o ab.o -O2 -flto -flinker-output=nolto-rel
>
> The resulted ab.o will include data and code in a.o and b.o, but it
> contains machine code instead of LTO bytecode. Now if ab.o refers to an
> external symbol c, the linker may relax "la.global c" to "la.local c"
> (if ab.o is linked together with another file c.o which contains the
> definition of c) or not. As we cannot exclude the possibility of a
> relaxation on la.global for incremental linking, just emit la.global and
> let the linker to do the correct thing.
>
I have no problem, thank you!
@@ -220,4 +220,5 @@ extern rtx loongarch_gen_const_int_vector_shuffle (machine_mode, int);
extern tree loongarch_build_builtin_va_list (void);
extern rtx loongarch_build_signbit_mask (machine_mode, bool, bool);
+extern bool loongarch_explicit_relocs_p (enum loongarch_symbol_type);
#endif /* ! GCC_LOONGARCH_PROTOS_H */
@@ -1925,6 +1925,29 @@ loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type)
gcc_unreachable ();
}
+/* If -mexplicit-relocs=auto, we use machine operations with reloc hints
+ for cases where the linker is unable to relax so we can schedule the
+ machine operations, otherwise use an assembler pseudo-op so the
+ assembler will generate R_LARCH_RELAX. */
+
+bool
+loongarch_explicit_relocs_p (enum loongarch_symbol_type type)
+{
+ if (la_opt_explicit_relocs != EXPLICIT_RELOCS_AUTO)
+ return la_opt_explicit_relocs == EXPLICIT_RELOCS_ALWAYS;
+
+ /* If we are performing LTO for a final link, and we have the linker
+ plugin so we know the resolution of the symbols, then all GOT
+ references are binding to external symbols or preemptable symbols.
+ So the linker cannot relax them. */
+ return (in_lto_p
+ && !flag_incremental_link
+ && HAVE_LTO_PLUGIN == 2
+ && (!global_options_set.x_flag_use_linker_plugin
+ || global_options.x_flag_use_linker_plugin)
+ && type == SYMBOL_GOT_DISP);
+}
+
/* Returns the number of instructions necessary to reference a symbol. */
static int
@@ -1940,7 +1963,7 @@ loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode)
case SYMBOL_GOT_DISP:
/* The constant will have to be loaded from the GOT before it
is used in an address. */
- if (!TARGET_EXPLICIT_RELOCS && mode != MAX_MACHINE_MODE)
+ if (!loongarch_explicit_relocs_p (type) && mode != MAX_MACHINE_MODE)
return 0;
return 3;
@@ -3038,7 +3061,7 @@ loongarch_symbol_extreme_p (enum loongarch_symbol_type type)
If so, and if LOW_OUT is nonnull, emit the high part and store the
low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise.
- Return false if build with '-mno-explicit-relocs'.
+ Return false if build with '-mexplicit-relocs=none'.
TEMP is as for loongarch_force_temporary and is used to load the high
part into a register.
@@ -3052,12 +3075,9 @@ loongarch_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out)
{
enum loongarch_symbol_type symbol_type;
- /* If build with '-mno-explicit-relocs', don't split symbol. */
- if (!TARGET_EXPLICIT_RELOCS)
- return false;
-
if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE)
|| !loongarch_symbolic_constant_p (addr, &symbol_type)
+ || !loongarch_explicit_relocs_p (symbol_type)
|| loongarch_symbol_insns (symbol_type, mode) == 0
|| !loongarch_split_symbol_type (symbol_type))
return false;
@@ -4797,7 +4817,7 @@ loongarch_output_move (rtx dest, rtx src)
}
}
- if (!TARGET_EXPLICIT_RELOCS
+ if (!loongarch_explicit_relocs_p (loongarch_classify_symbol (src))
&& dest_code == REG && symbolic_operand (src, VOIDmode))
{
if (loongarch_classify_symbol (src) == SYMBOL_PCREL)
@@ -2247,7 +2247,7 @@ (define_insn "*low<mode>"
[(set (match_operand:P 0 "register_operand" "=r")
(lo_sum:P (match_operand:P 1 "register_operand" " r")
(match_operand:P 2 "symbolic_operand" "")))]
- "TARGET_EXPLICIT_RELOCS"
+ ""
"addi.<d>\t%0,%1,%L2"
[(set_attr "type" "arith")
(set_attr "mode" "<MODE>")])
@@ -2275,7 +2275,7 @@ (define_insn "@ld_from_got<mode>"
(match_operand:P 1 "register_operand" "r")
(match_operand:P 2 "symbolic_operand")))]
UNSPEC_LOAD_FROM_GOT))]
- "TARGET_EXPLICIT_RELOCS"
+ ""
"ld.<d>\t%0,%1,%L2"
[(set_attr "type" "move")]
)
@@ -541,16 +541,14 @@ (define_predicate "move_operand"
case SYMBOL_REF:
case LABEL_REF:
return (loongarch_symbolic_constant_p (op, &symbol_type)
- && (!TARGET_EXPLICIT_RELOCS
+ && (!loongarch_explicit_relocs_p (symbol_type)
|| !loongarch_split_symbol_type (symbol_type)));
case HIGH:
- /* '-mno-explicit-relocs' don't generate high/low pairs. */
- if (!TARGET_EXPLICIT_RELOCS)
- return false;
-
op = XEXP (op, 0);
+
return (loongarch_symbolic_constant_p (op, &symbol_type)
+ && loongarch_explicit_relocs_p (symbol_type)
&& loongarch_split_symbol_type (symbol_type));
default:
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-do link } */
+/* { dg-require-effective-target lto } */
+/* { dg-require-linker-plugin "" } */
+/* { dg-options "-fpic -shared -O2 --save-temps -mexplicit-relocs=auto -flto -fuse-linker-plugin -flto-partition=one" } */
+
+int pcrel __attribute__ ((visibility ("hidden")));
+int got __attribute__ ((visibility ("default")));
+
+int
+*addr_pcrel (void)
+{
+ return &pcrel;
+}
+
+int
+*addr_got (void)
+{
+ return &got;
+}
+
+/* With linker plugin we should use la.local (it can be relaxed to pcaddi),
+ but not la.global (we are pretty sure the linker cannot relax la.global
+ got). */
+/* { dg-final { scan-lto-assembler "la.local.*pcrel" } } */
+/* { dg-final { scan-lto-assembler "pcalau12i.*%got_pc_hi20\\\(got\\\)" } } */
+/* { dg-final { scan-lto-assembler "ld.*%got_pc_lo12\\\(got\\\)" } } */