[llvm] 187a5f2 - [docs] Add details to MemorySSA docs.

Alina Sbirlea via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 17 15:25:50 PDT 2022


Author: Alina Sbirlea
Date: 2022-03-17T15:25:09-07:00
New Revision: 187a5f230f4b5d6602cfce163e7376426c31a9f2

URL: https://github.com/llvm/llvm-project/commit/187a5f230f4b5d6602cfce163e7376426c31a9f2
DIFF: https://github.com/llvm/llvm-project/commit/187a5f230f4b5d6602cfce163e7376426c31a9f2.diff

LOG: [docs] Add details to MemorySSA docs.

Add more details to the docs regarding optimized accesses for Uses and Defs.
Include incoming changes from https://reviews.llvm.org/D121381.

Differential Revision: https://reviews.llvm.org/D121740

Added: 
    

Modified: 
    llvm/docs/MemorySSA.rst

Removed: 
    


################################################################################
diff  --git a/llvm/docs/MemorySSA.rst b/llvm/docs/MemorySSA.rst
index b8e8af7b3c67b..2052531d619a5 100644
--- a/llvm/docs/MemorySSA.rst
+++ b/llvm/docs/MemorySSA.rst
@@ -256,20 +256,78 @@ they ultimately form a linked list of every clobber that dominates the
 ``MemoryDef`` or ``MemoryPhi`` of said ``MemoryDef``.
 
 
-Build-time use optimization
----------------------------
-
-``MemorySSA`` will optimize some ``MemoryAccess``\ es at build-time.
-Specifically, we optimize the operand of every ``MemoryUse`` to point to the
+Use and Def optimization
+------------------------
+
+``MemoryUse``\ s keep a single operand, which is their defining or optimized
+access.
+Traditionally ``MemorySSA`` optimized ``MemoryUse``\ s at build-time, up to a
+given threshold.
+Specifically, the operand of every ``MemoryUse`` was optimized to point to the
 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.
+As of `this revision <https://reviews.llvm.org/D121381>`_, the default was
+changed to not optimize uses at build time, in order to provide the option to
+reduce compile-time if the walking is not necessary in a pass. Most users call
+the new API ``ensureOptimizedUses()`` to keep the previous behavior and do a
+one-time optimization of ``MemoryUse``\ s, if this was not done before.
+New pass users are recommended to call ``ensureOptimizedUses()``.
+
+Initially it was not possible to optimize ``MemoryDef``\ s in the same way, as we
+restricted ``MemorySSA`` to one operand per access.
+This was changed and ``MemoryDef``\ s now keep two operands.
+The first one, the defining access, is
+always the previous ``MemoryDef`` or ``MemoryPhi`` in the same basic block, or
+the last one in a dominating predecessor if the current block doesn't have any
+other accesses writing to memory. This is needed for walking Def chains.
+The second operand is the optimized access, if there was a previous call on the
+walker's ``getClobberingMemoryAccess(MA)``. This API will cache information
+as part of ``MA``.
+Optimizing all ``MemoryDef``\ s has quadratic time complexity and is not done
+by default.
+
+A walk of the uses for any MemoryDef can find the accesses that were optimized
+to it.
+A code snippet for such a walk looks like this:
+
+.. code-block:: c++
+  MemoryDef *Def;  // find who's optimized or defining for this MemoryDef
+  for (auto& U : Def->uses()) {
+    MemoryAccess *MA = cast<MemoryAccess>(Use.getUser());
+    if (auto *DefUser = cast_of_null<MemoryDef>MA)
+      if (DefUser->isOptimized() && DefUser->getOptimized() == Def) {
+        // User who is optimized to Def
+      } else {
+        // User who's defining access is Def; optimized to something else or not optimized.
+      }
+  }
 
-It is not possible to optimize ``MemoryDef`` in the same way, as we
-restrict ``MemorySSA`` to one memory variable and, thus, one Phi node
-per block.
+When ``MemoryUse``\ s are optimized, for a given store,  you can find all loads
+clobbered by that store by walking the immediate and transitive uses of
+the store.
+
+.. code-block:: c++
+  checkUses(MemoryAccess *Def) { // Def can be a MemoryDef or a MemoryPhi.
+    for (auto& U : Def->uses()) {
+      MemoryAccess *MA = cast<MemoryAccess>(Use.getUser());
+      if (auto *MU = cast_of_null<MemoryUse>MA) {
+        // Process MemoryUse as needed.
+      }
+      else {
+        // Process MemoryDef or MemoryPhi as needed.
+
+        // As a user can come up twice, as an optimized access and defining
+        // access, keep a visited list.
+
+        // Check transitive uses as needed
+        checkUses (MA); // use a worklist for an iterative algorithm
+      }
+    }
+  }
 
+An example of similar traversals can be found in the DeadStoreElimination pass.
 
 Invalidation and updating
 -------------------------
@@ -277,7 +335,11 @@ 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 ``Instructions``. The update API is being made on an as-needed basis.
-If you'd like examples, ``GVNHoist`` is a user of ``MemorySSA``\ s update API.
+If you'd like examples, ``GVNHoist`` and ``LICM`` are users of ``MemorySSA``\ s
+update API.
+Note that adding new ``MemoryDef``\ s (by calling ``insertDef``) can be a
+time-consuming update, if the new access triggers many ``MemoryPhi`` insertions and
+renaming (optimization invalidation) of many ``MemoryAccesses``\ es.
 
 
 Phi placement
@@ -403,34 +465,25 @@ Precision in practice
 ^^^^^^^^^^^^^^^^^^^^^
 
 In practice, there are implementation details in LLVM that also affect the
-results' precision provided by MemorySSA. For example, AliasAnalysis has various
-caps, or restrictions on looking through phis which can affect what MemorySSA
+results' precision provided by ``MemorySSA``. For example, AliasAnalysis has various
+caps, or restrictions on looking through phis which can affect what ``MemorySSA``
 can infer. Changes made by 
diff erent passes may make MemorySSA either "overly
 optimized" (it can provide a more acccurate result than if it were recomputed
 from scratch), or "under optimized" (it could infer more if it were recomputed).
 This can lead to challenges to reproduced results in isolation with a single pass
-when the result relies on the state aquired by MemorySSA due to being updated by
+when the result relies on the state aquired by ``MemorySSA`` due to being updated by
 multiple subsequent passes.
-Passes that use and update MemorySSA should do so through the APIs provided by the
-MemorySSAUpdater, or through calls on the Walker.
-Direct optimizations to MemorySSA are not permitted.
+Passes that use and update ``MemorySSA`` should do so through the APIs provided by the
+``MemorySSAUpdater``, or through calls on the Walker.
+Direct optimizations to ``MemorySSA`` are not permitted.
 There is currently a single, narrowly scoped exception where DSE (DeadStoreElimination)
 updates an optimized access of a store, after a traversal that guarantees the
 optimization is correct. This is solely allowed due to the traversals and inferences
-being beyond what MemorySSA does and them being "free" (i.e. DSE does them anyway).
+being beyond what ``MemorySSA`` does and them being "free" (i.e. DSE does them anyway).
 This exception is set under a flag ("-dse-optimize-memoryssa") and can be disabled to
 help reproduce optimizations in isolation.
 
 
-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.
-
 LLVM Developers Meeting presentations
 -------------------------------------
 


        


More information about the llvm-commits mailing list