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

Johannes Doerfert via llvm-dev llvm-dev at lists.llvm.org
Sun Oct 25 19:37:14 PDT 2020


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
 >



More information about the llvm-dev mailing list