[llvm] r278875 - [Docs] Add initial MemorySSA documentation.

George Burgess IV via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 16 18:59:45 PDT 2016


That's what I get for trying to update the examples without running them
through `opt` again. :)

Thanks for the feedback! Fixed in r278885.

On Tue, Aug 16, 2016 at 6:47 PM, Sean Silva <chisophugis at gmail.com> wrote:

> This is great! Thanks for doing this. Some small comments inline.
>
> On Tue, Aug 16, 2016 at 5:17 PM, George Burgess IV via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> Author: gbiv
>> Date: Tue Aug 16 19:17:29 2016
>> New Revision: 278875
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=278875&view=rev
>> Log:
>> [Docs] Add initial MemorySSA documentation.
>>
>> Patch partially by Danny.
>>
>> Differential Revision: https://reviews.llvm.org/D23535
>>
>> Added:
>>     llvm/trunk/docs/MemorySSA.rst
>> Modified:
>>     llvm/trunk/docs/AliasAnalysis.rst
>>     llvm/trunk/docs/index.rst
>>
>> Modified: llvm/trunk/docs/AliasAnalysis.rst
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/AliasAna
>> lysis.rst?rev=278875&r1=278874&r2=278875&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/docs/AliasAnalysis.rst (original)
>> +++ llvm/trunk/docs/AliasAnalysis.rst Tue Aug 16 19:17:29 2016
>> @@ -702,6 +702,12 @@ algorithm will have a lower number of ma
>>  Memory Dependence Analysis
>>  ==========================
>>
>> +.. note::
>> +
>> +  We are currently in the process of migrating things from
>> +  ``MemoryDependenceAnalysis`` to :doc:`MemorySSA`. Please try to use
>> +  that instead.
>> +
>>  If you're just looking to be a client of alias analysis information,
>> consider
>>  using the Memory Dependence Analysis interface instead.  MemDep is a
>> lazy,
>>  caching layer on top of alias analysis that is able to answer the
>> question of
>>
>> Added: llvm/trunk/docs/MemorySSA.rst
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/MemorySS
>> A.rst?rev=278875&view=auto
>> ============================================================
>> ==================
>> --- llvm/trunk/docs/MemorySSA.rst (added)
>> +++ llvm/trunk/docs/MemorySSA.rst Tue Aug 16 19:17:29 2016
>> @@ -0,0 +1,358 @@
>> +=========
>> +MemorySSA
>> +=========
>> +
>> +.. contents::
>> +   :local:
>> +
>> +Introduction
>> +============
>> +
>> +``MemorySSA`` is an analysis that allows us to cheaply reason about the
>> +interactions between various memory operations. Its goal is to replace
>> +``MemoryDependenceAnalysis`` for most (if not all) use-cases. This is
>> because,
>> +unless you're very careful, use of ``MemoryDependenceAnalysis`` can
>> easily
>> +result in quadratic-time algorithms in LLVM. Additionally, ``MemorySSA``
>> doesn't
>> +have as many arbitrary limits as ``MemoryDependenceAnalysis``, so you
>> should get
>> +better results, too.
>> +
>> +At a high level, one of the goals of ``MemorySSA`` is to provide an SSA
>> based
>> +form for memory, complete with def-use and use-def chains, which
>> +enables users to quickly find may-def and may-uses of memory operations.
>> +It can also be thought of as a way to cheaply give versions to the
>> complete
>> +state of heap memory, and associate memory operations with those
>> versions.
>> +
>> +This document goes over how ``MemorySSA`` is structured, and some basic
>> +intuition on how ``MemorySSA`` works.
>> +
>> +A paper on MemorySSA (with notes about how it's implemented in GCC) `can
>> be
>> +found here <http://www.airs.com/dnovillo/Papers/mem-ssa.pdf>`_. Though,
>> it's
>> +relatively out-of-date; the paper references multiple heap partitions,
>> but GCC
>> +eventually swapped to just using one, like we now have in LLVM.  Like
>> +GCC's, LLVM's MemorySSA is intraprocedural.
>> +
>> +
>> +MemorySSA Structure
>> +===================
>> +
>> +MemorySSA is a virtual IR. After it's built, ``MemorySSA`` will contain a
>> +structure that maps ``Instruction`` s to ``MemoryAccess`` es, which are
>> +``MemorySSA``'s parallel to LLVM ``Instruction`` s.
>> +
>> +Each ``MemoryAccess`` can be one of three types:
>> +
>> +- ``MemoryPhi``
>> +- ``MemoryUse``
>> +- ``MemoryDef``
>> +
>> +``MemoryPhi`` s are ``PhiNode`` , but for memory operations. If at any
>> +point we have two (or more) ``MemoryDef`` s that could flow into a
>> +``BasicBlock``, the block's top ``MemoryAccess`` will be a
>> +``MemoryPhi``. As in LLVM IR, ``MemoryPhi`` s don't correspond to any
>> +concrete operation. As such, you can't look up a ``MemoryPhi`` with an
>> +``Instruction`` (though we do allow you to do so with a
>> +``BasicBlock``).
>> +
>> +Note also that in SSA, Phi nodes merge must-reach definitions (that
>> +is, definite new versions of variables).  In MemorySSA, PHI nodes merge
>> +may-reach definitions (that is, until disambiguated, the versions that
>> +reach a phi node may or may not clobber a given variable)
>> +
>> +``MemoryUse`` s are operations which use but don't modify memory. An
>> example of
>> +a ``MemoryUse`` is a ``load``, or a ``readonly`` function call.
>> +
>> +``MemoryDef`` s are operations which may either modify memory, or which
>> +otherwise clobber memory in unquantifiable ways. Examples of
>> ``MemoryDef`` s
>> +include ``store`` s, function calls, ``load`` s with ``acquire`` (or
>> higher)
>> +ordering, volatile operations, memory fences, etc.
>> +
>> +Every function that exists has a special ``MemoryDef`` called
>> ``liveOnEntry``.
>> +It dominates every ``MemoryAccess`` in the function that ``MemorySSA``
>> is being
>> +run on, and implies that we've hit the top of the function. It's the only
>> +``MemoryDef`` that maps to no ``Instruction`` in LLVM IR. Use of
>> +``liveOnEntry`` implies that the memory being used is either undefined or
>> +defined before the function begins.
>> +
>> +An example of all of this overlayed on LLVM IR (obtained by running ``opt
>> +-passes='print<memoryssa>' -disable-output`` on an ``.ll`` file) is
>> below. When
>> +viewing this example, it may be helpful to view it in terms of clobbers.
>> The
>> +operands of a given ``MemoryAccess`` are all (potential) clobbers of said
>> +MemoryAccess, and the value produced by a ``MemoryAccess`` can act as a
>> clobber
>> +for other ``MemoryAccess`` es. Another useful way of looking at it is in
>> +terms of heap versions.  In that view, operands of of a given
>> +``MemoryAccess`` are the version of the heap before the operation, and
>> +if the access produces a value, the value is the new version of the heap
>> +after the operation.
>> +
>> +.. code-block:: llvm
>> +
>> +  define void @foo() {
>> +  entry:
>> +    %p1 = alloca i8
>> +    %p2 = alloca i8
>> +    %p3 = alloca i8
>> +    ; 1 = MemoryDef(liveOnEntry)
>> +    store i8 0, i8* %p3
>> +    br label %while.cond
>> +
>> +  while.cond:
>> +    ; 6 = MemoryPhi({%0,1},{if.end,4})
>>
>
> I think `%0` should be `entry` here.
>
>
>
>> +    br i1 undef, label %if.then, label %if.else
>> +
>> +  if.then:
>> +    ; 2 = MemoryDef(6)
>> +    store i8 0, i8* %p1
>> +    br label %if.end
>> +
>> +  if.else:
>> +    ; 3 = MemoryDef(6)
>> +    store i8 1, i8* %p2
>> +    br label %if.end
>> +
>> +  if.end:
>> +    ; 5 = MemoryPhi({if.then,2},{if.then,3})
>>
>
> Should this be `{if.else,3}`?
>
>
>> +    ; MemoryUse(5)
>> +    %1 = load i8, i8* %p1
>> +    ; 4 = MemoryDef(5)
>> +    store i8 2, i8* %p2
>> +    ; MemoryUse(1)
>> +    %2 = load i8, i8* %p3
>> +    br label %while.cond
>> +  }
>> +
>> +The ``MemorySSA`` IR is located comments that precede the instructions
>> they map
>>
>
> This sounds like it should say: The ``MemorySSA`` IR comments are located
> such that they precede ...
>
>
>> +to (if such an instruction exists). For example, ``1 =
>> MemoryDef(liveOnEntry)``
>> +is a ``MemoryAccess`` (specifically, a ``MemoryDef``), and it describes
>> the LLVM
>> +instruction ``store i8 0, i8* %p3``. Other places in ``MemorySSA`` refer
>> to this
>> +particular ``MemoryDef`` as ``1`` (much like how one can refer to ``load
>> i8, i8*
>> +%p1`` in LLVM with ``%1``). Again, ``MemoryPhi`` s don't correspond to
>> any LLVM
>> +Instruction, so the line directly below a ``MemoryPhi`` isn't special.
>> +
>> +Going from the top down:
>> +
>> +- ``6 = MemoryPhi({%0,1},{if.end,4})`` notes that, when entering
>> ``while.cond``,
>>
>
> `%0` -> `entry` here also I think.
>
>
>> +  the reaching definition for it is either ``1`` or ``4``. This
>> ``MemoryPhi`` is
>> +  referred to in the textual IR by the number ``6``.
>> +- ``2 = MemoryDef(6)`` notes that ``store i8 0, i8* %p1`` is a
>> definition,
>> +  and its reaching definition before it is ``6``, or the ``MemoryPhi``
>> after
>> +  ``while.cond``.
>> +- ``3 = MemoryDef(6)`` notes that ``store i8 0, i8* %p2`` is a
>> definition; its
>> +  reaching definition is also ``6``.
>> +- ``5 = MemoryPhi({if.then,2},{if.then,3})`` notes that the clobber
>> before
>>
>
> `{if.else,3}` I think.
>
>
>> +  this block could either be ``2`` or ``3``.
>> +- ``MemoryUse(5)`` notes that ``load i8, i8* %p1`` is a use of memory,
>> and that
>> +  it's clobbered by ``5``.
>> +- ``4 = MemoryDef(5)`` notes that ``store i8 2, i8* %p2`` is a
>> definition; it's
>> +  reaching definition is ``5``.
>> +- ``MemoryUse(1)`` notes that ``load i8, i8* %p3`` is just a user of
>> memory,
>> +  and the last thing that could clobber this use is above ``while.cond``
>> (e.g.
>> +  the store to ``%p3``).  In heap versioning parlance, it really
>> +  only depends on the heap version 1, and is unaffected by the new
>> +  heap versions generated since then.
>> +
>> +As an aside, ``MemoryAccess`` is a ``Value`` mostly for convenience;
>> it's not
>> +meant to interact with LLVM IR.
>> +
>> +Design of MemorySSA
>> +===================
>> +
>> +``MemorySSA`` is an analysis that can be built for any arbitrary
>> function. When
>> +it's built, it does a pass over the function's IR in order to build up
>> its
>> +mapping of ``MemoryAccess`` es. You can then query ``MemorySSA`` for
>> things like
>> +the dominance relation between ``MemoryAccess`` es, and get the
>> ``MemoryAccess``
>> +for any given ``Instruction`` .
>> +
>> +When ``MemorySSA`` is done building, it also hands you a
>> ``MemorySSAWalker``
>> +that you can use (see below).
>> +
>> +
>> +The walker
>> +----------
>> +
>> +A structure that helps ``MemorySSA`` do its job is the
>> ``MemorySSAWalker``, or
>> +the walker, for short. The goal of the walker is to provide answers to
>> clobber
>> +queries beyond what's represented directly by ``MemoryAccess`` es. For
>> example,
>> +given:
>> +
>> +.. code-block:: llvm
>> +
>> +  define void @foo() {
>> +    %a = alloca i8
>> +    %b = alloca i8
>> +
>> +    ; 1 = MemoryDef(liveOnEntry)
>> +    store i8 0, i8* %a
>> +    ; 2 = MemoryDef(1)
>> +    store i8 0, i8* %b
>> +  }
>> +
>> +The store to ``%a`` is clearly not a clobber for the store to ``%b``. It
>> would
>> +be the walker's goal to figure this out, and return ``liveOnEntry`` when
>> queried
>> +for the clobber of ``MemoryAccess`` ``2``.
>> +
>> +By default, ``MemorySSA`` provides a walker that can optimize
>> ``MemoryDef`` s
>> +and ``MemoryUse`` s by consulting alias analysis. Walkers were built to
>> be
>> +flexible, though, so it's entirely reasonable (and expected) to create
>> more
>> +specialized walkers (e.g. one that queries ``GlobalsAA``).
>> +
>> +
>> +Locating clobbers yourself
>> +^^^^^^^^^^^^^^^^^^^^^^^^^^
>> +
>> +If you choose to make your own walker, you can find the clobber for a
>> +``MemoryAccess`` by walking every ``MemoryDef`` that dominates said
>> +``MemoryAccess``. The structure of ``MemoryDef`` s makes this relatively
>> simple;
>> +they ultimately form a linked list of every clobber that dominates the
>> +``MemoryAccess`` that you're trying to optimize. In other words, the
>> +``definingAccess`` of a ``MemoryDef`` is always the nearest dominating
>> +``MemoryDef`` or ``MemoryPhi`` of said ``MemoryDef``.
>> +
>> +
>> +Use optimization
>> +----------------
>> +
>> +``MemorySSA`` will optimize some ``MemoryAccess`` es at build-time.
>> +Specifically, we optimize the operand of every ``MemoryUse`` s to point
>> to the
>>
>
> MemoryUse should be non-plural here.
>
>
>> +actual clobber of said ``MemoryUse``. This can be seen in the above
>> example; the
>> +second ``MemoryUse`` in ``if.end`` has an operand of ``1``, which is a
>> +``MemoryDef`` from the entry block.  This is done to make walking,
>> +value numbering, etc, faster and easier.
>> +It is not possible to optimize ``MemoryDef`` in the same way, as we
>> +restrict ``MemorySSA`` to one heap variable and, thus, one Phi node
>> +per block.
>> +
>> +
>> +Invalidation and updating
>> +-------------------------
>> +
>> +Because ``MemorySSA`` keeps track of LLVM IR, it needs to be updated
>> whenever
>> +the IR is updated. "Update", in this case, includes the addition,
>> deletion, and
>> +motion of IR instructions. The update API is being made on an as-needed
>> basis.
>>
>
> You may want to mention any examples of in-tree optimizations that
> currently do updating.
>
>
>> +
>> +
>> +Phi placement
>> +^^^^^^^^^^^^^
>> +
>> +``MemorySSA`` only places ``MemoryPhi`` s where they're actually
>> +needed. That is, it is a pruned SSA form, like LLVM's SSA form.  For
>> +example, consider:
>> +
>> +.. code-block:: llvm
>> +
>> +  define void @foo() {
>> +  entry:
>> +    %p1 = alloca i8
>> +    %p2 = alloca i8
>> +    %p3 = alloca i8
>> +    ; 1 = MemoryDef(liveOnEntry)
>> +    store i8 0, i8* %p3
>> +    br label %while.cond
>> +
>> +  while.cond:
>> +    ; 3 = MemoryPhi({%0,1},{if.end,2})
>> +    br i1 undef, label %if.then, label %if.else
>> +
>> +  if.then:
>> +    br label %if.end
>> +
>> +  if.else:
>> +    br label %if.end
>> +
>> +  if.end:
>> +    ; MemoryUse(1)
>> +    %1 = load i8, i8* %p1
>> +    ; 2 = MemoryDef(3)
>> +    store i8 2, i8* %p2
>> +    ; MemoryUse(1)
>> +    %2 = load i8, i8* %p3
>> +    br label %while.cond
>> +  }
>> +
>> +Because we removed the stores from ``if.then`` and ``if.else``, a
>> ``MemoryPhi``
>> +for ``if.end`` would be pointless, so we don't place one. So, if you
>> need to
>> +place a ``MemoryDef`` in ``if.then`` or ``if.else``, you'll need to also
>> create
>> +a ``MemoryPhi`` for ``if.end``.
>> +
>> +If it turns out that this is a large burden, we can just place
>> ``MemoryPhi`` s
>> +everywhere. Because we have Walkers that are capable of optimizing above
>> said
>> +phis, doing so shouldn't prohibit optimizations.
>> +
>> +
>> +Non-Goals
>> +---------
>> +
>> +``MemorySSA`` is meant to reason about the relation between memory
>> +operations, and enable quicker querying.
>> +It isn't meant to be the single source of truth for all potential
>> memory-related
>> +optimizations. Specifically, care must be taken when trying to use
>> ``MemorySSA``
>> +to reason about atomic or volatile operations, as in:
>> +
>> +.. code-block:: llvm
>> +
>> +  define i8 @foo(i8* %a) {
>> +  entry:
>> +    br i1 undef, label %if.then, label %if.end
>> +
>> +  if.then:
>> +    ; 1 = MemoryDef(liveOnEntry)
>> +    %0 = load volatile i8, i8* %a
>> +    br label %if.end
>> +
>> +  if.end:
>> +    %av = phi i8 [0, %entry], [%0, %if.then]
>> +    ret i8 %av
>> +  }
>> +
>> +Going solely by ``MemorySSA``'s analysis, hoisting the ``load`` to
>> ``entry`` may
>> +seem legal. Because it's a volatile load, though, it's not.
>> +
>> +
>> +Design tradeoffs
>> +----------------
>> +
>> +Precision
>> +^^^^^^^^^
>> +``MemorySSA`` in LLVM deliberately trades off precision for speed.
>> +Let us think about memory variables as if they were disjoint partitions
>> of the
>> +heap (that is, if you have one variable, as above, it represents the
>> entire
>> +heap, and if you have multiple variables, each one represents some
>> +disjoint portion of the heap)
>> +
>> +First, because alias analysis results conflict with each other, and
>> +each result may be what an analysis wants (IE
>> +TBAA may say no-alias, and something else may say must-alias), it is
>> +not possible to partition the heap the way every optimization wants.
>> +Second, some alias analysis results are not transitive (IE A noalias B,
>> +and B noalias C, does not mean A noalias C), so it is not possible to
>> +come up with a precise partitioning in all cases without variables to
>> +represent every pair of possible aliases.  Thus, partitioning
>> +precisely may require introducing at least N^2 new virtual variables,
>> +phi nodes, etc.
>> +
>> +Each of these variables may be clobbered at multiple def sites.
>> +
>> +To give an example, if you were to split up struct fields into
>> +individual variables, all aliasing operations that may-def multiple
>> struct
>> +fields, will may-def more than one of them.  This is pretty common
>> (calls,
>> +copies, field stores, etc).
>> +
>> +Experience with SSA forms for memory in other compilers has shown that
>> +it is simply not possible to do this precisely, and in fact, doing it
>> +precisely is not worth it, because now all the optimizations have to
>> +walk tons and tons of virtual variables and phi nodes.
>> +
>> +So we partition.  At the point at which you partition, again,
>> +experience has shown us there is no point in partitioning to more than
>> +one variable.  It simply generates more IR, and optimizations still
>> +have to query something to disambiguate further anyway.
>> +
>> +As a result, LLVM partitions to one variable.
>> +
>> +Use Optimization
>> +^^^^^^^^^^^^^^^^
>> +
>> +Unlike other partitioned forms, LLVM's ``MemorySSA`` does make one
>> +useful guarantee - all loads are optimized to point at the thing that
>> +actually clobbers them. This gives some nice properties.  For example,
>> +for a given store, you can find all loads actually clobbered by that
>> +store by walking the immediate uses of the store.
>>
>> Modified: llvm/trunk/docs/index.rst
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/index.rs
>> t?rev=278875&r1=278874&r2=278875&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/docs/index.rst (original)
>> +++ llvm/trunk/docs/index.rst Tue Aug 16 19:17:29 2016
>> @@ -235,6 +235,7 @@ For API clients and LLVM developers.
>>     :hidden:
>>
>>     AliasAnalysis
>> +   MemorySSA
>>     BitCodeFormat
>>     BlockFrequencyTerminology
>>     BranchWeightMetadata
>> @@ -291,6 +292,9 @@ For API clients and LLVM developers.
>>     Information on how to write a new alias analysis implementation or
>> how to
>>     use existing analyses.
>>
>> +:doc:`MemorySSA`
>> +   Information about the MemorySSA utility in LLVM, as well as how to
>> use it.
>> +
>>  :doc:`GarbageCollection`
>>     The interfaces source-language compilers should use for compiling GC'd
>>     programs.
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20160816/70cd889e/attachment.html>


More information about the llvm-commits mailing list