erichkeane added a comment.

In D112349#3116457 <https://reviews.llvm.org/D112349#3116457>, @ibookstein 
wrote:

>> To me the 'not in the weeds' part is, "How do I get 
>> CPU-dispatch/CPU-specific to work without RAUW, since that is offensive to 
>> the CFE code owner?  Additionally, I found that some of the examples without 
>> the defined resolver actually DO work in my downstream, though I don't know 
>> what changes we make to make that happen.  So adding this limitation in 
>> actually breaks my downstream.
>
> Regarding the examples that do work, I've provided explanations as to how 
> they partially sort-of-work, but that the general semantics are broken (the 
> 'connection' to the resolver is 'severed' in each translation unit that has 
> it as a declaration + all references from within that TU are borked).

As I mentioned, my downstream actually DOES work, I believe our code-generator 
has some changes that are making this work, but I haven't found the actual 
commit yet.  I suspect it is some level of changes that emit ifuncs with 
undefined resolvers differently.

> The problems that the CFE currently uses RAUW to solve are //fundamental// to 
> the specified behavior of the **language features** that use it, so a 
> solution for these problems needs to arise from the CFE. Imagine the 
> following incremental compilation use-case:
>
>   > extern int x; // x is a GlobalVariable declaration (initializer == null).
>   > int y; // y is a GlobalVariable definition (initializer = constant 0).
>   > extern int x __attribute__((alias("y"))); // x needs to transform into a 
> GlobalAlias now.
>   >
>   > extern void *my_memcpy(void *dest, void *src, size_t n); // my_memcpy is 
> a Function declaration
>   > void *my_memcpy(void *dest, void *src, unsigned long n) 
> __attribute__((ifunc("my_memcpy_resolver"))); // my_memcpy_resolver is a 
> Function declaration, my_memcpy is a GlobalIFunc with declaration for a 
> resolver - whoops, incremental module is invalid at this point
>   > static void *my_memcpy_impl(void *dest, void *src, unsigned long n) { 
> return 0; }
>   > static void *my_memcpy_resolver(void) { return &my_memcpy_impl; } // Now 
> my_memcpy ifunc has defined resolver
>   >
>   > void CpuSpecific(void); // CpuSpecific is a Function declaration
>   > void Foo(void) { CpuSpecific(); } // Foo calls a function declaration
>   > __attribute__((cpu_specific(generic))) void CpuSpecific(void) { 
> puts("generic"); } // We still don't know whether cpu_dispatch will be in 
> this TU or not. Do we upgrade CpuSpecific from a function declaration to a 
> definition? to an ifunc? Bind directly to this body?
>   > __attribute__((cpu_dispatch(generic))) void CpuSpecific(void); // Now we 
> know that we have to upgrade it to an ifunc, and how the resolver should 
> look. But it's already a Function (declaration).
>
> All the above //need// to work in non-incremental compilation, and the only 
> existing way for them to work right now is RAUW or a module finalization 
> step. If the CFE code owner(s) find that offensive but proposes no 
> alternative, what is one to do? There are 3 possible states here:
>
> 1. Remain broken

As mentioned above, not necessarily always broken, my downstream works with 
your examples of how its broken.  Breaking this is IMO, not acceptable.

> 2. Solve using RAUW/finalization

This is unacceptable to the Clang CFE maintainer, so I believe this is a 
non-starter.

