[cfe-dev] RFC: Supported Optimizations attribute

Piotr Padlewski via cfe-dev cfe-dev at lists.llvm.org
Sun Dec 2 09:47:16 PST 2018


Hi folks,

please check out our RFC: Supported Optimizations attribute

https://docs.google.com/document/d/1s0n-JVaSNML1Z9SCZVg2bgisxswIJAK2Tp9DahucW10/edit?usp=sharing

Pasting it here for the record:

RFC: supported_optimizations attribute

Piotr Padlewski - piotr.padlewski at gmail.com
Krzysztof Pszeniczny - kpszeniczny at google.com
December 2018

Introduction

Sometimes a possible class of optimizations requires very precise use of
metadata and/or intrinsics, at least to achieve a clean implementation
thereof.

Our primary example is devirtualization[RFC: Devirtualization v2
<https://docs.google.com/document/d/16GVtCpzK8sIHNc2qZz6RN8amICNBtvjWUod2SujZVEo/edit>],
which requires that the LLVM IR be annotated with certain intrinsic calls,
viz. launder.invariant.group and strip.invariant.group, in certain places,
e.g. when the dynamic type of an object changes.

This currently makes it impossible to merge IR created with this kind of
optimization in mind and without it, as this can lead to miscompilation
when the optimizer relies on the lack of intrinsics and/or metadata to make
certain assumptions about the code in question.  For now, in case of
devirtualization, we plainly refuse to merge such pieces of IR. This
predominantly affects cross-language link-time optimization.
Solution

We propose a fine-grained, function-level solution to this problem,
originally suggested by John McCall: mark each function with a list of such
optimization requirements it complies with, by the means of an attribute
for now called supported_optimizations.  The exact naming of this attribute
is of course subject to bikeshedding, so we kindly request the community to
provide us with a better name, if found.

When performing inter-procedural optimizations (mostly inlining), the list
of supported optimizations by the both functions should be considered to be
to the intersection of their previous lists of supported optimizations.
This effectively hinders any optimization relying on precise annotations
from happening if such precise annotations can no longer be guaranteed.

More precisely, when doing an IPO other than inlining, such a pass must
treat the functions in question as if they had the supported optimizations
lists set to the intersection of their original ones.  This in general
obviously does not require modifying those lists.

In the case of inlining, the list of optimizations supported by the callee
is left intact, but the list of optimizations supported by the caller must
be set to the said intersection.

invariant.group

The !invariant.group metadata should be equipped with an argument referring
to the supported_optimization it is related to. In the existing case, it
will be set to "clang.cxx.devirtualization".  If this
supported_optimization is not present in the list of optimizations
supported by the enclosing function, the !invariant.group metadata has no
effect and cannot be relied upon by the optimizer.  Of course, in this case
the metadata in question is not useful anymore and can be safely removed.
We kindly thank Richard Smith for suggesting this approach.

As a transitional measure and for backwards compatibility reasons, any
!invariant.group metadata with an empty argument (i.e. as before this RFC),
shall not be subject to the above restrictions and shall remain applicable
even when there is no supported_optimizations list provided for the
enclosing function.
Example:

define void @with_devirtualization(i8* %p) supported_optimizations =
"clang.cxx.devirtualization" {
 %v1 = load i8, i8* %p, !invariant.group !0
 %p2 = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
 call void @without_devirtualization(i8* %p, i8* %p2)
 %v2 = load i8, i8* %p2, !invariant.group !0
 ret void
}

define void @without_devirtualization(i8* %p, i8* %p2) {
  %b = icmp eq i8* %p, %p2
  call void @llvm.assume(i1 %b)
  ret void
}

!0 = !{!"clang.cxx.devirtualization"}
declare void @llvm.assume(i1)
declare i8* @llvm.launder.invariant.group.p0i8(i8*)

After inlining:

define void @with_devirtualization(i8* %p) {
 ; !invariant.group is inactive now, so it can be stripped.
 %v1 = load i8, i8* %p, !invariant.group !0
 %p2 = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
 %b = icmp eq i8* %p, %p2
 call void @llvm.assume(i1 %b)
 %v2 = load i8, i8* %p2, !invariant.group !0
 ret void
}

Possible extensions

Instead of indiscriminately taking the intersection of supported
optimizations' lists, we may imagine that some of such optimizations may be
able to provide a conservative set of annotations to a function lacking
them.  By doing so, we may retain at least some level of information
available to the optimizer, by preserving the annotations already present
in the optimization-compliant function.

For example, devirtualization may conservatively pass every load and
function argument through a strip and every store and return value through
a launder, which is a way of conservatively making an arbitrary function
compliant with the requirements of this particular optimization.
Bikeshedding

Other possible names:

   -

   compilant_with
   -

   brittle_representations (John McCall's original suggestion)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20181202/c900a6f9/attachment.html>


More information about the cfe-dev mailing list