Excerpts from Richard Sandiford's message of August 19, 2020 1:22 pm: > Iain Buclaw via Gcc-patches <gcc-patches@gcc.gnu.org> writes: >> Hi, >> >> On x86, a memory reference reference to a TLS address can be broken out >> and stored in a register, for instance: >> >> movq %fs:8+testYearsBC@tpoff, %rdx >> >> Subsequently becomes: >> >> pushq %rbp >> leaq 8+testYearsBC@tpoff, %rbp >> // later... >> movq %fs:0(%rbp), %rdx >> >> When it comes to representing this in Dwarf, mem_loc_descriptor is left >> with the following RTL, which ix86_delegitimize_tls_address is unable to >> handle as the symbol_ref has been swapped out with the temporary. >> >> (plus:DI (unspec:DI [ >> (const_int 0 [0]) >> ] UNSPEC_TP) >> (reg/f:DI 6 bp [90])) >> >> The UNSPEC_TP expression is checked, ix86_const_not_ok_for_debug_p >> returns false as it only lets through UNSPEC_GOTOFF, and finally >> const_ok_for_output is either not smart enough or does not have the >> information needed to recognize that UNSPEC_TP is a TLS UNSPEC that >> should be ignored. This results in informational warnings being fired >> under -fchecking. >> >> The entrypoint that triggers this warning to occur is that MEM codes are >> first lowered with mem_loc_descriptor, with tls_mem_loc_descriptor only >> being used if that failed. This patch switches that around so that TLS >> memory expressions first call tls_mem_loc_descriptor, and only use >> mem_loc_descriptor if that fails (which may result in UNSPEC warnings). >> >> Bootstrapped and regression tested on x86_64-linux-gnu, I do also have a >> sparc64-linux-gnu build at hand, but have not yet got the results to >> check for a before/after comparison. >> >> OK for mainline? >> >> Regards >> Iain >> >> --- >> gcc/ChangeLog: >> >> PR target/96347 >> * dwarf2out.c (is_tls_mem_loc): New. >> (mem_loc_descriptor): Call tls_mem_loc_descriptor on TLS memory >> expressions, fallback to mem_loc_descriptor if unsuccessful. >> (loc_descriptor): Likewise. >> >> gcc/testsuite/ChangeLog: >> >> PR target/96347 >> * g++.dg/other/pr96347.C: New test. >> --- >> gcc/dwarf2out.c | 30 ++++++++++---- >> gcc/testsuite/g++.dg/other/pr96347.C | 61 ++++++++++++++++++++++++++++ >> 2 files changed, 84 insertions(+), 7 deletions(-) >> create mode 100644 gcc/testsuite/g++.dg/other/pr96347.C >> >> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c >> index 9deca031fc2..093ceb23c7a 100644 >> --- a/gcc/dwarf2out.c >> +++ b/gcc/dwarf2out.c >> @@ -14435,6 +14435,20 @@ is_based_loc (const_rtx rtl) >> && CONST_INT_P (XEXP (rtl, 1))))); >> } >> >> +/* Return true if this MEM expression is for a TLS variable. */ >> + >> +static bool >> +is_tls_mem_loc (rtx mem) >> +{ >> + tree base; >> + >> + if (MEM_EXPR (mem) == NULL_TREE || !MEM_OFFSET_KNOWN_P (mem)) >> + return false; >> + >> + base = get_base_address (MEM_EXPR (mem)); >> + return (base && VAR_P (base) && DECL_THREAD_LOCAL_P (base)); >> +} >> + >> /* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0) >> failed. */ >> >> @@ -15671,11 +15685,12 @@ mem_loc_descriptor (rtx rtl, machine_mode mode, >> return mem_loc_result; >> } >> } >> - mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), >> - get_address_mode (rtl), mode, >> - VAR_INIT_STATUS_INITIALIZED); >> - if (mem_loc_result == NULL) >> + if (is_tls_mem_loc (rtl)) >> mem_loc_result = tls_mem_loc_descriptor (rtl); >> + if (mem_loc_result == NULL) > > Is the is_tls_mem_loc part necessary? It looks like it's just > repeating the first part of tls_mem_loc_descriptor, and so we > could call that unconditionally instead. >
Not necessary, other than it makes it clear from the caller that rtl is a TLS symbol reference. The comments for both tls_mem_loc_descriptor and mem_loc_descriptor would need to be updated to reflect that the latter is called only if the tls_mem function fails. > I guess this raises the question: if we swapping the order for > TLS so that MEM_EXPR trumps the MEM address, why shouldn't we take > that approach more generally? I.e. is there any reason to look at > the MEM_EXPR only when DECL_THREAD_LOCAL_P is true for the base address, > rather than for all symbolic base addresses? > Someone else might better know. But as I understand, TLS addresses must always be looked up through the thread pointer, even if it is cached to a temporary. For the given test, the generated DWARF doesn't change whether it is either inferred from the MEM_EXPR or the MEM address, should the test be fixed up to not trigger the UNSPEC warning (swap the length and ptr fields). (0x7ffff7615870) DW_OP_const8u address, 0 (0x7ffff76158c0) DW_OP_GNU_push_tls_address 0, 0 (0x7ffff7615960) DW_OP_deref 0, 0 Whereas globals can be dereferenced from anywhere that can hold an address value. Using the test as an example again, removing __thread from the global generates. (0x7ffff7615870) DW_OP_breg6 8, 0 (0x7ffff76158c0) DW_OP_deref 0, 0 Removing the DECL_THREAD_LOCAL_P condition, so that tls_mem_loc_descriptor handles shared globals too instead gives us. (0x7ffff76158c0) DW_OP_addr address, 0 (0x7ffff76159b0) DW_OP_plus_uconst 8, 0 (0x7ffff7615a00) DW_OP_deref 0, 0 What would the benefit of the DW_OP_addr be over DW_OP_breg6? Iain.