On Sun, Mar 29, 2020 at 6:44 AM Richard Sandiford <richard.sandif...@arm.com> wrote: > > David Edelsohn <dje....@gmail.com> writes: > > On Sat, Mar 28, 2020 at 6:42 AM Richard Sandiford > > <richard.sandif...@arm.com> wrote: > >> > >> David Edelsohn via Gcc-patches <gcc-patches@gcc.gnu.org> writes: > >> > This patch is for an AIX problem, but the only robust solution is in > >> > common code: calls.c:precompute_register_parameters(). > >> > > >> > AIX, like other platforms, needs to call a function to obtain the > >> > pointer to thread-local storage. If the thread local variable is > >> > referenced as a parameter to a function call, the TLS resolution call > >> > may clobber parameters to the primary function call. > >> > > >> > GCC calls.c has special provisions for this situation in > >> > precompute_register_parameters, which tests each parameters with the > >> > legitimate_constant_p target hook. Most targets define that hook to > >> > return False for a TLS symbol. > >> > > >> > From the very initial implementation of the rs6000 port, Richard > >> > Kenner utilized the GCC constant pool for the TOC managed by the > >> > compiler -- the table of position independent pointers to global > >> > symbols (like GOT). TLS symbols must be referenced by the TOC like > >> > all global symbols and must be considered constants valid for the > >> > constant pool. > >> > >> The way the hooks are set up, > >> > >> - legitimate_constant_p tests whether a constant is a legitimate > >> immediate operand for insns > >> > >> - cannot_force_const_mem tests (in the negative) whether a constant > >> is legitimate for the constant pool > >> > >> It's common for legitimate_constant_p to return false for constants that > >> are valid for the constant pool. > >> > >> > Currently targetm.legitimate_constant_p() returns True for TLS symbols > >> > on AIX, which inhibits the pre-legitimization of the TLS symbol in > >> > calls.c and registers can be overridden. If the AIX port defines TLS > >> > symbols as non-constant, emit_move_insn() prematurely calls > >> > force_const_mem() and prevents the correct TLS and TOC code > >> > generation. > >> > > >> > I have experimented extensively with attempts to have it both ways and > >> > teach cannot_force_const_mem hook to reject TLS symbols during the > >> > appropriate phases. This has proven too fragile a solution that > >> > causes additional testsuite failures. targetm.legitimate_constant_p > >> > has too many meanings and too many uses. > >> > >> This set-up sounds similar to many ELF targets. The approach there is > >> that: > >> > >> (1) the TLS constant itself -- i.e. the actual runtime address for the > >> current thread -- is not legitimate_constant_p > >> > >> (2) the TLS constant itself satisfies cannot_force_const_mem (i.e. can't > >> go into the constant pool) > >> > >> (1) stops the constant reappearing in places that can't safely compute it > >> (like the call case here). (2) is necessary because TLS constants, > >> being thread-specific, are fundamentally not load-time constants. > >> > >> (1) + (2) together mean that the constant goes through the move expanders > >> even though it's not a legitimate constant. (In some ways that's a bit > >> of a hack, although not a bad one IMO.) The move expanders then reduce > >> the constant to individual pieces. One of those pieces might be something > >> that goes in the constant pool, with a TLS relocation applied to it. > >> > >> I'm probably well off the mark here, but it looks like the problem for > >> AIX might be that it tries to force the TLS constant itself into the > >> constant pool, rather than a piece of it. The structure is: > >> > >> static rtx > >> rs6000_legitimize_tls_address_aix (rtx addr, enum tls_model model) > >> { > >> rtx sym, mem, tocref, tlsreg, tmpreg, dest, tlsaddr; > >> const char *name; > >> char *tlsname; > >> > >> name = XSTR (addr, 0); > >> /* Append TLS CSECT qualifier, unless the symbol already is qualified > >> or the symbol will be in TLS private data section. */ > >> if (name[strlen (name) - 1] != ']' > >> && (TREE_PUBLIC (SYMBOL_REF_DECL (addr)) > >> || bss_initializer_p (SYMBOL_REF_DECL (addr)))) > >> ...create tlsaddr with a CSECT qualifier...; > >> else > >> tlsaddr = addr; > >> > >> /* Place addr into TOC constant pool. */ > >> sym = force_const_mem (GET_MODE (tlsaddr), tlsaddr); > >> > >> At this point we're forcing the original TLS constant into memory, > >> i.e. the thread-specific runtime value that we're trying to compute. > >> > >> /* Output the TOC entry and create the MEM referencing the value. */ > >> if (constant_pool_expr_p (XEXP (sym, 0)) > >> && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (XEXP > >> (sym, 0)), Pmode)) > >> { > >> tocref = create_TOC_reference (XEXP (sym, 0), NULL_RTX); > >> mem = gen_const_mem (Pmode, tocref); > >> set_mem_alias_set (mem, get_TOC_alias_set ()); > >> } > >> else > >> return sym; > >> > >> I don't understand what the "return sym" case is handling, but it looks > >> like that is the only time that the constant forced to memory above is > >> the actual TLS constant. Down here... > > > > The entire rhythm is exactly the same as rs6000_legitimize_address for > > the creating a normal TOC entry. If it's a valid symbol but not yet > > in the constant pool, it converts it to a TOC reference, otherwise it > > returns the previously re-written symbol that already is in the > > constant pool (TOC). > > > >> > >> /* Use global-dynamic for local-dynamic. */ > >> if (model == TLS_MODEL_GLOBAL_DYNAMIC > >> || model == TLS_MODEL_LOCAL_DYNAMIC) > >> { > >> ...use the tocref with tls_get_addr[sd]i... > >> return dest; > >> } > >> /* Obtain TLS pointer: 32 bit call or 64 bit GPR 13. */ > >> else if (TARGET_32BIT) > >> { > >> tlsreg = gen_reg_rtx (SImode); > >> emit_insn (gen_tls_get_tpointer (tlsreg)); > >> } > >> else > >> tlsreg = gen_rtx_REG (DImode, 13); > >> > >> /* Load the TOC value into temporary register. */ > >> tmpreg = gen_reg_rtx (Pmode); > >> emit_insn (gen_rtx_SET (tmpreg, mem)); > >> set_unique_reg_note (get_last_insn (), REG_EQUAL, > >> gen_rtx_MINUS (Pmode, addr, tlsreg)); > >> > >> /* Add TOC symbol value to TLS pointer. */ > >> dest = force_reg (Pmode, gen_rtx_PLUS (Pmode, tmpreg, tlsreg)); > >> > >> return dest; > >> > >> ...we use "mem"/"tocref" as one piece of a larger calculation. > >> I.e. the value in "mem"/"tocref" is just an input to the calculation, > >> rather than the full result. > >> > >> On ELF targets, the usual way of handling this to use: > >> > >> (const (unspec [SYM] UNSPEC_SOME_TLS_RELOC)) > >> > >> for the constant pieces of the TLS calculation. That const unspec is > >> then a legitimate constant and/or a legitimate constant pool entry, > >> even though the TLS constant itself isn't. > >> > >> So it looks like in the non-"return sym" case, the value being forced > >> to memory should be some kind of unspec wrapper around the constant, > >> instead of being the constant itself. > > > > I realize that is how TLS for ELF is implemented in GCC and I realize > > that GCC mostly is targeted at Linux/ELF. The support for AIX XCOFF > > exists in GCC and functions. There is only so much that I can contort > > XCOFF to look like ELF without completely re-writing TOC support, > > which I'm not going to do. I'm trying to find the minimally invasive > > solution for the TLS problem for AIX. > > > > AIX XCOFF is not ELF. AIX does not emit instructions referencing > > symbols with special decorations for the assembler and linker to > > manage the GOT and relocations. > > > > TLS on AIX creates additional, special TOC entries and appends > > decorations to the symbol placed in the TOC (the GCC constant pool). > > Frequently a GCC UNSPEC would be used to match a different pattern, > > but for AIX it's a normal load of a symbol from the TOC. > > > > .toc > > LC..1: > > .tc result.2299[TC],result.2299[UL] > > LCM..1: > > .tc .result.2299[TC],result.2299[UL]@m > > ... > > lwz 3,LCM..1(2) > > lwz 4,LC..1(2) > > bla __tls_get_addr > > > > How does wrapping the TLS symbol in an UNSPEC help? I'm not expecting > > you to remember how the TOC works on AIX or the peculiarities of the > > way that it is implemented in GCC, but your advice seems to ignore the > > infrastructure for the support of non-TLS TOC symbols in GCC. > > > > You are suggesting using the UNSPEC to flag a legitimized TLS symbol > > versus a non-legitimized TLS symbol, and a legitimized TLS symbol is a > > constant? > > Well, this is all about what (symbol_ref NAME) means for a TLS symbol > (with NAME always assumed to be TLS below). For all GCC ports it means > the actual address of NAME for the current thread. AIUI that is the > value returned by __tls_get_addr, not the [UL] value originally placed > in the TOC. > > But by forcing the TLS symbol_ref into the constant pool as-is: > > .toc > LC..1: > .tc result.2299[TC],result.2299[UL] // (symbol_ref NAME) in memory > > in normal rtl semantics, the load from the constant pool would give > the actual value of the symbol. I.e. this: > > lwz 4,LC..1(2) > > would load the address of NAME for the current thread into register 4. > There would then be no need for __tls_get_addr call. But AIUI this > is not what's actually happening. So it seems like the [UL] value > must be something closely related to (symbol_ref NAME) rather than > (symbol_ref NAME) itself.
[UL] is an AIX "mapping class". It basically tells AIX linker how to map objects to traditional COFF sections. UL is Uniitialized Thread-Local Variable. I'm uncertain what you mean by "something closely related to (symbol_ref NAME)". The value is the symbol_ref. It actually *is* constant for all threads. It is the offset of the symbol_ref relative to the base of the thread-local data section for that thread. If "closely related" means another indirection, no. If "closely related" means a displacement instead of the final address, yes. For some TLS models it invokes a special AIX call to compute the final address, and in other TLS models the compiler directly adds the displacement to a value returned by a call or in a reserved register. > > If instead of saying that (symbol_ref NAME) is being put into the > TOC you say that (const (unspec [(symbol_ref NAME)] UNSPEC_TOC_UL)) > or whatever is being put into the TOC, you can set it up so that: > > - legitimate_constant_p is false for (symbol_ref NAME) > - cannot_force_const_mem is true for (symbol_ref NAME) > - legitimate_constant_p is false for the (const (unspec ...)) [*] > - cannot_force_const_mem is false for the (const (unspec ...)) > > [*] although true could probably be made to work too, if that's better > for some reason > > This means that the move expanders still get the chance to legitimise > the (symbol_ref NAME) access, just like they do now. But the value > you put into the TOC works like any other TOC symbol. I'll see if hiding it in (const ...) is sufficient, with the appropriate re-engineering of the other patterns. But the TLS value placed in the TOC is CONST, as far as it matters to GCC semantics. I agree that TOC support is playing fast and loose with GCC constant pool, and that is a problem, but that is water under the bridge. And, again, I don't want to re-engineer the entire AIX TOC implementation. I'm trying to convince the current machinery to behave nicely with TLS symbols. Thanks, David