[llvm-dev] "Unusual" linkage inhibits interprocedural constant propagation?

Alex P. via llvm-dev llvm-dev at lists.llvm.org
Tue Oct 27 12:56:02 PDT 2020


Oh, I think I got it. Different optimizations (levels) applied to 
different translation units can still lead to semantically identical but 
different function body for the "interposed" function (same as for 
"_odr"), right?

I will take it into account for the bug report.

On 27-Oct-20 2:01 PM, Johannes Doerfert wrote:
> 
> On 10/27/20 1:50 PM, Alex P. wrote:
>> Johannes, thank you for your explanations. Now I understand why the 
>> "bug" exists in the first place.
>>
>> BTW, according to your explanations, does this mean that we can/should 
>> treat the "available_externally" definitions exactly in the same way 
>> as just "external"? I understand that probably that is not specified 
>> precisely in the manual (and no standard like C++ covers the behavior 
>> in this case, unlike "_odr").
>>
> I'm not sure what you mean and why it matters. The lang ref spell out 
> their semantics, neither is interposable as far as I can tell.
> 
> 
>> Should I now submit a bug report in order for us to proceed or you can 
>> do it yourself?
>>
> I don't need a bug report but having one to keep track doesn't hurt, 
> especially if we don't work on it right away.
> Feel free to create one.
> 
> ~ Johannes
> 
> 
>> On 25-Oct-20 9:37 PM, Johannes Doerfert wrote:
>>>
>>> IPConstProp was not in the default optimization pipeline for a long time
>>> and has been removed in LLVM11 (or shortly after).
>>>
>>> Both the Attributor nor IPSCCP perform the transformations IPConstProp
>>> did, though neither handles your case right now. The Attributor will not
>>> propagate information inter-procedurally, the relevant code in
>>> Attrinbutor.h (line 2190) describes the "problem" already:
>>>
>>>       bool IsFnInterface = IRP.isFnInterfaceKind();
>>>       const Function *FnScope = IRP.getAnchorScope();
>>>       // TODO: Not all attributes require an exact definition. Find a 
>>> way to
>>>       //       enable deduction for some but not all attributes in 
>>> case the
>>>       //       definition might be changed at runtime, see also
>>>       // 
>>> http://lists.llvm.org/pipermail/llvm-dev/2018-February/121275.html.
>>>       // TODO: We could always determine abstract attributes and if 
>>> sufficient
>>>       //       information was found we could duplicate the functions 
>>> that do not
>>>       //       have an exact definition.
>>>       if (IsFnInterface && (!FnScope || 
>>> !A.isFunctionIPOAmendable(*FnScope)))
>>>        this->getState().indicatePessimisticFixpoint();
>>>
>>> Note that we actually have code to do the duplication, though I need to
>>> push some fixes for this "deep wrapper" generation I have prepared
>>> locally.
>>>
>>> What you cannot do is, just as a simple example, derive readnone for
>>> a function, e.g.,
>>>    int f(int *a) { return 123; }
>>>
>>> While it clearly doesn't read or write any memory, a less
>>> optimized equivalent version could, e.g., the original code might have
>>> looked like this:
>>>
>>>    int f(int *a) { return *a ? 123 : *a + 123; }
>>>
>>> which clearly reads memory. You can play this game with various other
>>> properties as well. However, the observed return value should never be
>>> different between equivalent versions of the function (up to
>>> non-deterministic choices) and I therefore think the return value can be
>>> propagated.
>>>
>>> If you want to get your hands dirty and teach the Attributor about it,
>>> that would be great. I would probably go with a method in
>>> AbstractAttribute that can be overwritten if the Attribute is OK with
>>> _odr linkage on function interface positions. The only time we overwrite
>>> would be in AAReturnedValues for now.
>>>
>>> Let me know what you think.
>>>
>>> ~ Johannes
>>>
>>> P.S. After I wrote this I wanted to make sure the information is
>>> correct. Turns out, AAReturnedValuesImpl::initialize does not call
>>> IRAttribute::initialize but instead basically duplicates the check. In
>>>    llvm/lib/Transforms/IPO/AttributorAttributes.cpp  line 821
>>> it says
>>>    if (!A.isFunctionIPOAmendable(*F))
>>>      indicatePessimisticFixpoint();
>>> which is equivalent to the above because AAReturnedValues only exist for
>>> function interface positions anyway. So maybe we can for now just look
>>> for _odr linkage there. Or better, provide an argument to
>>> isFunctionIPOAmendable that determines if _odr is OK or not.
>>>
>>>
>>>
>>>
>>> On 10/25/20 3:24 PM, Alex P. wrote:
>>>  > Hi Johannes, thanks for reply. I suspected that ipconstprop was 
>>> not active in -O3 mode, but I did not know it was deprecated at all. 
>>> However, either -O3 or -ipsccp behave the same way.
>>>  >
>>>  > BTW what other inter-procedural deductions should not apply for 
>>> _odr linkage? As far as I understand, an _odr definition is quite 
>>> similar to an extern definition semantically (well, according to 
>>> C++'s definition of ODR rule)...
>>>  >
>>>  > On 25-Oct-20 12:08 PM, Johannes Doerfert wrote:
>>>  >> Hi Alex,
>>>  >>
>>>  >> this is a "bug", as far as I can tell.
>>>  >>
>>>  >> `_odr` linkage should allow inter-procedural propagation of 
>>> constant returns,
>>>  >> though prevent other inter-procedural deductions. This is why we 
>>> are a bit
>>>  >> cautious with these things.
>>>  >>
>>>  >> I won't fix ipconstprop because we actually removed it but I will 
>>> look into an
>>>  >> extension of the Attributor to allow this. IPSCCP can probably 
>>> also be taught to
>>>  >> do this.
>>>  >>
>>>  >> ~ Johannes
>>>  >>
>>>  >>
>>>  >> On 10/23/20 10:40 PM, Alex P. via llvm-dev wrote:
>>>  >>> Dear LLVM developers and adopters!
>>>  >>>
>>>  >>> $ cat ipcp-1.ll
>>>  >>> define
>>>  >>> ;linkonce_odr
>>>  >>> dso_local i32 @f() noinline {
>>>  >>>    ret i32 123
>>>  >>> }
>>>  >>> define dso_local i32 @g()
>>>  >>> {
>>>  >>>    %res = call i32 @f()
>>>  >>>    ret i32 %res
>>>  >>> }
>>>  >>> $ opt-10 -S -ipconstprop ipcp-1.ll
>>>  >>> ; ModuleID = 'ipcp-1.ll'
>>>  >>> source_filename = "ipcp-1.ll"
>>>  >>>
>>>  >>> ; Function Attrs: noinline
>>>  >>> define dso_local i32 @f() #0 {
>>>  >>>   ret i32 123
>>>  >>> }
>>>  >>>
>>>  >>> define dso_local i32 @g() {
>>>  >>>   %res = call i32 @f()
>>>  >>>   ret i32 123 <========== note the result
>>>  >>> }
>>>  >>>
>>>  >>> attributes #0 = { noinline }
>>>  >>>
>>>  >>> BUT:
>>>  >>>
>>>  >>> $ cat ipcp-2.ll
>>>  >>> define
>>>  >>> linkonce_odr
>>>  >>> dso_local i32 @f() noinline {
>>>  >>>    ret i32 123
>>>  >>> }
>>>  >>> define dso_local i32 @g()
>>>  >>> {
>>>  >>>    %res = call i32 @f()
>>>  >>>    ret i32 %res
>>>  >>> }
>>>  >>> $ opt-10 -S -ipconstprop ipcp-2.ll
>>>  >>> ; ModuleID = 'ipcp-2.ll'
>>>  >>> source_filename = "ipcp-2.ll"
>>>  >>>
>>>  >>> ; Function Attrs: noinline
>>>  >>> define linkonce_odr dso_local i32 @f() #0 {
>>>  >>>   ret i32 123
>>>  >>> }
>>>  >>>
>>>  >>> define dso_local i32 @g() {
>>>  >>>   %res = call i32 @f()
>>>  >>>   ret i32 %res <========== note the (lack of) result
>>>  >>> }
>>>  >>>
>>>  >>> attributes #0 = { noinline }
>>>  >>>
>>>  >>> WHY? It this a bug?
>>>  >>>
>>>  >>> I observe the same behavior if I replace "-ipconstprop" with 
>>> "-O3" or replace "linkonce_odr" with "available_externally", and if I 
>>> use an equivalent testcase in C++ (compiled with the clang++ 
>>> frontend). No problem with "external", "private" or "hidden" 
>>> linkages. Also note that those "linkonce_odr"/"available_externally" 
>>> do not inhibit, e.g., inlining (if I remove "noinline"), that is, as 
>>> implied from the IR documentation.
>>>  >>>
>>>  >>> I am using LLVM version 10.0.0.
>>>  >>>
>>>  >>> This is a showstopper for my project (actually trying to use 
>>> LLVM as an affordable static type inferer for a dynamically typed PL).
>>>  >>>
>>>  >>> Thanks for any help
>>>  >
>>>
>>

-- 
Alex <alex at webprise.net>


More information about the llvm-dev mailing list