> 3. Solve using yet-unproposed non-RAUW solution (which solves all of the 
> above use-cases simultaneously because they're essentially the same)

My preference is clearly this one.  If we had a way to create the ifunc in the 
CFE with NO resolver (that is, a forward-declaration of an ifunc!) that could 
be emitted as-if it were a normal function call (which is what you suggested to 
happen in the CFE, so this is just suggesting it later), it solves my problem.  
As far as I can tell, this is what my downstream is doing (except the key-off 
here is whether the resolver is not defined, not null).  Can we discuss an 
actual solution as a part of this discussion?

> It is better to treat (2) as a way out of (1) which doesn't increase the cost 
> of (3), than to just stay at (1) until (3) happens. As it currently stands, 
> the somewhat similar issue of calling a declared-but-undefined function in 
> the REPL is currently embodied as a JIT session error, with failure to 
> materialize symbols. Perhaps a valid solution for the work-in-progress 
> aliases and ifuncs is to transform them into declarations in the JIT module 
> until they have definitions for their aliasees/resolvers. But we won't know 
> without more context. I think it might be more effective that I'll write a 
> patch up which does use RAUW together with a test for the breakage we 
> discussed and we'll continue the discussion with the CFE code owner(s) there.
>
>>> The LLVM bitcode verifier does not consider GlobalAliases which have either 
>>> a null or an undefined aliasee to be valid. The same should have held for 
>>> GlobalIFuncs and their resolvers since their inception, and the fact that 
>>> it were not so is an oversight.
>>
>> Citation Needed here.  Is there a design document that specifies this, or is 
>> this your opinion?  If its the latter, the implementation was the 
>> documentation, so this is still a breaking change.
>
> I'm assuming that you're talking about GlobalIFuncs, since for GlobalAliases 
> you'll see both constraints encoded in the `Verifier::visitGlobalAlias` and 
> `Verifier::visitAliaseeSubExpr` functions.
> As for GlobalIFunc, I have no design document for the LLVM-IR-level 
> representation of the feature, but:
>
> 1. @MaskRay has provided reference for the object-format constraint itself 
> (`Requirement (a): Resolver must be defined in the same translation unit as 
> the implementations.`). Bitcode Modules correspond to TUs - so it stands to 
> reason that the same restriction apply to them, unless behavior is 
> implemented to bridge the gap.
> 2. I've demonstrated that compiling a bitcode module containing an ifunc with 
> an undefined resolver emits broken code for all usages of the 
> ifunc-with-no-resolver, where I can probably massage that into a crash with a 
> small bit of additional work (since it ends up calling the resolver rather 
> than calling the return value of the resolver). This applies to current 
> use-cases of cpu_specific in translation units that don't contain the 
> corresponding cpu_dispatch, which I understand is part of the documented 
> usage model of cpu_specific/cpu_dispatch. So it's already subtly broken, and 
> in reference to (1), this means that behavior to bridge the gap between what 
> the IR ostensibly allows and what the object format allows is **not** 
> implemented.
> 3. CFE already diagnoses this issue for plain ifuncs and aliases.

So its important to note here that a 'language level' ifunc is not a perfect 
match to the IR-level ifunc, which is not a perfect match to an object-file 
ifunc.  These are 3 different features that map closely, but not exactly.

> 4. I've drawn significant parallels between aliases and ifuncs at the object 
> file level, and extrapolated the reasoning for why aliases have to point at 
> defined objects to the reasoning why ifuncs have to point at defined objects.
>
> So, when I say "should have held" I am indeed expressing my opinion. But I do 
> believe that I've backed it up substantially, and that from a design 
> perspective adding that restriction to the IR really is the correct decision.

I appreciate the explanation.

> Calling the implementation the documentation doesn't ring right with me - is 
> the broken behavior I referred to in (2) a compatibility constraint now? Are 
> the bizarre disallowed-by-the-specification undefined STT_GNU_IFUNC symbols 
> emitted by cpu_specific-without-cpu_dispatch a compatibility constraint now?

In absence of a spec, the implement-is-the-spec just by nature.  As I 
mentioned, my downstream DOES have this work, so this is a compatibility 
constraint (and we as a project tend to at least try to work with downstreams 
by not breaking them this aggressively). That said, I am, and have been willing 
to work with you to make the reasonable changes here.  I'm trying to get us to 
a solution that doesn't break my users and allows you to recommit your patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D112349/new/

https://reviews.llvm.org/D112349

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to