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...

      /* 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.

Thanks,
Richard

Reply via email to