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

Reply via email to