[docs] Migrate 20 LLVM docs to markdown (#201465)
An LLM assisted in the rewrite. I didn't use pandoc or any other tools, and I think there are fewer artifacts. I validated these docs by opening all 20 HTML files in my browser. Files: - DebuggingLLVM.md - FAQ.md - CompilerWriterInfo.md - GettingStartedVS.md - NewPassManager.md - MemorySSA.md - LoopTerminology.md - Lexicon.md - MIRLangRef.md - CoverageMappingFormat.md - InstrProfileFormat.md - BitCodeFormat.md - Atomics.md - GarbageCollection.md - AliasAnalysis.md - LibFuzzer.md - Extensions.md - CommandLine.md - ExceptionHandling.md - Coroutines.md Tracking issue: #201242
This commit is contained in:
parent
638f3ae61d
commit
cab8d8c9e2
20 changed files with 6467 additions and 7161 deletions
|
|
@ -1,12 +1,10 @@
|
|||
==================================
|
||||
LLVM Alias Analysis Infrastructure
|
||||
==================================
|
||||
# LLVM Alias Analysis Infrastructure
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
Introduction
|
||||
============
|
||||
## Introduction
|
||||
|
||||
Alias Analysis (aka Pointer Analysis) is a class of techniques which attempt to
|
||||
determine whether or not two pointers ever can point to the same object in
|
||||
|
|
@ -14,12 +12,11 @@ memory. There are many different algorithms for alias analysis and many
|
|||
different ways of classifying them: flow-sensitive vs. flow-insensitive,
|
||||
context-sensitive vs. context-insensitive, field-sensitive
|
||||
vs. field-insensitive, unification-based vs. subset-based, etc. Traditionally,
|
||||
alias analyses respond to a query with a `Must, May, or No`_ alias response,
|
||||
alias analyses respond to a query with a {ref}`Must, May, or No <Must, May, or No>` alias response,
|
||||
indicating that two pointers always point to the same object, might point to the
|
||||
same object, or are known to never point to the same object.
|
||||
|
||||
The LLVM `AliasAnalysis
|
||||
<https://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html>`__ class is the
|
||||
The LLVM [AliasAnalysis](https://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html) class is the
|
||||
primary interface used by clients and implementations of alias analyses in the
|
||||
LLVM system. This class is the common interface between clients of alias
|
||||
analysis information and the implementations providing it, and is designed to
|
||||
|
|
@ -33,91 +30,87 @@ This document contains information necessary to successfully implement this
|
|||
interface, use it, and to test both sides. It also explains some of the finer
|
||||
points about what exactly results mean.
|
||||
|
||||
``AliasAnalysis`` Class Overview
|
||||
================================
|
||||
## `AliasAnalysis` Class Overview
|
||||
|
||||
The `AliasAnalysis <https://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html>`__
|
||||
The [AliasAnalysis](https://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html)
|
||||
class defines the interface that the various alias analysis implementations
|
||||
should support. This class exports two important enums: ``AliasResult`` and
|
||||
``ModRefResult`` which represent the result of an alias query or a mod/ref
|
||||
should support. This class exports two important enums: `AliasResult` and
|
||||
`ModRefResult` which represent the result of an alias query or a mod/ref
|
||||
query, respectively.
|
||||
|
||||
The ``AliasAnalysis`` interface exposes information about memory, represented in
|
||||
The `AliasAnalysis` interface exposes information about memory, represented in
|
||||
several different ways. In particular, memory objects are represented as a
|
||||
starting address and size, and function calls are represented as the actual
|
||||
``call`` or ``invoke`` instructions that perform the call. The
|
||||
``AliasAnalysis`` interface also exposes some helper methods which allow you to
|
||||
`call` or `invoke` instructions that perform the call. The
|
||||
`AliasAnalysis` interface also exposes some helper methods which allow you to
|
||||
get mod/ref information for arbitrary instructions.
|
||||
|
||||
All ``AliasAnalysis`` interfaces require that in queries involving multiple
|
||||
values, values which are not :ref:`constants <constants>` are all
|
||||
All `AliasAnalysis` interfaces require that in queries involving multiple
|
||||
values, values which are not {ref}`constants <constants>` are all
|
||||
defined within the same function.
|
||||
|
||||
Representation of Pointers
|
||||
--------------------------
|
||||
### Representation of Pointers
|
||||
|
||||
Most importantly, the ``AliasAnalysis`` class provides several methods which are
|
||||
Most importantly, the `AliasAnalysis` class provides several methods which are
|
||||
used to query whether or not two memory objects alias, whether function calls
|
||||
can modify or read a memory object, etc. For all of these queries, memory
|
||||
objects are represented as a pair of their starting address (a symbolic LLVM
|
||||
``Value*``) and a static size.
|
||||
`Value*`) and a static size.
|
||||
|
||||
Representing memory objects as a starting address and a size is critically
|
||||
important for correct Alias Analyses. For example, consider this (silly, but
|
||||
possible) C code:
|
||||
|
||||
.. code-block:: c++
|
||||
```c++
|
||||
int i;
|
||||
char C[2];
|
||||
char A[10];
|
||||
/* ... */
|
||||
for (i = 0; i != 10; ++i) {
|
||||
C[0] = A[i]; /* One byte store */
|
||||
C[1] = A[9-i]; /* One byte store */
|
||||
}
|
||||
```
|
||||
|
||||
int i;
|
||||
char C[2];
|
||||
char A[10];
|
||||
/* ... */
|
||||
for (i = 0; i != 10; ++i) {
|
||||
C[0] = A[i]; /* One byte store */
|
||||
C[1] = A[9-i]; /* One byte store */
|
||||
}
|
||||
|
||||
In this case, the ``basic-aa`` pass will disambiguate the stores to ``C[0]`` and
|
||||
``C[1]`` because they are accesses to two distinct locations one byte apart, and
|
||||
In this case, the `basic-aa` pass will disambiguate the stores to `C[0]` and
|
||||
`C[1]` because they are accesses to two distinct locations one byte apart, and
|
||||
the accesses are each one byte. In this case, the Loop Invariant Code Motion
|
||||
(LICM) pass can use store motion to remove the stores from the loop. In
|
||||
contrast, the following code:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int i;
|
||||
char C[2];
|
||||
char A[10];
|
||||
/* ... */
|
||||
for (i = 0; i != 10; ++i) {
|
||||
((short*)C)[0] = A[i]; /* Two byte store! */
|
||||
C[1] = A[9-i]; /* One byte store */
|
||||
}
|
||||
```c++
|
||||
int i;
|
||||
char C[2];
|
||||
char A[10];
|
||||
/* ... */
|
||||
for (i = 0; i != 10; ++i) {
|
||||
((short*)C)[0] = A[i]; /* Two byte store! */
|
||||
C[1] = A[9-i]; /* One byte store */
|
||||
}
|
||||
```
|
||||
|
||||
In this case, the two stores to C do alias each other, because the access to the
|
||||
``&C[0]`` element is a two byte access. If size information wasn't available in
|
||||
`&C[0]` element is a two byte access. If size information wasn't available in
|
||||
the query, even the first case would have to conservatively assume that the
|
||||
accesses alias.
|
||||
|
||||
.. _alias:
|
||||
(alias)=
|
||||
|
||||
The ``alias`` method
|
||||
--------------------
|
||||
### The `alias` method
|
||||
|
||||
The ``alias`` method is the primary interface used to determine whether or not
|
||||
The `alias` method is the primary interface used to determine whether or not
|
||||
two memory objects alias each other. It takes two memory objects as input and
|
||||
returns MustAlias, PartialAlias, MayAlias, or NoAlias as appropriate.
|
||||
|
||||
Like all ``AliasAnalysis`` interfaces, the ``alias`` method requires that either
|
||||
Like all `AliasAnalysis` interfaces, the `alias` method requires that either
|
||||
the two pointer values be defined within the same function, or at least one of
|
||||
the values is a :ref:`constant <constants>`.
|
||||
the values is a {ref}`constant <constants>`.
|
||||
|
||||
.. _Must, May, or No:
|
||||
(Must, May, or No)=
|
||||
|
||||
Must, May, and No Alias Responses
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### Must, May, and No Alias Responses
|
||||
|
||||
The ``NoAlias`` response may be used when there is never an immediate dependence
|
||||
The `NoAlias` response may be used when there is never an immediate dependence
|
||||
between any memory reference *based* on one pointer and any memory reference
|
||||
*based on* the other. The most obvious example is when the two pointers point to
|
||||
non-overlapping memory ranges. Another is when the two pointers are only ever
|
||||
|
|
@ -125,223 +118,208 @@ used for reading memory. Another is when the memory is freed and reallocated
|
|||
between accesses through one pointer and accesses through the other --- in this
|
||||
case, there is a dependence, but it's mediated by the free and reallocation.
|
||||
|
||||
An exception to this is with the :ref:`noalias <noalias>` keyword;
|
||||
An exception to this is with the {ref}`noalias <noalias>` keyword;
|
||||
the "irrelevant" dependencies are ignored.
|
||||
|
||||
The ``MayAlias`` response is used whenever the two pointers might refer to the
|
||||
The `MayAlias` response is used whenever the two pointers might refer to the
|
||||
same object.
|
||||
|
||||
The ``PartialAlias`` response is used when the two memory objects are known to
|
||||
The `PartialAlias` response is used when the two memory objects are known to
|
||||
be overlapping in some way, regardless of whether they start at the same address
|
||||
or not.
|
||||
|
||||
The ``MustAlias`` response may only be returned if the two memory objects are
|
||||
guaranteed to always start at exactly the same location. A ``MustAlias``
|
||||
The `MustAlias` response may only be returned if the two memory objects are
|
||||
guaranteed to always start at exactly the same location. A `MustAlias`
|
||||
response does not imply that the pointers compare equal.
|
||||
|
||||
The ``getModRefInfo`` methods
|
||||
-----------------------------
|
||||
### The `getModRefInfo` methods
|
||||
|
||||
The ``getModRefInfo`` methods return information about whether the execution of
|
||||
The `getModRefInfo` methods return information about whether the execution of
|
||||
an instruction can read or modify a memory location. Mod/Ref information is
|
||||
always conservative: if an instruction **might** read or write a location,
|
||||
``ModRef`` is returned.
|
||||
`ModRef` is returned.
|
||||
|
||||
The ``AliasAnalysis`` class also provides a ``getModRefInfo`` method for testing
|
||||
dependencies between function calls. This method takes two call sites (``CS1``
|
||||
& ``CS2``), returns ``NoModRef`` if neither call writes to memory read or
|
||||
written by the other, ``Ref`` if ``CS1`` reads memory written by ``CS2``,
|
||||
``Mod`` if ``CS1`` writes to memory read or written by ``CS2``, or ``ModRef`` if
|
||||
``CS1`` might read or write memory written to by ``CS2``. Note that this
|
||||
The `AliasAnalysis` class also provides a `getModRefInfo` method for testing
|
||||
dependencies between function calls. This method takes two call sites (`CS1`
|
||||
& `CS2`), returns `NoModRef` if neither call writes to memory read or
|
||||
written by the other, `Ref` if `CS1` reads memory written by `CS2`,
|
||||
`Mod` if `CS1` writes to memory read or written by `CS2`, or `ModRef` if
|
||||
`CS1` might read or write memory written to by `CS2`. Note that this
|
||||
relation is not commutative.
|
||||
|
||||
Other useful ``AliasAnalysis`` methods
|
||||
--------------------------------------
|
||||
### Other useful `AliasAnalysis` methods
|
||||
|
||||
Several other tidbits of information are often collected by various alias
|
||||
analysis implementations and can be put to good use by various clients.
|
||||
|
||||
The ``getModRefInfoMask`` method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `getModRefInfoMask` method
|
||||
|
||||
The ``getModRefInfoMask`` method returns a bound on Mod/Ref information for
|
||||
The `getModRefInfoMask` method returns a bound on Mod/Ref information for
|
||||
the supplied pointer, based on knowledge about whether the pointer points to
|
||||
globally-constant memory (for which it returns ``NoModRef``) or
|
||||
locally-invariant memory (for which it returns ``Ref``). Globally-constant
|
||||
globally-constant memory (for which it returns `NoModRef`) or
|
||||
locally-invariant memory (for which it returns `Ref`). Globally-constant
|
||||
memory includes functions, constant global variables, and the null pointer.
|
||||
Locally-invariant memory is memory that we know is invariant for the lifetime
|
||||
of its SSA value, but not necessarily for the life of the program: for example,
|
||||
the memory pointed to by ``readonly`` ``noalias`` parameters is known-invariant
|
||||
the memory pointed to by `readonly` `noalias` parameters is known-invariant
|
||||
for the duration of the corresponding function call. Given Mod/Ref information
|
||||
``MRI`` for a memory location ``Loc``, ``MRI`` can be refined with a statement
|
||||
like ``MRI &= AA.getModRefInfoMask(Loc);``. Another useful idiom is
|
||||
``isModSet(AA.getModRefInfoMask(Loc))``; this checks to see if the given
|
||||
`MRI` for a memory location `Loc`, `MRI` can be refined with a statement
|
||||
like `MRI &= AA.getModRefInfoMask(Loc);`. Another useful idiom is
|
||||
`isModSet(AA.getModRefInfoMask(Loc))`; this checks to see if the given
|
||||
location can be modified at all. For convenience, there is also a method
|
||||
``pointsToConstantMemory(Loc)``; this is synonymous with
|
||||
``isNoModRef(AA.getModRefInfoMask(Loc))``.
|
||||
`pointsToConstantMemory(Loc)`; this is synonymous with
|
||||
`isNoModRef(AA.getModRefInfoMask(Loc))`.
|
||||
|
||||
.. _never access memory or only read memory:
|
||||
(never access memory or only read memory)=
|
||||
|
||||
The ``doesNotAccessMemory`` and ``onlyReadsMemory`` methods
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `doesNotAccessMemory` and `onlyReadsMemory` methods
|
||||
|
||||
These methods are used to provide very simple mod/ref information for function
|
||||
calls. The ``doesNotAccessMemory`` method returns true for a function if the
|
||||
calls. The `doesNotAccessMemory` method returns true for a function if the
|
||||
analysis can prove that the function never reads or writes to memory, or if the
|
||||
function only reads from constant memory. Functions with this property are
|
||||
side-effect free and only depend on their input arguments, allowing them to be
|
||||
eliminated if they form common subexpressions or be hoisted out of loops. Many
|
||||
common functions behave this way (e.g., ``sin`` and ``cos``) but many others do
|
||||
not (e.g., ``acos``, which modifies the ``errno`` variable).
|
||||
common functions behave this way (e.g., `sin` and `cos`) but many others do
|
||||
not (e.g., `acos`, which modifies the `errno` variable).
|
||||
|
||||
The ``onlyReadsMemory`` method returns true for a function if analysis can prove
|
||||
The `onlyReadsMemory` method returns true for a function if analysis can prove
|
||||
that (at most) the function only reads from non-volatile memory. Functions with
|
||||
this property are side-effect free, only depending on their input arguments and
|
||||
the state of memory when they are called. This property allows calls to these
|
||||
functions to be eliminated and moved around, as long as there is no store
|
||||
instruction that changes the contents of memory. Note that all functions that
|
||||
satisfy the ``doesNotAccessMemory`` method also satisfy ``onlyReadsMemory``.
|
||||
satisfy the `doesNotAccessMemory` method also satisfy `onlyReadsMemory`.
|
||||
|
||||
Writing a new ``AliasAnalysis`` Implementation
|
||||
==============================================
|
||||
## Writing a new `AliasAnalysis` Implementation
|
||||
|
||||
Writing a new alias analysis implementation for LLVM is quite straightforward.
|
||||
There are already several implementations that you can use for examples, and the
|
||||
following information should help fill in any details. For example, take a
|
||||
look at the `various alias analysis implementations`_ included with LLVM.
|
||||
look at the {ref}`various alias analysis implementations` included with LLVM.
|
||||
|
||||
Different Pass styles
|
||||
---------------------
|
||||
### Different Pass styles
|
||||
|
||||
The first step is to determine what type of :doc:`LLVM pass <WritingAnLLVMPass>`
|
||||
The first step is to determine what type of {doc}`LLVM pass <WritingAnLLVMPass>`
|
||||
you need to use for your Alias Analysis. As is the case with most other
|
||||
analyses and transformations, the answer should be fairly obvious from what type
|
||||
of problem you are trying to solve:
|
||||
|
||||
#. If you require interprocedural analysis, it should be a ``Pass``.
|
||||
#. If you are a function-local analysis, subclass ``FunctionPass``.
|
||||
#. If you don't need to look at the program at all, subclass ``ImmutablePass``.
|
||||
1. If you require interprocedural analysis, it should be a `Pass`.
|
||||
1. If you are a function-local analysis, subclass `FunctionPass`.
|
||||
1. If you don't need to look at the program at all, subclass `ImmutablePass`.
|
||||
|
||||
In addition to the pass that you subclass, you should also inherit from the
|
||||
``AliasAnalysis`` interface, of course, and use the ``RegisterAnalysisGroup``
|
||||
template to register as an implementation of ``AliasAnalysis``.
|
||||
`AliasAnalysis` interface, of course, and use the `RegisterAnalysisGroup`
|
||||
template to register as an implementation of `AliasAnalysis`.
|
||||
|
||||
Required initialization calls
|
||||
-----------------------------
|
||||
### Required initialization calls
|
||||
|
||||
Your subclass of ``AliasAnalysis`` is required to invoke two methods on the
|
||||
``AliasAnalysis`` base class: ``getAnalysisUsage`` and
|
||||
``InitializeAliasAnalysis``. In particular, your implementation of
|
||||
``getAnalysisUsage`` should explicitly call into the
|
||||
``AliasAnalysis::getAnalysisUsage`` method in addition to declaring
|
||||
Your subclass of `AliasAnalysis` is required to invoke two methods on the
|
||||
`AliasAnalysis` base class: `getAnalysisUsage` and
|
||||
`InitializeAliasAnalysis`. In particular, your implementation of
|
||||
`getAnalysisUsage` should explicitly call into the
|
||||
`AliasAnalysis::getAnalysisUsage` method in addition to declaring
|
||||
any pass dependencies your pass has. Thus you should have something like this:
|
||||
|
||||
.. code-block:: c++
|
||||
```c++
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AliasAnalysis::getAnalysisUsage(AU);
|
||||
// declare your dependencies here.
|
||||
}
|
||||
```
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AliasAnalysis::getAnalysisUsage(AU);
|
||||
// declare your dependencies here.
|
||||
}
|
||||
Additionally, you must invoke the `InitializeAliasAnalysis` method from your
|
||||
analysis run method (`run` for a `Pass`, `runOnFunction` for a
|
||||
`FunctionPass`, or `InitializePass` for an `ImmutablePass`). For example
|
||||
(as part of a `Pass`):
|
||||
|
||||
Additionally, you must invoke the ``InitializeAliasAnalysis`` method from your
|
||||
analysis run method (``run`` for a ``Pass``, ``runOnFunction`` for a
|
||||
``FunctionPass``, or ``InitializePass`` for an ``ImmutablePass``). For example
|
||||
(as part of a ``Pass``):
|
||||
```c++
|
||||
bool run(Module &M) {
|
||||
InitializeAliasAnalysis(this);
|
||||
// Perform analysis here...
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
.. code-block:: c++
|
||||
### Interfaces which may be specified
|
||||
|
||||
bool run(Module &M) {
|
||||
InitializeAliasAnalysis(this);
|
||||
// Perform analysis here...
|
||||
return false;
|
||||
}
|
||||
|
||||
Interfaces which may be specified
|
||||
---------------------------------
|
||||
|
||||
All of the `AliasAnalysis
|
||||
<https://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html>`__ virtual methods
|
||||
default to providing :ref:`chaining <aliasanalysis-chaining>` to another alias
|
||||
All of the [AliasAnalysis](https://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html) virtual methods
|
||||
default to providing {ref}`chaining <aliasanalysis-chaining>` to another alias
|
||||
analysis implementation, which ends up returning conservatively correct
|
||||
information (returning "May" Alias and "Mod/Ref" for alias and mod/ref queries
|
||||
respectively). Depending on the capabilities of the analysis you are
|
||||
implementing, you just override the interfaces you can improve.
|
||||
|
||||
.. _aliasanalysis-chaining:
|
||||
(aliasanalysis-chaining)=
|
||||
|
||||
``AliasAnalysis`` chaining behavior
|
||||
-----------------------------------
|
||||
### `AliasAnalysis` chaining behavior
|
||||
|
||||
Every alias analysis pass chains to another alias analysis implementation (for
|
||||
example, the user can specify "``-basic-aa -ds-aa -licm``" to get the maximum
|
||||
example, the user can specify "`-basic-aa -ds-aa -licm`" to get the maximum
|
||||
benefit from both alias analyses). The alias analysis class automatically
|
||||
takes care of most of this for methods that you don't override. For methods
|
||||
that you do override, in code paths that return a conservative MayAlias or
|
||||
Mod/Ref result, simply return whatever the superclass computes. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
```c++
|
||||
AliasResult alias(const Value *V1, unsigned V1Size,
|
||||
const Value *V2, unsigned V2Size) {
|
||||
if (...)
|
||||
return NoAlias;
|
||||
...
|
||||
|
||||
AliasResult alias(const Value *V1, unsigned V1Size,
|
||||
const Value *V2, unsigned V2Size) {
|
||||
if (...)
|
||||
return NoAlias;
|
||||
...
|
||||
|
||||
// Couldn't determine a must or no-alias result.
|
||||
return AliasAnalysis::alias(V1, V1Size, V2, V2Size);
|
||||
}
|
||||
// Couldn't determine a must or no-alias result.
|
||||
return AliasAnalysis::alias(V1, V1Size, V2, V2Size);
|
||||
}
|
||||
```
|
||||
|
||||
In addition to analysis queries, you must make sure to unconditionally pass LLVM
|
||||
`update notification`_ methods to the superclass as well if you override them,
|
||||
{ref}`update notification` methods to the superclass as well if you override them,
|
||||
which allows all alias analyses in a change to be updated.
|
||||
|
||||
.. _update notification:
|
||||
(update notification)=
|
||||
|
||||
Updating analysis results for transformations
|
||||
---------------------------------------------
|
||||
### Updating analysis results for transformations
|
||||
|
||||
Alias analysis information is initially computed for a static snapshot of the
|
||||
program, but clients will use this information to make transformations to the
|
||||
code. All but the most trivial forms of alias analysis will need to have their
|
||||
analysis results updated to reflect the changes made by these transformations.
|
||||
|
||||
The ``AliasAnalysis`` interface exposes four methods which are used to
|
||||
The `AliasAnalysis` interface exposes four methods which are used to
|
||||
communicate program changes from the clients to the analysis implementations.
|
||||
Various alias analysis implementations should use these methods to ensure that
|
||||
their internal data structures are kept up-to-date as the program changes (for
|
||||
example, when an instruction is deleted), and clients of alias analysis must be
|
||||
sure to call these interfaces appropriately.
|
||||
|
||||
The ``deleteValue`` method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `deleteValue` method
|
||||
|
||||
The ``deleteValue`` method is called by transformations when they remove an
|
||||
The `deleteValue` method is called by transformations when they remove an
|
||||
instruction or any other value from the program (including values that do not
|
||||
use pointers). Typically alias analyses keep data structures that have entries
|
||||
for each value in the program. When this method is called, they should remove
|
||||
any entries for the specified value, if they exist.
|
||||
|
||||
The ``copyValue`` method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `copyValue` method
|
||||
|
||||
The ``copyValue`` method is used when a new value is introduced into the
|
||||
The `copyValue` method is used when a new value is introduced into the
|
||||
program. There is no way to introduce a value into the program that did not
|
||||
exist before (this doesn't make sense for a safe compiler transformation), so
|
||||
this is the only way to introduce a new value. This method indicates that the
|
||||
new value has exactly the same properties as the value being copied.
|
||||
|
||||
The ``replaceWithNewValue`` method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `replaceWithNewValue` method
|
||||
|
||||
This method is a simple helper method that is provided to make clients easier to
|
||||
use. It is implemented by copying the old analysis information to the new
|
||||
value, then deleting the old value. This method cannot be overridden by alias
|
||||
analysis implementations.
|
||||
|
||||
The ``addEscapingUse`` method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `addEscapingUse` method
|
||||
|
||||
The ``addEscapingUse`` method is used when the uses of a pointer value have
|
||||
The `addEscapingUse` method is used when the uses of a pointer value have
|
||||
changed in ways that may invalidate precomputed analysis information.
|
||||
Implementations may either use this callback to provide conservative responses
|
||||
for points whose uses have changed since analysis time, or may recompute some or
|
||||
|
|
@ -350,12 +328,11 @@ all of their internal state to continue providing accurate responses.
|
|||
In general, any new use of a pointer value is considered an escaping use, and
|
||||
must be reported through this callback, *except* for the uses below:
|
||||
|
||||
* A ``bitcast`` or ``getelementptr`` of the pointer
|
||||
* A ``store`` through the pointer (but not a ``store`` *of* the pointer)
|
||||
* A ``load`` through the pointer
|
||||
* A `bitcast` or `getelementptr` of the pointer
|
||||
* A `store` through the pointer (but not a `store` *of* the pointer)
|
||||
* A `load` through the pointer
|
||||
|
||||
Efficiency Issues
|
||||
-----------------
|
||||
### Efficiency Issues
|
||||
|
||||
From the LLVM perspective, the only thing you need to do to provide an efficient
|
||||
alias analysis is to make sure that alias analysis **queries** are serviced
|
||||
|
|
@ -364,108 +341,103 @@ method) is only performed once, but many (perhaps duplicate) queries may be
|
|||
performed. Because of this, try to move as much computation to the run method
|
||||
as possible (within reason).
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
### Limitations
|
||||
|
||||
The AliasAnalysis infrastructure has several limitations which make writing a
|
||||
new ``AliasAnalysis`` implementation difficult.
|
||||
new `AliasAnalysis` implementation difficult.
|
||||
|
||||
There is no way to override the default alias analysis. It would be very useful
|
||||
to be able to do something like "``opt -my-aa -O2``" and have it use ``-my-aa``
|
||||
to be able to do something like "`opt -my-aa -O2`" and have it use `-my-aa`
|
||||
for all passes which need AliasAnalysis, but there is currently no support for
|
||||
that, short of changing the source code and recompiling. Similarly, there is
|
||||
also no way of setting a chain of analyses as the default.
|
||||
|
||||
There is no way for transform passes to declare that they preserve
|
||||
``AliasAnalysis`` implementations. The ``AliasAnalysis`` interface includes
|
||||
``deleteValue`` and ``copyValue`` methods which are intended to allow a pass to
|
||||
`AliasAnalysis` implementations. The `AliasAnalysis` interface includes
|
||||
`deleteValue` and `copyValue` methods which are intended to allow a pass to
|
||||
keep an AliasAnalysis consistent; however, there's no way for a pass to declare
|
||||
in its ``getAnalysisUsage`` that it does so. Some passes attempt to use
|
||||
``AU.addPreserved<AliasAnalysis>``; however, this doesn't actually have any
|
||||
in its `getAnalysisUsage` that it does so. Some passes attempt to use
|
||||
`AU.addPreserved<AliasAnalysis>`; however, this doesn't actually have any
|
||||
effect.
|
||||
|
||||
Similarly, the ``opt -p`` option introduces ``ModulePass`` passes between each
|
||||
pass, which prevents the use of ``FunctionPass`` alias analysis passes.
|
||||
Similarly, the `opt -p` option introduces `ModulePass` passes between each
|
||||
pass, which prevents the use of `FunctionPass` alias analysis passes.
|
||||
|
||||
The ``AliasAnalysis`` API does have functions for notifying implementations when
|
||||
The `AliasAnalysis` API does have functions for notifying implementations when
|
||||
values are deleted or copied; however, these aren't sufficient. There are many
|
||||
other ways that LLVM IR can be modified which could be relevant to
|
||||
``AliasAnalysis`` implementations which can not be expressed.
|
||||
`AliasAnalysis` implementations which can not be expressed.
|
||||
|
||||
The ``AliasAnalysisDebugger`` utility seems to suggest that ``AliasAnalysis``
|
||||
implementations can expect that they will be informed of any relevant ``Value``
|
||||
before it appears in an alias query. However, popular clients such as ``GVN``
|
||||
The `AliasAnalysisDebugger` utility seems to suggest that `AliasAnalysis`
|
||||
implementations can expect that they will be informed of any relevant `Value`
|
||||
before it appears in an alias query. However, popular clients such as `GVN`
|
||||
don't support this, and are known to trigger errors when run with the
|
||||
``AliasAnalysisDebugger``.
|
||||
`AliasAnalysisDebugger`.
|
||||
|
||||
The ``AliasSetTracker`` class (which is used by ``LICM``) makes a
|
||||
The `AliasSetTracker` class (which is used by `LICM`) makes a
|
||||
non-deterministic number of alias queries. This can cause debugging techniques
|
||||
involving pausing execution after a predetermined number of queries to be
|
||||
unreliable.
|
||||
|
||||
Many alias queries can be reformulated in terms of other alias queries. When
|
||||
multiple ``AliasAnalysis`` queries are chained together, it would make sense to
|
||||
multiple `AliasAnalysis` queries are chained together, it would make sense to
|
||||
start those queries from the beginning of the chain, with care taken to avoid
|
||||
infinite looping; however, currently an implementation which wants to do this can
|
||||
only start such queries from itself.
|
||||
|
||||
Using alias analysis results
|
||||
============================
|
||||
## Using alias analysis results
|
||||
|
||||
There are several different ways to use alias analysis results. In order of
|
||||
preference, these are:
|
||||
|
||||
Using the ``MemoryDependenceAnalysis`` Pass
|
||||
-------------------------------------------
|
||||
### Using the `MemoryDependenceAnalysis` Pass
|
||||
|
||||
The ``memdep`` pass uses alias analysis to provide high-level dependence
|
||||
The `memdep` pass uses alias analysis to provide high-level dependence
|
||||
information about memory-using instructions. This will tell you which store
|
||||
feeds into a load, for example. It uses caching and other techniques to be
|
||||
efficient, and is used by Dead Store Elimination, GVN, and memcpy optimizations.
|
||||
|
||||
.. _AliasSetTracker:
|
||||
(AliasSetTracker)=
|
||||
|
||||
Using the ``AliasSetTracker`` class
|
||||
-----------------------------------
|
||||
### Using the `AliasSetTracker` class
|
||||
|
||||
Many transformations need information about alias **sets** that are active in
|
||||
some scope, rather than information about pairwise aliasing. The
|
||||
`AliasSetTracker <https://llvm.org/doxygen/classllvm_1_1AliasSetTracker.html>`__
|
||||
[AliasSetTracker](https://llvm.org/doxygen/classllvm_1_1AliasSetTracker.html)
|
||||
class is used to efficiently build these Alias Sets from the pairwise alias
|
||||
analysis information provided by the ``AliasAnalysis`` interface.
|
||||
analysis information provided by the `AliasAnalysis` interface.
|
||||
|
||||
First you initialize the AliasSetTracker by using the "``add``" methods to add
|
||||
First you initialize the AliasSetTracker by using the "`add`" methods to add
|
||||
information about various potentially aliasing instructions in the scope you are
|
||||
interested in. Once all of the alias sets are completed, your pass should
|
||||
simply iterate through the constructed alias sets, using the ``AliasSetTracker``
|
||||
``begin()``/``end()`` methods.
|
||||
simply iterate through the constructed alias sets, using the `AliasSetTracker`
|
||||
`begin()`/`end()` methods.
|
||||
|
||||
The ``AliasSet``\s formed by the ``AliasSetTracker`` are guaranteed to be
|
||||
The `AliasSet`s formed by the `AliasSetTracker` are guaranteed to be
|
||||
disjoint, calculate mod/ref information and volatility for the set, and keep
|
||||
track of whether or not all of the pointers in the set are Must aliases. The
|
||||
AliasSetTracker also makes sure that sets are properly folded due to call
|
||||
instructions, and can provide a list of pointers in each set.
|
||||
|
||||
As an example user of this, the `Loop Invariant Code Motion
|
||||
<doxygen/structLICM.html>`_ pass uses ``AliasSetTracker``\s to calculate alias
|
||||
sets for each loop nest. If an ``AliasSet`` in a loop is not modified, then all
|
||||
As an example user of this, the [Loop Invariant Code Motion](https://llvm.org/doxygen/structLICM.html)
|
||||
pass uses `AliasSetTracker`s to calculate alias
|
||||
sets for each loop nest. If an `AliasSet` in a loop is not modified, then all
|
||||
load instructions from that set may be hoisted out of the loop. If any alias
|
||||
sets are stored to **and** are must alias sets, then the stores may be sunk
|
||||
to outside of the loop, promoting the memory location to a register for the
|
||||
duration of the loop nest. Both of these transformations only apply if the
|
||||
pointer argument is loop-invariant.
|
||||
|
||||
The AliasSetTracker implementation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The AliasSetTracker implementation
|
||||
|
||||
The AliasSetTracker class is implemented to be as efficient as possible. It
|
||||
uses the union-find algorithm to efficiently merge AliasSets when a pointer is
|
||||
inserted into the AliasSetTracker that aliases multiple sets. The primary data
|
||||
structure is a hash table mapping pointers to the AliasSet they are in.
|
||||
|
||||
The AliasSetTracker class must maintain a list of all of the LLVM ``Value*``\s
|
||||
The AliasSetTracker class must maintain a list of all of the LLVM `Value*`s
|
||||
that are in each AliasSet. Since the hash table already has entries for each
|
||||
LLVM ``Value*`` of interest, the AliasesSets thread the linked list through
|
||||
LLVM `Value*` of interest, the AliasesSets thread the linked list through
|
||||
these hash-table nodes to avoid having to allocate memory unnecessarily, and to
|
||||
make merging alias sets extremely efficient (the linked list merge is constant
|
||||
time).
|
||||
|
|
@ -474,51 +446,46 @@ You shouldn't need to understand these details if you are just a client of the
|
|||
AliasSetTracker, but if you look at the code, hopefully this brief description
|
||||
will help make sense of why things are designed the way they are.
|
||||
|
||||
Using the ``AliasAnalysis`` interface directly
|
||||
----------------------------------------------
|
||||
### Using the `AliasAnalysis` interface directly
|
||||
|
||||
If neither of these utility classes are what your pass needs, you should use the
|
||||
interfaces exposed by the ``AliasAnalysis`` class directly. Try to use the
|
||||
interfaces exposed by the `AliasAnalysis` class directly. Try to use the
|
||||
higher-level methods when possible (e.g., use mod/ref information instead of the
|
||||
`alias`_ method directly if possible) to get the best precision and efficiency.
|
||||
{ref}`alias` method directly if possible) to get the best precision and efficiency.
|
||||
|
||||
Existing alias analysis implementations and clients
|
||||
===================================================
|
||||
## Existing alias analysis implementations and clients
|
||||
|
||||
If you're going to be working with the LLVM alias analysis infrastructure, you
|
||||
should know what clients and implementations of alias analysis are available.
|
||||
In particular, if you are implementing an alias analysis, you should be aware of
|
||||
`the clients`_ that are useful for monitoring and evaluating different
|
||||
{ref}`the clients` that are useful for monitoring and evaluating different
|
||||
implementations.
|
||||
|
||||
.. _various alias analysis implementations:
|
||||
(various alias analysis implementations)=
|
||||
|
||||
Available ``AliasAnalysis`` implementations
|
||||
-------------------------------------------
|
||||
### Available `AliasAnalysis` implementations
|
||||
|
||||
This section lists the various implementations of the ``AliasAnalysis``
|
||||
interface. All of these :ref:`chain <aliasanalysis-chaining>` to other
|
||||
This section lists the various implementations of the `AliasAnalysis`
|
||||
interface. All of these {ref}`chain <aliasanalysis-chaining>` to other
|
||||
alias analysis implementations.
|
||||
|
||||
The ``-basic-aa`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `-basic-aa` pass
|
||||
|
||||
The ``-basic-aa`` pass is an aggressive local analysis that *knows* many
|
||||
The `-basic-aa` pass is an aggressive local analysis that *knows* many
|
||||
important facts:
|
||||
|
||||
* Distinct globals, stack allocations, and heap allocations can never alias.
|
||||
* Globals, stack allocations, and heap allocations never alias the null pointer.
|
||||
* Different fields of a structure do not alias.
|
||||
* Indexes into arrays with statically differing subscripts cannot alias.
|
||||
* Many common standard C library functions `never access memory or only read
|
||||
memory`_.
|
||||
* Pointers that obviously point to constant globals "``pointToConstantMemory``".
|
||||
* Many common standard C library functions {ref}`never access memory or only read
|
||||
memory <never access memory or only read memory>`.
|
||||
* Pointers that obviously point to constant globals "`pointToConstantMemory`".
|
||||
* Function calls cannot modify or reference stack allocations if they never
|
||||
escape from the function that allocates them (a common case for automatic
|
||||
arrays).
|
||||
|
||||
The ``-globalsmodref-aa`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `-globalsmodref-aa` pass
|
||||
|
||||
This pass implements a simple context-sensitive mod/ref and alias analysis for
|
||||
internal global variables that don't "have their address taken". If a global
|
||||
|
|
@ -532,73 +499,67 @@ information for call instructions. This allows the optimizer to know that calls
|
|||
to a function do not clobber or read the value of the global, allowing loads and
|
||||
stores to be eliminated.
|
||||
|
||||
.. note::
|
||||
```{note}
|
||||
This pass is somewhat limited in its scope (only support non-address taken
|
||||
globals), but is very quick analysis.
|
||||
```
|
||||
|
||||
This pass is somewhat limited in its scope (only support non-address taken
|
||||
globals), but is very quick analysis.
|
||||
#### The `-steens-aa` pass
|
||||
|
||||
The ``-steens-aa`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``-steens-aa`` pass implements a variation on the well-known "Steensgaard's
|
||||
The `-steens-aa` pass implements a variation on the well-known "Steensgaard's
|
||||
algorithm" for interprocedural alias analysis. Steensgaard's algorithm is a
|
||||
unification-based, flow-insensitive, context-insensitive, and field-insensitive
|
||||
alias analysis that is also very scalable (effectively linear time).
|
||||
|
||||
The LLVM ``-steens-aa`` pass implements a "speculatively field-**sensitive**"
|
||||
The LLVM `-steens-aa` pass implements a "speculatively field-**sensitive**"
|
||||
version of Steensgaard's algorithm using the Data Structure Analysis framework.
|
||||
This gives it substantially more precision than the standard algorithm while
|
||||
maintaining excellent analysis scalability.
|
||||
|
||||
.. note::
|
||||
```{note}
|
||||
`-steens-aa` is available in the optional "poolalloc" module. It is not part
|
||||
of the LLVM core.
|
||||
```
|
||||
|
||||
``-steens-aa`` is available in the optional "poolalloc" module. It is not part
|
||||
of the LLVM core.
|
||||
#### The `-ds-aa` pass
|
||||
|
||||
The ``-ds-aa`` pass
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``-ds-aa`` pass implements the full Data Structure Analysis algorithm. Data
|
||||
The `-ds-aa` pass implements the full Data Structure Analysis algorithm. Data
|
||||
Structure Analysis is a modular unification-based, flow-insensitive,
|
||||
context-**sensitive**, and speculatively field-**sensitive** alias
|
||||
analysis that is also quite scalable, usually at ``O(n * log(n))``.
|
||||
analysis that is also quite scalable, usually at `O(n * log(n))`.
|
||||
|
||||
This algorithm is capable of responding to a full variety of alias analysis
|
||||
queries, and can provide context-sensitive mod/ref information as well. The
|
||||
only major facility not implemented so far is support for must-alias
|
||||
information.
|
||||
|
||||
.. note::
|
||||
```{note}
|
||||
`-ds-aa` is available in the optional "poolalloc" module. It is not part of
|
||||
the LLVM core.
|
||||
```
|
||||
|
||||
``-ds-aa`` is available in the optional "poolalloc" module. It is not part of
|
||||
the LLVM core.
|
||||
#### The `-scev-aa` pass
|
||||
|
||||
The ``-scev-aa`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``-scev-aa`` pass implements AliasAnalysis queries by translating them into
|
||||
The `-scev-aa` pass implements AliasAnalysis queries by translating them into
|
||||
ScalarEvolution queries. This gives it a more complete understanding of
|
||||
``getelementptr`` instructions and loop induction variables than other alias
|
||||
`getelementptr` instructions and loop induction variables than other alias
|
||||
analyses have.
|
||||
|
||||
Alias analysis driven transformations
|
||||
-------------------------------------
|
||||
### Alias analysis driven transformations
|
||||
|
||||
LLVM includes several alias-analysis driven transformations which can be used
|
||||
with any of the implementations above.
|
||||
|
||||
The ``-adce`` pass
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
#### The `-adce` pass
|
||||
|
||||
The ``-adce`` pass, which implements Aggressive Dead Code Elimination, uses the
|
||||
``AliasAnalysis`` interface to delete calls to functions that do not have
|
||||
The `-adce` pass, which implements Aggressive Dead Code Elimination, uses the
|
||||
`AliasAnalysis` interface to delete calls to functions that do not have
|
||||
side-effects and are not used.
|
||||
|
||||
The ``-licm`` pass
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
#### The `-licm` pass
|
||||
|
||||
The ``-licm`` pass implements various Loop Invariant Code Motion related
|
||||
transformations. It uses the ``AliasAnalysis`` interface for several different
|
||||
The `-licm` pass implements various Loop Invariant Code Motion related
|
||||
transformations. It uses the `AliasAnalysis` interface for several different
|
||||
transformations:
|
||||
|
||||
* It uses mod/ref information to hoist or sink load instructions out of loops if
|
||||
|
|
@ -611,61 +572,55 @@ transformations:
|
|||
to in loops to live in a register instead. It can do this if there are no may
|
||||
aliases to the loaded/stored memory location.
|
||||
|
||||
The ``-argpromotion`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `-argpromotion` pass
|
||||
|
||||
The ``-argpromotion`` pass promotes by-reference arguments to be passed in
|
||||
The `-argpromotion` pass promotes by-reference arguments to be passed in
|
||||
by-value instead. In particular, if pointer arguments are only loaded from, it
|
||||
passes in the value loaded instead of the address to the function. This pass
|
||||
uses alias information to make sure that the value loaded from the argument
|
||||
pointer is not modified between the entry of the function and any load of the
|
||||
pointer.
|
||||
|
||||
The ``-gvn``, ``-memcpyopt``, and ``-dse`` passes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### The `-gvn`, `-memcpyopt`, and `-dse` passes
|
||||
|
||||
These passes use AliasAnalysis information to reason about loads and stores.
|
||||
|
||||
.. _the clients:
|
||||
(the clients)=
|
||||
|
||||
Clients for debugging and evaluation of implementations
|
||||
-------------------------------------------------------
|
||||
### Clients for debugging and evaluation of implementations
|
||||
|
||||
These passes are useful for evaluating the various alias analysis
|
||||
implementations. You can use them with commands like:
|
||||
|
||||
.. code-block:: bash
|
||||
```console
|
||||
% opt -ds-aa -aa-eval foo.bc -disable-output -stats
|
||||
```
|
||||
|
||||
% opt -ds-aa -aa-eval foo.bc -disable-output -stats
|
||||
#### The `print<alias-sets>` pass
|
||||
|
||||
The ``print<alias-sets>`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The `print<alias-sets>` pass is exposed as part of the `opt` tool to print
|
||||
out the Alias Sets formed by the {ref}`AliasSetTracker` class. This is useful if
|
||||
you're using the `AliasSetTracker` class. To use it, use something like:
|
||||
|
||||
The ``print<alias-sets>`` pass is exposed as part of the ``opt`` tool to print
|
||||
out the Alias Sets formed by the `AliasSetTracker`_ class. This is useful if
|
||||
you're using the ``AliasSetTracker`` class. To use it, use something like:
|
||||
```console
|
||||
% opt -passes='print<alias-sets>' -disable-output
|
||||
```
|
||||
|
||||
.. code-block:: bash
|
||||
#### The `-aa-eval` pass
|
||||
|
||||
% opt -passes='print<alias-sets>' -disable-output
|
||||
|
||||
The ``-aa-eval`` pass
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``-aa-eval`` pass simply iterates through all pairs of pointers in a
|
||||
The `-aa-eval` pass simply iterates through all pairs of pointers in a
|
||||
function and asks an alias analysis whether or not the pointers alias. This
|
||||
gives an indication of the precision of the alias analysis. Statistics are
|
||||
printed indicating the percent of no/may/must aliases found (a more precise
|
||||
algorithm will have a lower number of may aliases).
|
||||
|
||||
Memory Dependence Analysis
|
||||
==========================
|
||||
## Memory Dependence Analysis
|
||||
|
||||
.. note::
|
||||
|
||||
We are currently in the process of migrating things from
|
||||
``MemoryDependenceAnalysis`` to :doc:`MemorySSA`. Please try to use
|
||||
that instead.
|
||||
```{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,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
==============================================
|
||||
LLVM Atomic Instructions and Concurrency Guide
|
||||
==============================================
|
||||
---
|
||||
myst:
|
||||
enable_extensions:
|
||||
- deflist
|
||||
---
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
# LLVM Atomic Instructions and Concurrency Guide
|
||||
|
||||
Introduction
|
||||
============
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
LLVM supports instructions which are well-defined in the presence of threads and
|
||||
asynchronous signals.
|
||||
|
|
@ -14,27 +18,29 @@ asynchronous signals.
|
|||
The atomic instructions are designed specifically to provide readable IR and
|
||||
optimized code generation for the following:
|
||||
|
||||
* The C++ ``<atomic>`` header and C ``<stdatomic.h>`` headers. These
|
||||
* The C++ `<atomic>` header and C `<stdatomic.h>` headers. These
|
||||
were originally added in C++11 and C11. The memory model has been
|
||||
subsequently adjusted to correct errors in the initial
|
||||
specification, so LLVM currently intends to implement the version
|
||||
specified by C++20. (See the `C++20 draft standard
|
||||
<https://isocpp.org/files/papers/N4860.pdf>`_ or the unofficial
|
||||
`latest C++ draft <https://eel.is/c++draft/>`_. A `C2x draft
|
||||
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf>`_ is
|
||||
specified by C++20. (See the [C++20 draft standard]
|
||||
or the unofficial [latest C++ draft]. A [C2x draft] is
|
||||
also available, though the text has not yet been updated with the
|
||||
errata corrected by C++20.)
|
||||
|
||||
* Proper semantics for Java-style memory, for both ``volatile`` and regular
|
||||
shared variables. (`Java Specification
|
||||
<http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html>`_)
|
||||
* Proper semantics for Java-style memory, for both `volatile` and regular
|
||||
shared variables. ([Java Specification])
|
||||
|
||||
* gcc-compatible ``__sync_*`` builtins. (`Description
|
||||
<https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html>`_)
|
||||
* gcc-compatible `__sync_*` builtins. ([Description])
|
||||
|
||||
* Other scenarios with atomic semantics, including ``static`` variables with
|
||||
* Other scenarios with atomic semantics, including `static` variables with
|
||||
non-trivial constructors in C++.
|
||||
|
||||
[C++20 draft standard]: https://isocpp.org/files/papers/N4860.pdf
|
||||
[latest C++ draft]: https://eel.is/c++draft/
|
||||
[C2x draft]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf
|
||||
[Java Specification]: http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
|
||||
[Description]: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
|
||||
Atomic and volatile in the IR are orthogonal; "volatile" is the C/C++ volatile,
|
||||
which ensures that every volatile load and store happens and is performed in the
|
||||
stated order. A couple examples: if a SequentiallyConsistent store is
|
||||
|
|
@ -50,13 +56,12 @@ with instructions with special semantics in the presence of concurrency. This
|
|||
is not intended to be a precise guide to the semantics; the details can get
|
||||
extremely complicated and unreadable, and are not usually necessary.
|
||||
|
||||
.. _Optimization outside atomic:
|
||||
(Optimization outside atomic)=
|
||||
|
||||
Optimization outside atomic
|
||||
===========================
|
||||
## Optimization outside atomic
|
||||
|
||||
The basic ``'load'`` and ``'store'`` allow a variety of optimizations, but can
|
||||
lead to undefined results in a concurrent environment; see `NotAtomic`_. This
|
||||
The basic `'load'` and `'store'` allow a variety of optimizations, but can
|
||||
lead to undefined results in a concurrent environment; see {ref}`NotAtomic`. This
|
||||
section specifically goes into the one optimizer restriction which applies in
|
||||
concurrent environments, which gets a bit more of an extended description
|
||||
because any optimization dealing with stores needs to be aware of it.
|
||||
|
|
@ -67,8 +72,7 @@ one exception: if a variable might be visible to another thread or signal
|
|||
handler, a store cannot be inserted along a path where it might not execute
|
||||
otherwise. Take the following example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
```c
|
||||
/* C code, for readability; run through clang -O2 -S -emit-llvm to get
|
||||
equivalent IR */
|
||||
int x;
|
||||
|
|
@ -78,11 +82,11 @@ otherwise. Take the following example:
|
|||
x += 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following is equivalent in non-concurrent situations:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
```c
|
||||
int x;
|
||||
void f(int* a) {
|
||||
int xtemp = x;
|
||||
|
|
@ -92,33 +96,33 @@ The following is equivalent in non-concurrent situations:
|
|||
}
|
||||
x = xtemp;
|
||||
}
|
||||
```
|
||||
|
||||
However, LLVM is not allowed to transform the former to the latter: it could
|
||||
indirectly introduce undefined behavior if another thread can access ``x`` at
|
||||
the same time. That thread would read ``undef`` instead of the value it was
|
||||
indirectly introduce undefined behavior if another thread can access `x` at
|
||||
the same time. That thread would read `undef` instead of the value it was
|
||||
expecting, which can lead to undefined behavior down the line. (This example is
|
||||
particularly of interest because before the concurrency model was implemented,
|
||||
LLVM would perform this transformation.)
|
||||
|
||||
Note that speculative loads are allowed; a load which is part of a race returns
|
||||
``undef``, but does not have undefined behavior.
|
||||
`undef`, but does not have undefined behavior.
|
||||
|
||||
Atomic instructions
|
||||
===================
|
||||
## Atomic instructions
|
||||
|
||||
For cases where simple loads and stores are not sufficient, LLVM provides
|
||||
various atomic instructions. The exact guarantees provided depend on the
|
||||
ordering; see `Atomic orderings`_.
|
||||
ordering; see {ref}`Atomic orderings`.
|
||||
|
||||
``load atomic`` and ``store atomic`` provide the same basic functionality as
|
||||
`load atomic` and `store atomic` provide the same basic functionality as
|
||||
non-atomic loads and stores, but provide additional guarantees in situations
|
||||
where threads and signals are involved.
|
||||
|
||||
``cmpxchg`` and ``atomicrmw`` are essentially like an atomic load followed by an
|
||||
atomic store (where the store is conditional for ``cmpxchg``), but no other
|
||||
`cmpxchg` and `atomicrmw` are essentially like an atomic load followed by an
|
||||
atomic store (where the store is conditional for `cmpxchg`), but no other
|
||||
memory operation can happen on any thread between the load and store.
|
||||
|
||||
A ``fence`` provides Acquire and/or Release ordering which is not part
|
||||
A `fence` provides Acquire and/or Release ordering which is not part
|
||||
of another operation; it is normally used along with Monotonic memory
|
||||
operations. A Monotonic load followed by an Acquire fence is roughly
|
||||
equivalent to an Acquire load, and a Monotonic store following a
|
||||
|
|
@ -132,33 +136,31 @@ target to some degree; atomic instructions are guaranteed to be lock-free, and
|
|||
therefore an instruction which is wider than the target natively supports can be
|
||||
impossible to generate.
|
||||
|
||||
.. _Atomic orderings:
|
||||
(Atomic orderings)=
|
||||
|
||||
Atomic orderings
|
||||
================
|
||||
## Atomic orderings
|
||||
|
||||
In order to achieve a balance between performance and necessary guarantees,
|
||||
there are six levels of atomicity. They are listed in order of strength; each
|
||||
level includes all the guarantees of the previous level except for
|
||||
Acquire/Release. (See also `LangRef Ordering <LangRef.html#ordering>`_.)
|
||||
Acquire/Release. (See also {ref}`LangRef Ordering <ordering>`.)
|
||||
|
||||
.. _NotAtomic:
|
||||
(NotAtomic)=
|
||||
|
||||
NotAtomic
|
||||
---------
|
||||
### NotAtomic
|
||||
|
||||
NotAtomic is the obvious, a load or store which is not atomic. (This isn't
|
||||
really a level of atomicity, but is listed here for comparison.) This is
|
||||
essentially a regular load or store. If there is a race on a given memory
|
||||
location, loads from that location return ``undef``.
|
||||
location, loads from that location return `undef`.
|
||||
|
||||
Relevant standard
|
||||
This is intended to match shared variables in C/C++, and to be used in any
|
||||
: This is intended to match shared variables in C/C++, and to be used in any
|
||||
other context where memory access is necessary, and a race is impossible. (The
|
||||
precise definition is in `LangRef Memory Model <LangRef.html#memmodel>`_.)
|
||||
precise definition is in {ref}`LangRef Memory Model <memmodel>`.)
|
||||
|
||||
Notes for frontends
|
||||
The rule is essentially that all memory accessed with basic loads and stores
|
||||
: The rule is essentially that all memory accessed with basic loads and stores
|
||||
by multiple threads should be protected by a lock or other synchronization;
|
||||
otherwise, you are likely to run into undefined behavior. If your frontend is
|
||||
for a "safe" language like Java, use Unordered to load and store any shared
|
||||
|
|
@ -168,12 +170,12 @@ Notes for frontends
|
|||
atomics are generally a better solution.)
|
||||
|
||||
Notes for optimizers
|
||||
Introducing loads to shared variables along a codepath where they would not
|
||||
: Introducing loads to shared variables along a codepath where they would not
|
||||
otherwise exist is allowed; introducing stores to shared variables is not. See
|
||||
`Optimization outside atomic`_.
|
||||
{ref}`Optimization outside atomic`.
|
||||
|
||||
Notes for code generation
|
||||
The one interesting restriction here is that it is not allowed to write to
|
||||
: The one interesting restriction here is that it is not allowed to write to
|
||||
bytes outside of the bytes relevant to a store. This is mostly relevant to
|
||||
unaligned stores: it is not allowed in general to convert an unaligned store
|
||||
into two aligned stores of the same width as the unaligned store. Backends are
|
||||
|
|
@ -182,8 +184,7 @@ Notes for code generation
|
|||
architecture which cannot satisfy these restrictions and cares about
|
||||
concurrency, please send an email to llvm-dev.)
|
||||
|
||||
Unordered
|
||||
---------
|
||||
### Unordered
|
||||
|
||||
Unordered is the lowest level of atomicity. It essentially guarantees that races
|
||||
produce somewhat sane results instead of having undefined behavior. It also
|
||||
|
|
@ -193,10 +194,10 @@ global lock. Note that code generation will fail for unsupported atomic
|
|||
operations; if you need such an operation, use explicit locking.
|
||||
|
||||
Relevant standard
|
||||
This is intended to match the Java memory model for shared variables.
|
||||
: This is intended to match the Java memory model for shared variables.
|
||||
|
||||
Notes for frontends
|
||||
This cannot be used for synchronization, but is useful for Java and other
|
||||
: This cannot be used for synchronization, but is useful for Java and other
|
||||
"safe" languages which need to guarantee that the generated code never
|
||||
exhibits undefined behavior. Note that this guarantee is cheap on common
|
||||
platforms for loads of a native width, but can be expensive or unavailable for
|
||||
|
|
@ -205,7 +206,7 @@ Notes for frontends
|
|||
stores.)
|
||||
|
||||
Notes for optimizers
|
||||
In terms of the optimizer, this prohibits any transformation that transforms a
|
||||
: In terms of the optimizer, this prohibits any transformation that transforms a
|
||||
single load into multiple loads, transforms a store into multiple stores,
|
||||
narrows a store, or stores a value which would not be stored otherwise. Some
|
||||
examples of unsafe optimizations are narrowing an assignment into a bitfield,
|
||||
|
|
@ -215,15 +216,14 @@ Notes for optimizers
|
|||
that need them.
|
||||
|
||||
Notes for code generation
|
||||
These operations are required to be atomic in the sense that if you use
|
||||
: These operations are required to be atomic in the sense that if you use
|
||||
unordered loads and unordered stores, a load cannot see a value which was
|
||||
never stored. A normal load or store instruction is usually sufficient, but
|
||||
note that an unordered load or store cannot be split into multiple
|
||||
instructions (or an instruction which does multiple memory operations, like
|
||||
``LDRD`` on ARM without LPAE, or not naturally-aligned ``LDRD`` on LPAE ARM).
|
||||
`LDRD` on ARM without LPAE, or not naturally-aligned `LDRD` on LPAE ARM).
|
||||
|
||||
Monotonic
|
||||
---------
|
||||
### Monotonic
|
||||
|
||||
Monotonic is the weakest level of atomicity that can be used in synchronization
|
||||
primitives, although it does not provide any general synchronization. It
|
||||
|
|
@ -231,18 +231,18 @@ essentially guarantees that if you take all the operations affecting a specific
|
|||
address, a consistent ordering exists.
|
||||
|
||||
Relevant standard
|
||||
This corresponds to the C++/C ``memory_order_relaxed``; see those
|
||||
: This corresponds to the C++/C `memory_order_relaxed`; see those
|
||||
standards for the exact definition.
|
||||
|
||||
Notes for frontends
|
||||
If you are writing a frontend which uses this directly, use with caution. The
|
||||
: If you are writing a frontend which uses this directly, use with caution. The
|
||||
guarantees in terms of synchronization are very weak, so make sure these are
|
||||
only used in a pattern which you know is correct. Generally, these would
|
||||
either be used for atomic operations which do not protect other memory (like
|
||||
an atomic counter), or along with a ``fence``.
|
||||
an atomic counter), or along with a `fence`.
|
||||
|
||||
Notes for optimizers
|
||||
In terms of the optimizer, this can be treated as a read+write on the relevant
|
||||
: In terms of the optimizer, this can be treated as a read+write on the relevant
|
||||
memory location (and alias analysis will take advantage of that). In addition,
|
||||
it is legal to reorder non-atomic and Unordered loads around Monotonic
|
||||
loads. CSE/DSE and a few other optimizations are allowed, but Monotonic
|
||||
|
|
@ -250,135 +250,130 @@ Notes for optimizers
|
|||
optimizations useful.
|
||||
|
||||
Notes for code generation
|
||||
Code generation is essentially the same as that for unordered for loads and
|
||||
stores. No fences are required. ``cmpxchg`` and ``atomicrmw`` are required
|
||||
: Code generation is essentially the same as that for unordered for loads and
|
||||
stores. No fences are required. `cmpxchg` and `atomicrmw` are required
|
||||
to appear as a single operation.
|
||||
|
||||
Acquire
|
||||
-------
|
||||
### Acquire
|
||||
|
||||
Acquire provides a barrier of the sort necessary to acquire a lock to access
|
||||
other memory with normal loads and stores.
|
||||
|
||||
Relevant standard
|
||||
This corresponds to the C++/C ``memory_order_acquire``. It should also be
|
||||
used for C++/C ``memory_order_consume``.
|
||||
: This corresponds to the C++/C `memory_order_acquire`. It should also be
|
||||
used for C++/C `memory_order_consume`.
|
||||
|
||||
Notes for frontends
|
||||
If you are writing a frontend which uses this directly, use with caution.
|
||||
: If you are writing a frontend which uses this directly, use with caution.
|
||||
Acquire only provides a semantic guarantee when paired with a Release
|
||||
operation.
|
||||
|
||||
Notes for optimizers
|
||||
Optimizers not aware of atomics can treat this like a nothrow call. It is
|
||||
: Optimizers not aware of atomics can treat this like a nothrow call. It is
|
||||
also possible to move stores from before an Acquire load or read-modify-write
|
||||
operation to after it, and move non-Acquire loads from before an Acquire
|
||||
operation to after it.
|
||||
|
||||
Notes for code generation
|
||||
Architectures with weak memory ordering (essentially everything relevant today
|
||||
: Architectures with weak memory ordering (essentially everything relevant today
|
||||
except x86 and SPARC) require some sort of fence to maintain the Acquire
|
||||
semantics. The precise fences required varies widely by architecture, but for
|
||||
a simple implementation, most architectures provide a barrier which is strong
|
||||
enough for everything (``dmb`` on ARM, ``sync`` on PowerPC, etc.). Putting
|
||||
enough for everything (`dmb` on ARM, `sync` on PowerPC, etc.). Putting
|
||||
such a fence after the equivalent Monotonic operation is sufficient to
|
||||
maintain Acquire semantics for a memory operation.
|
||||
|
||||
Release
|
||||
-------
|
||||
### Release
|
||||
|
||||
Release is similar to Acquire, but with a barrier of the sort necessary to
|
||||
release a lock.
|
||||
|
||||
Relevant standard
|
||||
This corresponds to the C++/C ``memory_order_release``.
|
||||
: This corresponds to the C++/C `memory_order_release`.
|
||||
|
||||
Notes for frontends
|
||||
If you are writing a frontend which uses this directly, use with caution.
|
||||
: If you are writing a frontend which uses this directly, use with caution.
|
||||
Release only provides a semantic guarantee when paired with an Acquire
|
||||
operation.
|
||||
|
||||
Notes for optimizers
|
||||
Optimizers not aware of atomics can treat this like a nothrow call. It is
|
||||
: Optimizers not aware of atomics can treat this like a nothrow call. It is
|
||||
also possible to move loads from after a Release store or read-modify-write
|
||||
operation to before it, and move non-Release stores from after a Release
|
||||
operation to before it.
|
||||
|
||||
Notes for code generation
|
||||
See the section on Acquire; a fence before the relevant operation is usually
|
||||
: See the section on Acquire; a fence before the relevant operation is usually
|
||||
sufficient for Release. Note that a store-store fence is not sufficient to
|
||||
implement Release semantics; store-store fences are generally not exposed to
|
||||
IR because they are extremely difficult to use correctly.
|
||||
|
||||
AcquireRelease
|
||||
--------------
|
||||
### AcquireRelease
|
||||
|
||||
AcquireRelease (``acq_rel`` in IR) provides both an Acquire and a Release
|
||||
AcquireRelease (`acq_rel` in IR) provides both an Acquire and a Release
|
||||
barrier (for fences and operations which both read and write memory).
|
||||
|
||||
Relevant standard
|
||||
This corresponds to the C++/C ``memory_order_acq_rel``.
|
||||
: This corresponds to the C++/C `memory_order_acq_rel`.
|
||||
|
||||
Notes for frontends
|
||||
If you are writing a frontend which uses this directly, use with caution.
|
||||
: If you are writing a frontend which uses this directly, use with caution.
|
||||
Acquire only provides a semantic guarantee when paired with a Release
|
||||
operation, and vice versa.
|
||||
|
||||
Notes for optimizers
|
||||
In general, optimizers should treat this like a nothrow call; the possible
|
||||
: In general, optimizers should treat this like a nothrow call; the possible
|
||||
optimizations are usually not interesting.
|
||||
|
||||
Notes for code generation
|
||||
This operation has Acquire and Release semantics; see the sections on Acquire
|
||||
: This operation has Acquire and Release semantics; see the sections on Acquire
|
||||
and Release.
|
||||
|
||||
SequentiallyConsistent
|
||||
----------------------
|
||||
### SequentiallyConsistent
|
||||
|
||||
SequentiallyConsistent (``seq_cst`` in IR) provides Acquire semantics for loads
|
||||
SequentiallyConsistent (`seq_cst` in IR) provides Acquire semantics for loads
|
||||
and Release semantics for stores. Additionally, it guarantees that a total
|
||||
ordering exists between all SequentiallyConsistent operations.
|
||||
|
||||
Relevant standard
|
||||
This corresponds to the C++/C ``memory_order_seq_cst``, Java volatile, and
|
||||
the gcc-compatible ``__sync_*`` builtins which do not specify otherwise.
|
||||
: This corresponds to the C++/C `memory_order_seq_cst`, Java volatile, and
|
||||
the gcc-compatible `__sync_*` builtins which do not specify otherwise.
|
||||
|
||||
Notes for frontends
|
||||
If a frontend is exposing atomic operations, these are much easier to reason
|
||||
: If a frontend is exposing atomic operations, these are much easier to reason
|
||||
about for the programmer than other kinds of operations, and using them is
|
||||
generally a practical performance tradeoff.
|
||||
|
||||
Notes for optimizers
|
||||
Optimizers not aware of atomics can treat this like a nothrow call. For
|
||||
: Optimizers not aware of atomics can treat this like a nothrow call. For
|
||||
SequentiallyConsistent loads and stores, the same reorderings are allowed as
|
||||
for Acquire loads and Release stores, except that SequentiallyConsistent
|
||||
operations may not be reordered.
|
||||
|
||||
Notes for code generation
|
||||
SequentiallyConsistent loads minimally require the same barriers as Acquire
|
||||
: SequentiallyConsistent loads minimally require the same barriers as Acquire
|
||||
operations and SequentiallyConsistent stores require Release
|
||||
barriers. Additionally, the code generator must enforce ordering between
|
||||
SequentiallyConsistent stores followed by SequentiallyConsistent loads. This
|
||||
is usually done by emitting either a full fence before the loads or a full
|
||||
fence after the stores; which is preferred varies by architecture.
|
||||
|
||||
Atomics and IR optimization
|
||||
===========================
|
||||
## Atomics and IR optimization
|
||||
|
||||
Predicates for optimizer writers to query:
|
||||
|
||||
* ``isSimple()``: A load or store which is not volatile or atomic. This is
|
||||
* `isSimple()`: A load or store which is not volatile or atomic. This is
|
||||
what, for example, memcpyopt would check for operations it might transform.
|
||||
|
||||
* ``isUnordered()``: A load or store which is not volatile and at most
|
||||
* `isUnordered()`: A load or store which is not volatile and at most
|
||||
Unordered. This would be checked, for example, by LICM before hoisting an
|
||||
operation.
|
||||
|
||||
* ``mayReadFromMemory()``/``mayWriteToMemory()``: Existing predicate, but note
|
||||
* `mayReadFromMemory()`/`mayWriteToMemory()`: Existing predicate, but note
|
||||
that they return true for any operation which is volatile or at least
|
||||
Monotonic.
|
||||
|
||||
* ``isStrongerThan`` / ``isAtLeastOrStrongerThan``: These are predicates on
|
||||
* `isStrongerThan` / `isAtLeastOrStrongerThan`: These are predicates on
|
||||
orderings. They can be useful for passes that are aware of atomics, for
|
||||
example to do DSE across a single atomic access, but not across a
|
||||
release-acquire pair (see MemoryDependencyAnalysis for an example of this)
|
||||
|
|
@ -394,7 +389,7 @@ it doesn't replace an atomic load or store with a non-atomic operation.
|
|||
Some examples of how optimizations interact with various kinds of atomic
|
||||
operations:
|
||||
|
||||
* ``memcpyopt``: An atomic operation cannot be optimized into part of a
|
||||
* `memcpyopt`: An atomic operation cannot be optimized into part of a
|
||||
memcpy/memset, including unordered loads/stores. It can pull operations
|
||||
across some atomic operations.
|
||||
|
||||
|
|
@ -412,13 +407,12 @@ operations:
|
|||
because it cannot be observed. Similar reasoning allows SROA with
|
||||
atomic loads and stores.
|
||||
|
||||
Atomics and Codegen
|
||||
===================
|
||||
## Atomics and Codegen
|
||||
|
||||
Atomic operations are represented in the SelectionDAG with ``ATOMIC_*`` opcodes.
|
||||
Atomic operations are represented in the SelectionDAG with `ATOMIC_*` opcodes.
|
||||
On architectures which use barrier instructions for all atomic ordering (like
|
||||
ARM), appropriate fences can be emitted by the AtomicExpand Codegen pass if
|
||||
``shouldInsertFencesForAtomic()`` returns true.
|
||||
`shouldInsertFencesForAtomic()` returns true.
|
||||
|
||||
The MachineMemOperand for all atomic operations is currently marked as volatile;
|
||||
this is not correct in the IR sense of volatile, but CodeGen handles anything
|
||||
|
|
@ -428,53 +422,53 @@ One very important property of the atomic operations is that if your backend
|
|||
supports any inline lock-free atomic operations of a given size, you should
|
||||
support *ALL* operations of that size in a lock-free manner.
|
||||
|
||||
When the target implements atomic ``cmpxchg`` or LL/SC instructions (as most do)
|
||||
When the target implements atomic `cmpxchg` or LL/SC instructions (as most do)
|
||||
this is trivial: all the other operations can be implemented on top of those
|
||||
primitives. However, on many older CPUs (e.g. ARMv5, Sparc V8, Intel 80386) there
|
||||
are atomic load and store instructions, but no ``cmpxchg`` or LL/SC. As it is
|
||||
invalid to implement ``atomic load`` using the native instruction, but
|
||||
``cmpxchg`` using a library call to a function that uses a mutex, ``atomic
|
||||
load`` must *also* expand to a library call on such architectures, so that it
|
||||
can remain atomic with regards to a simultaneous ``cmpxchg``, by using the same
|
||||
are atomic load and store instructions, but no `cmpxchg` or LL/SC. As it is
|
||||
invalid to implement `atomic load` using the native instruction, but
|
||||
`cmpxchg` using a library call to a function that uses a mutex, `atomic
|
||||
load` must *also* expand to a library call on such architectures, so that it
|
||||
can remain atomic with regards to a simultaneous `cmpxchg`, by using the same
|
||||
mutex.
|
||||
|
||||
AtomicExpandPass can help with that: it will expand all atomic operations to the
|
||||
proper ``__atomic_*`` libcalls for any size above the maximum set by
|
||||
``setMaxAtomicSizeInBitsSupported`` (which defaults to 0).
|
||||
proper `__atomic_*` libcalls for any size above the maximum set by
|
||||
`setMaxAtomicSizeInBitsSupported` (which defaults to 0).
|
||||
|
||||
On x86, all atomic loads generate a ``MOV``. SequentiallyConsistent stores
|
||||
generate an ``XCHG``, other stores generate a ``MOV``. SequentiallyConsistent
|
||||
fences generate an ``MFENCE``, other fences do not cause any code to be
|
||||
generated. ``cmpxchg`` uses the ``LOCK CMPXCHG`` instruction. ``atomicrmw xchg``
|
||||
uses ``XCHG``, ``atomicrmw add`` and ``atomicrmw sub`` use ``XADD``, and all
|
||||
other ``atomicrmw`` operations generate a loop with ``LOCK CMPXCHG``. Depending
|
||||
on the users of the result, some ``atomicrmw`` operations can be translated into
|
||||
operations like ``LOCK AND``, but that does not work in general.
|
||||
On x86, all atomic loads generate a `MOV`. SequentiallyConsistent stores
|
||||
generate an `XCHG`, other stores generate a `MOV`. SequentiallyConsistent
|
||||
fences generate an `MFENCE`, other fences do not cause any code to be
|
||||
generated. `cmpxchg` uses the `LOCK CMPXCHG` instruction. `atomicrmw xchg`
|
||||
uses `XCHG`, `atomicrmw add` and `atomicrmw sub` use `XADD`, and all
|
||||
other `atomicrmw` operations generate a loop with `LOCK CMPXCHG`. Depending
|
||||
on the users of the result, some `atomicrmw` operations can be translated into
|
||||
operations like `LOCK AND`, but that does not work in general.
|
||||
|
||||
On ARM (before v8), MIPS, and many other RISC architectures, Acquire, Release,
|
||||
and SequentiallyConsistent semantics require barrier instructions for every such
|
||||
operation. Loads and stores generate normal instructions. ``cmpxchg`` and
|
||||
``atomicrmw`` can be represented using a loop with LL/SC-style instructions
|
||||
which take some sort of exclusive lock on a cache line (``LDREX`` and ``STREX``
|
||||
operation. Loads and stores generate normal instructions. `cmpxchg` and
|
||||
`atomicrmw` can be represented using a loop with LL/SC-style instructions
|
||||
which take some sort of exclusive lock on a cache line (`LDREX` and `STREX`
|
||||
on ARM, etc.).
|
||||
|
||||
It is often easiest for backends to use AtomicExpandPass to lower some of the
|
||||
atomic constructs. Here are some lowerings it can do:
|
||||
|
||||
* cmpxchg -> loop with load-linked/store-conditional
|
||||
by overriding ``shouldExpandAtomicCmpXchgInIR()``, ``emitLoadLinked()``,
|
||||
``emitStoreConditional()``
|
||||
by overriding `shouldExpandAtomicCmpXchgInIR()`, `emitLoadLinked()`,
|
||||
`emitStoreConditional()`
|
||||
* large loads/stores -> ll-sc/cmpxchg
|
||||
by overriding ``shouldExpandAtomicStoreInIR()``/``shouldExpandAtomicLoadInIR()``
|
||||
by overriding `shouldExpandAtomicStoreInIR()`/`shouldExpandAtomicLoadInIR()`
|
||||
* strong atomic accesses -> monotonic accesses + fences by overriding
|
||||
``shouldInsertFencesForAtomic()``, ``emitLeadingFence()``, and
|
||||
``emitTrailingFence()``
|
||||
`shouldInsertFencesForAtomic()`, `emitLeadingFence()`, and
|
||||
`emitTrailingFence()`
|
||||
* atomic rmw -> loop with cmpxchg or load-linked/store-conditional
|
||||
by overriding ``expandAtomicRMWInIR()``
|
||||
* expansion to __atomic_* libcalls for unsupported sizes.
|
||||
by overriding `expandAtomicRMWInIR()`
|
||||
* expansion to `__atomic_*` libcalls for unsupported sizes.
|
||||
* part-word atomicrmw/cmpxchg -> target-specific intrinsic by overriding
|
||||
``shouldExpandAtomicRMWInIR``, ``emitMaskedAtomicRMWIntrinsic``,
|
||||
``shouldExpandAtomicCmpXchgInIR``, and ``emitMaskedAtomicCmpXchgIntrinsic``.
|
||||
`shouldExpandAtomicRMWInIR`, `emitMaskedAtomicRMWIntrinsic`,
|
||||
`shouldExpandAtomicCmpXchgInIR`, and `emitMaskedAtomicCmpXchgIntrinsic`.
|
||||
|
||||
For an example of these, look at the ARM (first five lowerings) or RISC-V (last
|
||||
lowering) backend.
|
||||
|
|
@ -491,52 +485,58 @@ AtomicExpandPass can help support lowering of part-word atomicrmw or cmpxchg
|
|||
using this strategy by producing IR for any shifting and masking that can be
|
||||
performed outside of the LL/SC loop.
|
||||
|
||||
Libcalls: __atomic_*
|
||||
====================
|
||||
## Libcalls: `__atomic_*`
|
||||
|
||||
There are two kinds of atomic library calls that are generated by LLVM. Please
|
||||
note that both sets of library functions somewhat confusingly share the names of
|
||||
builtin functions defined by clang. Despite this, the library functions are
|
||||
not directly related to the builtins: it is *not* the case that ``__atomic_*``
|
||||
builtins lower to ``__atomic_*`` library calls and ``__sync_*`` builtins lower
|
||||
to ``__sync_*`` library calls.
|
||||
not directly related to the builtins: it is *not* the case that `__atomic_*`
|
||||
builtins lower to `__atomic_*` library calls and `__sync_*` builtins lower
|
||||
to `__sync_*` library calls.
|
||||
|
||||
The first set of library functions are named ``__atomic_*``. This set has been
|
||||
"standardized" by GCC, and is described below. (See also `GCC's documentation
|
||||
<https://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary>`_)
|
||||
The first set of library functions are named `__atomic_*`. This set has been
|
||||
"standardized" by GCC, and is described below. (See also [GCC's documentation])
|
||||
|
||||
[GCC's documentation]: https://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary
|
||||
|
||||
LLVM's AtomicExpandPass will translate atomic operations on data sizes above
|
||||
``MaxAtomicSizeInBitsSupported`` into calls to these functions.
|
||||
`MaxAtomicSizeInBitsSupported` into calls to these functions.
|
||||
|
||||
There are four generic functions, which can be called with data of any size or
|
||||
alignment::
|
||||
alignment:
|
||||
|
||||
```
|
||||
void __atomic_load(size_t size, void *ptr, void *ret, int ordering)
|
||||
void __atomic_store(size_t size, void *ptr, void *val, int ordering)
|
||||
void __atomic_exchange(size_t size, void *ptr, void *val, void *ret, int ordering)
|
||||
bool __atomic_compare_exchange(size_t size, void *ptr, void *expected, void *desired, int success_order, int failure_order)
|
||||
```
|
||||
|
||||
There are also size-specialized versions of the above functions, which can only
|
||||
be used with *naturally-aligned* pointers of the appropriate size. In the
|
||||
signatures below, "N" is one of 1, 2, 4, 8, and 16, and "iN" is the appropriate
|
||||
integer type of that size; if no such integer type exists, the specialization
|
||||
cannot be used::
|
||||
cannot be used:
|
||||
|
||||
```
|
||||
iN __atomic_load_N(iN *ptr, iN val, int ordering)
|
||||
void __atomic_store_N(iN *ptr, iN val, int ordering)
|
||||
iN __atomic_exchange_N(iN *ptr, iN val, int ordering)
|
||||
bool __atomic_compare_exchange_N(iN *ptr, iN *expected, iN desired, int success_order, int failure_order)
|
||||
```
|
||||
|
||||
Finally there are some read-modify-write functions, which are only available in
|
||||
the size-specific variants (any other sizes use a ``__atomic_compare_exchange``
|
||||
loop)::
|
||||
the size-specific variants (any other sizes use a `__atomic_compare_exchange`
|
||||
loop):
|
||||
|
||||
```
|
||||
iN __atomic_fetch_add_N(iN *ptr, iN val, int ordering)
|
||||
iN __atomic_fetch_sub_N(iN *ptr, iN val, int ordering)
|
||||
iN __atomic_fetch_and_N(iN *ptr, iN val, int ordering)
|
||||
iN __atomic_fetch_or_N(iN *ptr, iN val, int ordering)
|
||||
iN __atomic_fetch_xor_N(iN *ptr, iN val, int ordering)
|
||||
iN __atomic_fetch_nand_N(iN *ptr, iN val, int ordering)
|
||||
```
|
||||
|
||||
This set of library functions have some interesting implementation requirements
|
||||
to take note of:
|
||||
|
|
@ -552,9 +552,9 @@ to take note of:
|
|||
|
||||
- The set of atomic sizes supported lock-free must be a superset of the sizes
|
||||
any compiler can emit. That is: if a new compiler introduces support for
|
||||
inline-lock-free atomics of size N, the ``__atomic_*`` functions must also have a
|
||||
inline-lock-free atomics of size N, the `__atomic_*` functions must also have a
|
||||
lock-free implementation for size N. This is a requirement so that code
|
||||
produced by an old compiler (which will have called the ``__atomic_*`` function)
|
||||
produced by an old compiler (which will have called the `__atomic_*` function)
|
||||
interoperates with code produced by the new compiler (which will use native
|
||||
the atomic instruction).
|
||||
|
||||
|
|
@ -563,8 +563,7 @@ of these library functions by using the compiler atomic builtins themselves to
|
|||
implement the operations on naturally-aligned pointers of supported sizes, and a
|
||||
generic mutex implementation otherwise.
|
||||
|
||||
Libcalls: __sync_*
|
||||
==================
|
||||
## Libcalls: `__sync_*`
|
||||
|
||||
Some targets or OS/target combinations can support lock-free atomics, but for
|
||||
various reasons, it is not practical to emit the instructions inline.
|
||||
|
|
@ -579,9 +578,8 @@ instructions are not encodable. However, those instructions are available via a
|
|||
function call to a function with the longer encoding.
|
||||
|
||||
Additionally, a few OS/target pairs provide kernel-supported lock-free
|
||||
atomics. ARM/Linux is an example of this: the kernel `provides
|
||||
<https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt>`_ a
|
||||
function which on older CPUs contains a "magically-restartable" atomic sequence
|
||||
atomics. ARM/Linux is an example of this: the kernel [provides]
|
||||
a function which on older CPUs contains a "magically-restartable" atomic sequence
|
||||
(which looks atomic so long as there's only one CPU), and contains actual atomic
|
||||
instructions on newer multicore models. This sort of functionality can typically
|
||||
be provided on any architecture, if all CPUs which are missing atomic
|
||||
|
|
@ -590,17 +588,19 @@ case. The only common architecture without that property is SPARC -- SPARCV8 SMP
|
|||
systems were common, yet it doesn't support any sort of compare-and-swap
|
||||
operation.
|
||||
|
||||
Some targets (like RISC-V) support a ``+forced-atomics`` target feature, which
|
||||
[provides]: https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
|
||||
|
||||
Some targets (like RISC-V) support a `+forced-atomics` target feature, which
|
||||
enables the use of lock-free atomics even if LLVM is not aware of any specific
|
||||
OS support for them. In this case, the user is responsible for ensuring that
|
||||
necessary ``__sync_*`` implementations are available. Code using
|
||||
``+forced-atomics`` is ABI-incompatible with code not using the feature, if
|
||||
necessary `__sync_*` implementations are available. Code using
|
||||
`+forced-atomics` is ABI-incompatible with code not using the feature, if
|
||||
atomic variables cross the ABI boundary.
|
||||
|
||||
In either of these cases, the Target in LLVM can claim support for atomics of an
|
||||
appropriate size, and then implement some subset of the operations via libcalls
|
||||
to a ``__sync_*`` function. Such functions *must* not use locks in their
|
||||
implementation, because unlike the ``__atomic_*`` routines used by
|
||||
to a `__sync_*` function. Such functions *must* not use locks in their
|
||||
implementation, because unlike the `__atomic_*` routines used by
|
||||
AtomicExpandPass, these may be mixed-and-matched with native instructions by the
|
||||
target lowering.
|
||||
|
||||
|
|
@ -609,14 +609,15 @@ there is no issue with having multiple copies included in one binary. Thus,
|
|||
typically these routines are implemented by the statically-linked compiler
|
||||
runtime support library.
|
||||
|
||||
LLVM will emit a call to an appropriate ``__sync_*`` routine if the target
|
||||
ISelLowering code has set the corresponding ``ATOMIC_CMPXCHG``, ``ATOMIC_SWAP``,
|
||||
or ``ATOMIC_LOAD_*`` operation to "Expand", and if it has opted-into the
|
||||
availability of those library functions via a call to ``initSyncLibcalls()``.
|
||||
LLVM will emit a call to an appropriate `__sync_*` routine if the target
|
||||
ISelLowering code has set the corresponding `ATOMIC_CMPXCHG`, `ATOMIC_SWAP`,
|
||||
or `ATOMIC_LOAD_*` operation to "Expand", and if it has opted-into the
|
||||
availability of those library functions via a call to `initSyncLibcalls()`.
|
||||
|
||||
The full set of functions that may be called by LLVM is (for ``N`` being 1, 2,
|
||||
4, 8, or 16)::
|
||||
The full set of functions that may be called by LLVM is (for `N` being 1, 2,
|
||||
4, 8, or 16):
|
||||
|
||||
```
|
||||
iN __sync_val_compare_and_swap_N(iN *ptr, iN expected, iN desired)
|
||||
iN __sync_lock_test_and_set_N(iN *ptr, iN val)
|
||||
iN __sync_fetch_and_add_N(iN *ptr, iN val)
|
||||
|
|
@ -629,30 +630,33 @@ The full set of functions that may be called by LLVM is (for ``N`` being 1, 2,
|
|||
iN __sync_fetch_and_umax_N(iN *ptr, iN val)
|
||||
iN __sync_fetch_and_min_N(iN *ptr, iN val)
|
||||
iN __sync_fetch_and_umin_N(iN *ptr, iN val)
|
||||
```
|
||||
|
||||
This list doesn't include any function for atomic load or store; all known
|
||||
architectures support atomic loads and stores directly (possibly by emitting a
|
||||
fence on either side of a normal load or store.)
|
||||
|
||||
There's also, somewhat separately, the possibility to lower ``ATOMIC_FENCE`` to
|
||||
``__sync_synchronize()``. This may happen or not happen independent of all the
|
||||
above, controlled purely by ``setOperationAction(ISD::ATOMIC_FENCE, ...)``.
|
||||
There's also, somewhat separately, the possibility to lower `ATOMIC_FENCE` to
|
||||
`__sync_synchronize()`. This may happen or not happen independent of all the
|
||||
above, controlled purely by `setOperationAction(ISD::ATOMIC_FENCE, ...)`.
|
||||
|
||||
On AArch64, a variant of the __sync_* routines is used which contain the memory
|
||||
On AArch64, a variant of the `__sync_*` routines is used which contain the memory
|
||||
order as part of the function name. These routines may determine at runtime
|
||||
whether the single-instruction atomic operations which were introduced as part
|
||||
of AArch64 Large System Extensions "LSE" instruction set are available, or if
|
||||
it needs to fall back to an LL/SC loop. The following helper functions are
|
||||
implemented in both ``compiler-rt`` and ``libgcc`` libraries
|
||||
(``N`` is one of 1, 2, 4, 8, and ``M`` is one of 1, 2, 4, 8 and 16, and
|
||||
``ORDER`` is one of 'relax', 'acq', 'rel', 'acq_rel')::
|
||||
implemented in both `compiler-rt` and `libgcc` libraries
|
||||
(`N` is one of 1, 2, 4, 8, and `M` is one of 1, 2, 4, 8 and 16, and
|
||||
`ORDER` is one of 'relax', 'acq', 'rel', 'acq_rel'):
|
||||
|
||||
```
|
||||
iM __aarch64_casM_ORDER(iM expected, iM desired, iM *ptr)
|
||||
iN __aarch64_swpN_ORDER(iN val, iN *ptr)
|
||||
iN __aarch64_ldaddN_ORDER(iN val, iN *ptr)
|
||||
iN __aarch64_ldclrN_ORDER(iN val, iN *ptr)
|
||||
iN __aarch64_ldeorN_ORDER(iN val, iN *ptr)
|
||||
iN __aarch64_ldsetN_ORDER(iN val, iN *ptr)
|
||||
```
|
||||
|
||||
Please note, if LSE instruction set is specified for AArch64 target, then
|
||||
out-of-line atomics calls are not generated and single-instruction atomic
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,214 +1,186 @@
|
|||
========================================================
|
||||
Architecture & Platform Information for Compiler Writers
|
||||
========================================================
|
||||
# Architecture & Platform Information for Compiler Writers
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
.. note::
|
||||
```{note}
|
||||
This document is a work-in-progress. Additions and clarifications are
|
||||
welcome.
|
||||
```
|
||||
|
||||
This document is a work-in-progress. Additions and clarifications are
|
||||
welcome.
|
||||
## Hardware
|
||||
|
||||
Hardware
|
||||
========
|
||||
### AArch64 & ARM
|
||||
|
||||
AArch64 & ARM
|
||||
-------------
|
||||
* [ARMv8-A Architecture Reference Manual](https://developer.arm.com/docs/ddi0487/latest) This document covers both AArch64 and ARM instructions
|
||||
|
||||
* `ARMv8-A Architecture Reference Manual <https://developer.arm.com/docs/ddi0487/latest>`_ This document covers both AArch64 and ARM instructions
|
||||
* [ARMv7-A Architecture Reference Manual](https://developer.arm.com/docs/ddi0406/latest) This has some useful info on what is supported by older architecture versions.
|
||||
|
||||
* `ARMv7-A Architecture Reference Manual <https://developer.arm.com/docs/ddi0406/latest>`_ This has some useful info on what is supported by older architecture versions.
|
||||
* [ARMv7-M Architecture Reference Manual](https://developer.arm.com/docs/ddi0403/latest) This covers the Thumb2-only microcontrollers
|
||||
|
||||
* `ARMv7-M Architecture Reference Manual <https://developer.arm.com/docs/ddi0403/latest>`_ This covers the Thumb2-only microcontrollers
|
||||
* [ARMv6-M Architecture Reference Manual](https://developer.arm.com/docs/ddi0419/latest) This covers the Thumb1-only microcontrollers
|
||||
|
||||
* `ARMv6-M Architecture Reference Manual <https://developer.arm.com/docs/ddi0419/latest>`_ This covers the Thumb1-only microcontrollers
|
||||
* [ARM C Language Extensions](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053c/IHI0053C_acle_2_0.pdf)
|
||||
|
||||
* `ARM C Language Extensions <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053c/IHI0053C_acle_2_0.pdf>`_
|
||||
* [ARM NEON Intrinsics Reference](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073b/IHI0073B_arm_neon_intrinsics_ref.pdf)
|
||||
|
||||
* `ARM NEON Intrinsics Reference <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073b/IHI0073B_arm_neon_intrinsics_ref.pdf>`_
|
||||
* AArch32 [ABI Addenda and Errata](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0045d/IHI0045D_ABI_addenda.pdf)
|
||||
|
||||
* AArch32 `ABI Addenda and Errata <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0045d/IHI0045D_ABI_addenda.pdf>`_
|
||||
* [Cortex-A57 Software Optimization Guide](http://infocenter.arm.com/help/topic/com.arm.doc.uan0015b/Cortex_A57_Software_Optimization_Guide_external.pdf)
|
||||
|
||||
* `Cortex-A57 Software Optimization Guide <http://infocenter.arm.com/help/topic/com.arm.doc.uan0015b/Cortex_A57_Software_Optimization_Guide_external.pdf>`_
|
||||
* [Run-time ABI for the ARM Architecture](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0043d/IHI0043D_rtabi.pdf) This documents the `__aeabi_*` helper functions.
|
||||
|
||||
* `Run-time ABI for the ARM Architecture <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0043d/IHI0043D_rtabi.pdf>`_ This documents the __aeabi_* helper functions.
|
||||
### Itanium (ia64)
|
||||
|
||||
Itanium (ia64)
|
||||
--------------
|
||||
* [Itanium documentation](http://developer.intel.com/design/itanium2/documentation.htm)
|
||||
|
||||
* `Itanium documentation <http://developer.intel.com/design/itanium2/documentation.htm>`_
|
||||
### Lanai
|
||||
|
||||
Lanai
|
||||
-----
|
||||
* [Lanai Instruction Set Architecture](http://g.co/lanai/isa)
|
||||
|
||||
* `Lanai Instruction Set Architecture <http://g.co/lanai/isa>`_
|
||||
|
||||
### MIPS
|
||||
|
||||
MIPS
|
||||
----
|
||||
* [MIPS Processor Architecture](https://www.mips.com/products/)
|
||||
|
||||
* `MIPS Processor Architecture <https://www.mips.com/products/>`_
|
||||
* [MIPS 64-bit ELF Object File Specification](https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf)
|
||||
|
||||
* `MIPS 64-bit ELF Object File Specification <https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf>`_
|
||||
### PowerPC
|
||||
|
||||
PowerPC
|
||||
-------
|
||||
#### IBM - Official manuals and docs
|
||||
|
||||
IBM - Official manuals and docs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* [Power Instruction Set Architecture, Version 3.0B](https://openpowerfoundation.org/?resource_lib=power-isa-version-3-0)
|
||||
|
||||
* `Power Instruction Set Architecture, Version 3.0B <https://openpowerfoundation.org/?resource_lib=power-isa-version-3-0>`_
|
||||
* [POWER9 Processor User's Manual](https://openpowerfoundation.org/?resource_lib=power9-processor-users-manual)
|
||||
|
||||
* `POWER9 Processor User's Manual <https://openpowerfoundation.org/?resource_lib=power9-processor-users-manual>`_
|
||||
* [Power Instruction Set Architecture, Version 2.07B](https://openpowerfoundation.org/?resource_lib=ibm-power-isa-version-2-07-b)
|
||||
|
||||
* `Power Instruction Set Architecture, Version 2.07B <https://openpowerfoundation.org/?resource_lib=ibm-power-isa-version-2-07-b>`_
|
||||
* [POWER8 Processor User's Manual](https://openpowerfoundation.org/?resource_lib=power8-processor-users-manual)
|
||||
|
||||
* `POWER8 Processor User's Manual <https://openpowerfoundation.org/?resource_lib=power8-processor-users-manual>`_
|
||||
* [Power Instruction Set Architecture, Versions 2.03 through 2.06 (Internet Archive)](https://web.archive.org/web/20121124005736/https://www.power.org/technology-introduction/standards-specifications)
|
||||
|
||||
* `Power Instruction Set Architecture, Versions 2.03 through 2.06 (Internet Archive) <https://web.archive.org/web/20121124005736/https://www.power.org/technology-introduction/standards-specifications>`_
|
||||
* [IBM AIX 7.2 POWER Assembly Reference](https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/assembler/alangref_kickoff.html)
|
||||
|
||||
* `IBM AIX 7.2 POWER Assembly Reference <https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/assembler/alangref_kickoff.html>`_
|
||||
* [IBM AIX/5L for POWER Assembly Reference](http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixassem/alangref/alangreftfrm.htm)
|
||||
|
||||
* `IBM AIX/5L for POWER Assembly Reference <http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixassem/alangref/alangreftfrm.htm>`_
|
||||
#### Embedded PowerPC Processors manuals and docs
|
||||
|
||||
Embedded PowerPC Processors manuals and docs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* [Book E: Enhanced PowerPC Architecture](https://www.nxp.com/docs/en/user-guide/BOOK_EUM.pdf)
|
||||
|
||||
* `Book E: Enhanced PowerPC Architecture <https://www.nxp.com/docs/en/user-guide/BOOK_EUM.pdf>`_
|
||||
* [EREF: A Programmer's Reference Manual for Freescale Embedded Processors (EREFRM)](https://www.nxp.com/files-static/32bit/doc/ref_manual/EREF_RM.pdf)
|
||||
|
||||
* `EREF: A Programmer's Reference Manual for Freescale Embedded Processors (EREFRM) <https://www.nxp.com/files-static/32bit/doc/ref_manual/EREF_RM.pdf>`_
|
||||
* [Signal Processing Engine (SPE) Programming Environments Manual: A Supplement to the EREF](https://www.nxp.com/docs/en/reference-manual/SPEPEM.pdf)
|
||||
|
||||
* `Signal Processing Engine (SPE) Programming Environments Manual: A Supplement to the EREF <https://www.nxp.com/docs/en/reference-manual/SPEPEM.pdf>`_
|
||||
* [Variable-Length Encoding (VLE) Programming Environments Manual: A Supplement to the EREF](https://www.nxp.com/docs/en/reference-manual/VLEPEM.pdf)
|
||||
|
||||
* `Variable-Length Encoding (VLE) Programming Environments Manual: A Supplement to the EREF <https://www.nxp.com/docs/en/reference-manual/VLEPEM.pdf>`_
|
||||
#### Other documents, collections, notes
|
||||
|
||||
Other documents, collections, notes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* [PowerPC Compiler Writer's Guide](http://www.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF7785256996007558C6)
|
||||
* [Intro to PowerPC Architecture](http://www.ibm.com/developerworks/linux/library/l-powarch/)
|
||||
* [Various IBM specifications and white papers](https://www.power.org/documentation/?document_company=105&document_category=all&publish_year=all&grid_order=DESC&grid_sort=title)
|
||||
* [PowerPC ABI documents](http://penguinppc.org/dev/#library)
|
||||
* [PowerPC64 alignment of long doubles (from GCC)](http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00997.html)
|
||||
* [Long branch stubs for powerpc64-linux (from binutils)](http://sources.redhat.com/ml/binutils/2002-04/msg00573.html)
|
||||
|
||||
* `PowerPC Compiler Writer's Guide <http://www.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF7785256996007558C6>`_
|
||||
* `Intro to PowerPC Architecture <http://www.ibm.com/developerworks/linux/library/l-powarch/>`_
|
||||
* `Various IBM specifications and white papers <https://www.power.org/documentation/?document_company=105&document_category=all&publish_year=all&grid_order=DESC&grid_sort=title>`_
|
||||
* `PowerPC ABI documents <http://penguinppc.org/dev/#library>`_
|
||||
* `PowerPC64 alignment of long doubles (from GCC) <http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00997.html>`_
|
||||
* `Long branch stubs for powerpc64-linux (from binutils) <http://sources.redhat.com/ml/binutils/2002-04/msg00573.html>`_
|
||||
### AMDGPU
|
||||
|
||||
AMDGPU
|
||||
------
|
||||
Refer to {doc}`AMDGPUUsage` for additional documentation.
|
||||
|
||||
Refer to :doc:`AMDGPUUsage` for additional documentation.
|
||||
### RISC-V
|
||||
* [RISC-V User-Level ISA Specification](https://riscv.org/specifications/)
|
||||
|
||||
RISC-V
|
||||
------
|
||||
* `RISC-V User-Level ISA Specification <https://riscv.org/specifications/>`_
|
||||
### C-SKY
|
||||
* [C-SKY Architecture User Guide](https://github.com/c-sky/csky-doc/blob/master/CSKY%20Architecture%20user_guide.pdf)
|
||||
* [C-SKY V2 ABI](https://github.com/c-sky/csky-doc/blob/master/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf)
|
||||
|
||||
C-SKY
|
||||
------
|
||||
* `C-SKY Architecture User Guide <https://github.com/c-sky/csky-doc/blob/master/CSKY%20Architecture%20user_guide.pdf>`_
|
||||
* `C-SKY V2 ABI <https://github.com/c-sky/csky-doc/blob/master/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf>`_
|
||||
### LoongArch
|
||||
* [LoongArch Reference Manual - Volume 1: Basic Architecture](https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html)
|
||||
* [LoongArch ELF ABI specification](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html)
|
||||
|
||||
LoongArch
|
||||
---------
|
||||
* `LoongArch Reference Manual - Volume 1: Basic Architecture <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html>`_
|
||||
* `LoongArch ELF ABI specification <https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html>`_
|
||||
### SPARC
|
||||
|
||||
SPARC
|
||||
-----
|
||||
* [SPARC standards](http://sparc.org/standards)
|
||||
* [SPARC V9 ABI](http://sparc.org/standards/64.psabi.1.35.ps.Z)
|
||||
* [SPARC V8 ABI](http://sparc.org/standards/psABI3rd.pdf)
|
||||
|
||||
* `SPARC standards <http://sparc.org/standards>`_
|
||||
* `SPARC V9 ABI <http://sparc.org/standards/64.psabi.1.35.ps.Z>`_
|
||||
* `SPARC V8 ABI <http://sparc.org/standards/psABI3rd.pdf>`_
|
||||
### SystemZ
|
||||
|
||||
SystemZ
|
||||
-------
|
||||
* [z/Architecture Principles of Operation (registration required, free sign-up)](http://www-01.ibm.com/support/docview.wss?uid=isg2b9de5f05a9d57819852571c500428f9a)
|
||||
|
||||
* `z/Architecture Principles of Operation (registration required, free sign-up) <http://www-01.ibm.com/support/docview.wss?uid=isg2b9de5f05a9d57819852571c500428f9a>`_
|
||||
### VE
|
||||
|
||||
VE
|
||||
--
|
||||
* [NEC SX-Aurora TSUBASA ISA Guide](https://www.hpc.nec/documents/guide/pdfs/Aurora_ISA_guide.pdf)
|
||||
* [NEC SX-Aurora TSUBASA manuals and documentation](https://www.hpc.nec/documentation)
|
||||
|
||||
* `NEC SX-Aurora TSUBASA ISA Guide <https://www.hpc.nec/documents/guide/pdfs/Aurora_ISA_guide.pdf>`_
|
||||
* `NEC SX-Aurora TSUBASA manuals and documentation <https://www.hpc.nec/documentation>`_
|
||||
### X86
|
||||
|
||||
X86
|
||||
---
|
||||
* [AMD processor manuals](http://developer.amd.com/resources/developer-guides-manuals/)
|
||||
* [Intel 64 and IA-32 manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html)
|
||||
* [Intel Itanium documentation](http://www.intel.com/design/itanium/documentation.htm?iid=ipp_srvr_proc_itanium2+techdocs)
|
||||
* [X86 and X86-64 SysV psABI](https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI)
|
||||
* [Calling conventions for different C++ compilers and operating systems](http://www.agner.org/optimize/calling_conventions.pdf)
|
||||
|
||||
* `AMD processor manuals <http://developer.amd.com/resources/developer-guides-manuals/>`_
|
||||
* `Intel 64 and IA-32 manuals <http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html>`_
|
||||
* `Intel Itanium documentation <http://www.intel.com/design/itanium/documentation.htm?iid=ipp_srvr_proc_itanium2+techdocs>`_
|
||||
* `X86 and X86-64 SysV psABI <https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI>`_
|
||||
* `Calling conventions for different C++ compilers and operating systems <http://www.agner.org/optimize/calling_conventions.pdf>`_
|
||||
### XCore
|
||||
|
||||
XCore
|
||||
-----
|
||||
* [The XMOS XS1 Architecture (ISA)](https://www.xmos.ai/download/The-XMOS-XS1-Architecture%281.0%29.pdf)
|
||||
* [The XMOS XS2 Architecture (ISA)](https://www.xmos.ai/download/xCORE-200:-The-XMOS-XS2-Architecture-%28ISA%29%281.1%29.pdf)
|
||||
* [Tools Development Guide (includes ABI)](https://www.xmos.ai/download/Tools-Development-Guide%282.1%29.pdf)
|
||||
|
||||
* `The XMOS XS1 Architecture (ISA) <https://www.xmos.ai/download/The-XMOS-XS1-Architecture%281.0%29.pdf>`_
|
||||
* `The XMOS XS2 Architecture (ISA) <https://www.xmos.ai/download/xCORE-200:-The-XMOS-XS2-Architecture-%28ISA%29%281.1%29.pdf>`_
|
||||
* `Tools Development Guide (includes ABI) <https://www.xmos.ai/download/Tools-Development-Guide%282.1%29.pdf>`_
|
||||
### Hexagon
|
||||
|
||||
Hexagon
|
||||
-------
|
||||
* [Hexagon Programmer's Reference Manuals and Hexagon ABI Specification (registration required, free sign-up)](https://developer.qualcomm.com/software/hexagon-dsp-sdk/tools)
|
||||
|
||||
* `Hexagon Programmer's Reference Manuals and Hexagon ABI Specification (registration required, free sign-up) <https://developer.qualcomm.com/software/hexagon-dsp-sdk/tools>`_
|
||||
### Other relevant lists
|
||||
|
||||
Other relevant lists
|
||||
--------------------
|
||||
* [GCC reading list](http://gcc.gnu.org/readings.html)
|
||||
|
||||
* `GCC reading list <http://gcc.gnu.org/readings.html>`_
|
||||
## ABI
|
||||
|
||||
ABI
|
||||
===
|
||||
* [System V Application Binary Interface](http://www.sco.com/developers/gabi/latest/contents.html)
|
||||
* [Itanium C++ ABI](http://itanium-cxx-abi.github.io/cxx-abi/) (This is used for all non-Windows targets.)
|
||||
|
||||
* `System V Application Binary Interface <http://www.sco.com/developers/gabi/latest/contents.html>`_
|
||||
* `Itanium C++ ABI <http://itanium-cxx-abi.github.io/cxx-abi/>`_ (This is used for all non-Windows targets.)
|
||||
### Linux
|
||||
|
||||
Linux
|
||||
-----
|
||||
* [Linux extensions to gabi](https://github.com/hjl-tools/linux-abi/wiki/Linux-Extensions-to-gABI)
|
||||
* [64-Bit ELF V2 ABI Specification: Power Architecture](https://openpowerfoundation.org/?resource_lib=64-bit-elf-v2-abi-specification-power-architecture)
|
||||
|
||||
* `Linux extensions to gabi <https://github.com/hjl-tools/linux-abi/wiki/Linux-Extensions-to-gABI>`_
|
||||
* `64-Bit ELF V2 ABI Specification: Power Architecture <https://openpowerfoundation.org/?resource_lib=64-bit-elf-v2-abi-specification-power-architecture>`_
|
||||
* [OpenPOWER ELFv2 Errata: ELFv2 ABI Version 1.4](https://openpowerfoundation.org/?resource_lib=openpower-elfv2-errata-elfv2-abi-version-1-4)
|
||||
* [PowerPC 64-bit ELF ABI Supplement](http://www.linuxbase.org/spec/ELF/ppc64/)
|
||||
* [Procedure Call Standard for the AArch64 Architecture](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055a/IHI0055A_aapcs64.pdf)
|
||||
* [Procedure Call Standard for the ARM Architecture](https://developer.arm.com/docs/ihi0042/latest)
|
||||
* [ELF for the ARM Architecture](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/IHI0044E_aaelf.pdf)
|
||||
* [ELF for the ARM 64-bit Architecture (AArch64)](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056a/IHI0056A_aaelf64.pdf)
|
||||
* [System z ELF ABI Supplement](http://legacy.redhat.com/pub/redhat/linux/7.1/es/os/s390x/doc/lzsabi0.pdf)
|
||||
|
||||
* `OpenPOWER ELFv2 Errata: ELFv2 ABI Version 1.4 <https://openpowerfoundation.org/?resource_lib=openpower-elfv2-errata-elfv2-abi-version-1-4>`_
|
||||
* `PowerPC 64-bit ELF ABI Supplement <http://www.linuxbase.org/spec/ELF/ppc64/>`_
|
||||
* `Procedure Call Standard for the AArch64 Architecture <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055a/IHI0055A_aapcs64.pdf>`_
|
||||
* `Procedure Call Standard for the ARM Architecture <https://developer.arm.com/docs/ihi0042/latest>`_
|
||||
* `ELF for the ARM Architecture <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/IHI0044E_aaelf.pdf>`_
|
||||
* `ELF for the ARM 64-bit Architecture (AArch64) <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056a/IHI0056A_aaelf64.pdf>`_
|
||||
* `System z ELF ABI Supplement <http://legacy.redhat.com/pub/redhat/linux/7.1/es/os/s390x/doc/lzsabi0.pdf>`_
|
||||
### macOS
|
||||
|
||||
macOS
|
||||
-----
|
||||
* [Mach-O Runtime Architecture](http://developer.apple.com/documentation/Darwin/RuntimeArchitecture-date.html)
|
||||
* [Notes on Mach-O ABI](http://www.unsanity.org/archives/000044.php)
|
||||
* [ARM64 Function Calling Conventions](https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html)
|
||||
|
||||
* `Mach-O Runtime Architecture <http://developer.apple.com/documentation/Darwin/RuntimeArchitecture-date.html>`_
|
||||
* `Notes on Mach-O ABI <http://www.unsanity.org/archives/000044.php>`_
|
||||
* `ARM64 Function Calling Conventions <https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html>`_
|
||||
### Windows
|
||||
|
||||
Windows
|
||||
-------
|
||||
* [Microsoft PE/COFF Specification](http://www.microsoft.com/whdc/system/platform/firmware/pecoff.mspx)
|
||||
* [ARM64 exception handling](https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling)
|
||||
* [ARM exception handling](https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling)
|
||||
* [Overview of ARM64 ABI conventions](https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions)
|
||||
* [Overview of ARM32 ABI Conventions](https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions)
|
||||
|
||||
* `Microsoft PE/COFF Specification <http://www.microsoft.com/whdc/system/platform/firmware/pecoff.mspx>`_
|
||||
* `ARM64 exception handling <https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling>`_
|
||||
* `ARM exception handling <https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling>`_
|
||||
* `Overview of ARM64 ABI conventions <https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions>`_
|
||||
* `Overview of ARM32 ABI Conventions <https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions>`_
|
||||
## NVPTX
|
||||
|
||||
NVPTX
|
||||
=====
|
||||
|
||||
* `CUDA Documentation <http://docs.nvidia.com/cuda/index.html>`_ includes the PTX
|
||||
* [CUDA Documentation](http://docs.nvidia.com/cuda/index.html) includes the PTX
|
||||
ISA and Driver API documentation
|
||||
|
||||
SPIR-V
|
||||
======
|
||||
## SPIR-V
|
||||
|
||||
* `SPIR-V documentation <https://www.khronos.org/registry/SPIR-V/>`_
|
||||
* [SPIR-V documentation](https://www.khronos.org/registry/SPIR-V/)
|
||||
|
||||
Miscellaneous Resources
|
||||
=======================
|
||||
## Miscellaneous Resources
|
||||
|
||||
* `Executable File Formats <https://wiki.osdev.org/Category:Executable_Formats>`_
|
||||
* [Executable File Formats](https://wiki.osdev.org/Category:Executable_Formats)
|
||||
has a list of various executable file formats.
|
||||
|
||||
* `GCC prefetch project <http://gcc.gnu.org/projects/prefetch.html>`_ page has a
|
||||
* [GCC prefetch project](http://gcc.gnu.org/projects/prefetch.html) page has a
|
||||
good survey of the prefetching capabilities of a variety of modern
|
||||
processors.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,25 +1,20 @@
|
|||
.. role:: raw-html(raw)
|
||||
:format: html
|
||||
# LLVM Code Coverage Mapping Format
|
||||
|
||||
=================================
|
||||
LLVM Code Coverage Mapping Format
|
||||
=================================
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
============
|
||||
## Introduction
|
||||
|
||||
LLVM's code coverage mapping format is used to provide code coverage
|
||||
analysis using LLVM's and Clang's instrumentation based profiling
|
||||
(Clang's ``-fprofile-instr-generate`` option).
|
||||
(Clang's `-fprofile-instr-generate` option).
|
||||
|
||||
This document is aimed at those who would like to know how LLVM's code coverage
|
||||
mapping works under the hood. A prior knowledge of how Clang's profile guided
|
||||
optimization works is useful, but not required. For those interested in using
|
||||
LLVM to provide code coverage analysis for their own programs, see the `Clang
|
||||
documentation <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html>`.
|
||||
LLVM to provide code coverage analysis for their own programs, see the [Clang
|
||||
documentation](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
|
||||
|
||||
We start by briefly describing LLVM's code coverage mapping format and the
|
||||
way that Clang and LLVM's code coverage tool work with this format. After
|
||||
|
|
@ -27,8 +22,7 @@ the basics are down, more advanced features of the coverage mapping format
|
|||
are discussed - such as the data structures, LLVM IR representation and
|
||||
the binary encoding.
|
||||
|
||||
High Level Overview
|
||||
===================
|
||||
## High Level Overview
|
||||
|
||||
LLVM's code coverage mapping format is designed to be a self contained
|
||||
data format that can be embedded into the LLVM IR and into object files.
|
||||
|
|
@ -39,7 +33,7 @@ after running the instrumented version of the program.
|
|||
|
||||
The mapping data is used in two places in the code coverage process:
|
||||
|
||||
1. When clang compiles a source file with ``-fcoverage-mapping``, it
|
||||
1. When clang compiles a source file with `-fcoverage-mapping`, it
|
||||
generates the mapping information that describes the mapping between the
|
||||
source ranges and the profiling instrumentation counters.
|
||||
This information gets embedded into the LLVM IR and conveniently
|
||||
|
|
@ -59,8 +53,7 @@ instead of emitting mapping information for each statement in a function, the
|
|||
frontend is allowed to group the statements with the same execution count into
|
||||
regions of code, and emit the mapping information only for those regions.
|
||||
|
||||
Advanced Concepts
|
||||
=================
|
||||
## Advanced Concepts
|
||||
|
||||
The remainder of this guide is meant to give you insight into the way the
|
||||
coverage mapping format works.
|
||||
|
|
@ -71,23 +64,22 @@ For each function that requires code coverage, the frontend has to create
|
|||
coverage mapping data that can map between the source code ranges and
|
||||
the profile instrumentation counters for that function.
|
||||
|
||||
Mapping Region
|
||||
--------------
|
||||
### Mapping Region
|
||||
|
||||
The function's coverage mapping data contains an array of mapping regions.
|
||||
A mapping region stores the `source code range`_ that is covered by this region,
|
||||
the `file id <coverage file id_>`_, the `coverage mapping counter`_ and
|
||||
A mapping region stores the {ref}`source code range` that is covered by this region,
|
||||
the {ref}`file id <coverage file id>`, the {ref}`coverage mapping counter` and
|
||||
the region's kind.
|
||||
There are several kinds of mapping regions:
|
||||
|
||||
* Code regions associate portions of source code and `coverage mapping
|
||||
counters`_. They make up the majority of the mapping regions. They are used
|
||||
* Code regions associate portions of source code and {ref}`coverage mapping counters <coverage mapping counters>`.
|
||||
They make up the majority of the mapping regions. They are used
|
||||
by the code coverage tool to compute the execution counts for lines,
|
||||
highlight the regions of code that were never executed, and to obtain
|
||||
the various code coverage statistics for a function.
|
||||
For example:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int main(int argc, const char *argv[]) </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Code Region from 1:40 to 9:2</span>
|
||||
<pre class='highlight' style='line-height:initial;'><span>int main(int argc, const char *argv[]) </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Code Region from 1:40 to 9:2</span>
|
||||
<span style='background-color:#4A789C'> </span>
|
||||
<span style='background-color:#4A789C'> if (argc > 1) </span><span style='background-color:#85C1F5'>{ </span> <span class='c1'>// Code Region from 3:17 to 5:4</span>
|
||||
<span style='background-color:#85C1F5'> printf("%s\n", argv[1]); </span>
|
||||
|
|
@ -96,46 +88,49 @@ There are several kinds of mapping regions:
|
|||
<span style='background-color:#F6D55D'> }</span><span style='background-color:#4A789C'> </span>
|
||||
<span style='background-color:#4A789C'> return 0; </span>
|
||||
<span style='background-color:#4A789C'>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
* Skipped regions are used to represent source ranges that were skipped
|
||||
by Clang's preprocessor. They don't associate with
|
||||
`coverage mapping counters`_, as the frontend knows that they are never
|
||||
{ref}`coverage mapping counters`, as the frontend knows that they are never
|
||||
executed. They are used by the code coverage tool to mark the skipped lines
|
||||
inside a function as non-code lines that don't have execution counts.
|
||||
For example:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int main() </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Code Region from 1:12 to 6:2</span>
|
||||
<pre class='highlight' style='line-height:initial;'><span>int main() </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Code Region from 1:12 to 6:2</span>
|
||||
<span style='background-color:#85C1F5'>#ifdef DEBUG </span> <span class='c1'>// Skipped Region from 2:1 to 4:2</span>
|
||||
<span style='background-color:#85C1F5'> printf("Hello world"); </span>
|
||||
<span style='background-color:#85C1F5'>#</span><span style='background-color:#4A789C'>endif </span>
|
||||
<span style='background-color:#4A789C'> return 0; </span>
|
||||
<span style='background-color:#4A789C'>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
* Expansion regions are used to represent Clang's macro expansions. They
|
||||
have an additional property - *expanded file id*. This property can be
|
||||
used by the code coverage tool to find the mapping regions that are created
|
||||
as a result of this macro expansion, by checking if their file id matches the
|
||||
expanded file id. They don't associate with `coverage mapping counters`_,
|
||||
expanded file id. They don't associate with {ref}`coverage mapping counters`,
|
||||
as the code coverage tool can determine the execution count for this region
|
||||
by looking up the execution count of the first region with a corresponding
|
||||
file id.
|
||||
For example:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int func(int x) </span><span style='background-color:#4A789C'>{ </span>
|
||||
<pre class='highlight' style='line-height:initial;'><span>int func(int x) </span><span style='background-color:#4A789C'>{ </span>
|
||||
<span style='background-color:#4A789C'> #define MAX(x,y) </span><span style='background-color:#85C1F5'>((x) > (y)? </span><span style='background-color:#F6D55D'>(x)</span><span style='background-color:#85C1F5'> : </span><span style='background-color:#F4BA70'>(y)</span><span style='background-color:#85C1F5'>)</span><span style='background-color:#4A789C'> </span>
|
||||
<span style='background-color:#4A789C'> return </span><span style='background-color:#7FCA9F'>MAX</span><span style='background-color:#4A789C'>(x, 42); </span> <span class='c1'>// Expansion Region from 3:10 to 3:13</span>
|
||||
<span style='background-color:#4A789C'>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
* Branch regions associate instrumentable branch conditions in the source code
|
||||
with a `coverage mapping counter`_ to track how many times an individual
|
||||
condition evaluated to 'true' and another `coverage mapping counter`_ to
|
||||
with a {ref}`coverage mapping counter` to track how many times an individual
|
||||
condition evaluated to 'true' and another {ref}`coverage mapping counter` to
|
||||
track how many times that condition evaluated to false. Instrumentable
|
||||
branch conditions may comprise larger boolean expressions using boolean
|
||||
logical operators. The 'true' and 'false' cases reflect unique branch paths
|
||||
that can be traced back to the source code.
|
||||
For example:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int func(int x, int y) {
|
||||
<pre class='highlight' style='line-height:initial;'><span>int func(int x, int y) {
|
||||
<span> if (<span style='background-color:#4A789C'>(x > 1)</span> || <span style='background-color:#4A789C'>(y > 3)</span>) {</span> <span class='c1'>// Branch Region from 3:6 to 3:12</span>
|
||||
<span> </span><span class='c1'>// Branch Region from 3:17 to 3:23</span>
|
||||
<span> printf("%d\n", x); </span>
|
||||
|
|
@ -144,7 +139,7 @@ There are several kinds of mapping regions:
|
|||
<span> }</span>
|
||||
<span> return 0; </span>
|
||||
<span>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
* Decision regions associate multiple branch regions with a boolean
|
||||
expression in the source code. This information also includes the number of
|
||||
|
|
@ -159,37 +154,34 @@ There are several kinds of mapping regions:
|
|||
*llvm-cov* to reconstruct the control flow around the conditions in order to
|
||||
comprehend the full list of potential executable test vectors.
|
||||
|
||||
.. _source code range:
|
||||
(source code range)=
|
||||
|
||||
Source Range:
|
||||
^^^^^^^^^^^^^
|
||||
#### Source Range:
|
||||
|
||||
The source range record contains the starting and ending location of a certain
|
||||
mapping region. Both locations include the line and the column numbers.
|
||||
|
||||
.. _coverage file id:
|
||||
(coverage file id)=
|
||||
|
||||
File ID:
|
||||
^^^^^^^^
|
||||
#### File ID:
|
||||
|
||||
The file id an integer value that tells us
|
||||
in which source file or macro expansion is this region located.
|
||||
It enables Clang to produce mapping information for the code
|
||||
defined inside macros, like this example demonstrates:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>void func(const char *str) </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Code Region from 1:28 to 6:2 with file id 0</span>
|
||||
<pre class='highlight' style='line-height:initial;'><span>void func(const char *str) </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Code Region from 1:28 to 6:2 with file id 0</span>
|
||||
<span style='background-color:#4A789C'> #define PUT </span><span style='background-color:#85C1F5'>printf("%s\n", str)</span><span style='background-color:#4A789C'> </span> <span class='c1'>// 2 Code Regions from 2:15 to 2:34 with file ids 1 and 2</span>
|
||||
<span style='background-color:#4A789C'> if(*str) </span>
|
||||
<span style='background-color:#4A789C'> </span><span style='background-color:#F6D55D'>PUT</span><span style='background-color:#4A789C'>; </span> <span class='c1'>// Expansion Region from 4:5 to 4:8 with file id 0 that expands a macro with file id 1</span>
|
||||
<span style='background-color:#4A789C'> </span><span style='background-color:#F6D55D'>PUT</span><span style='background-color:#4A789C'>; </span> <span class='c1'>// Expansion Region from 5:3 to 5:6 with file id 0 that expands a macro with file id 2</span>
|
||||
<span style='background-color:#4A789C'>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
.. _coverage mapping counter:
|
||||
.. _coverage mapping counters:
|
||||
(coverage mapping counter)=
|
||||
(coverage mapping counters)=
|
||||
|
||||
Counter:
|
||||
^^^^^^^^
|
||||
#### Counter:
|
||||
|
||||
A coverage mapping counter can represent a reference to the profile
|
||||
instrumentation counter. The execution count for a region with such counter
|
||||
|
|
@ -204,7 +196,7 @@ subtracting them from one another.
|
|||
In the example below, a subtraction expression is used to compute the execution
|
||||
count for the compound statement that follows the *else* keyword:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int main(int argc, const char *argv[]) </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Region's counter is a reference to the profile counter #0</span>
|
||||
<pre class='highlight' style='line-height:initial;'><span>int main(int argc, const char *argv[]) </span><span style='background-color:#4A789C'>{ </span> <span class='c1'>// Region's counter is a reference to the profile counter #0</span>
|
||||
<span style='background-color:#4A789C'> </span>
|
||||
<span style='background-color:#4A789C'> if (argc > 1) </span><span style='background-color:#85C1F5'>{ </span> <span class='c1'>// Region's counter is a reference to the profile counter #1</span>
|
||||
<span style='background-color:#85C1F5'> printf("%s\n", argv[1]); </span><span> </span>
|
||||
|
|
@ -213,17 +205,17 @@ count for the compound statement that follows the *else* keyword:
|
|||
<span style='background-color:#F6D55D'> }</span><span style='background-color:#4A789C'> </span>
|
||||
<span style='background-color:#4A789C'> return 0; </span>
|
||||
<span style='background-color:#4A789C'>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
Finally, a coverage mapping counter can also represent an execution count of
|
||||
of zero. The zero counter is used to provide coverage mapping for
|
||||
unreachable statements and expressions, like in the example below:
|
||||
|
||||
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int main() </span><span style='background-color:#4A789C'>{ </span>
|
||||
<pre class='highlight' style='line-height:initial;'><span>int main() </span><span style='background-color:#4A789C'>{ </span>
|
||||
<span style='background-color:#4A789C'> return 0; </span>
|
||||
<span style='background-color:#4A789C'> </span><span style='background-color:#85C1F5'>printf("Hello world!\n")</span><span style='background-color:#4A789C'>; </span> <span class='c1'>// Unreachable region's counter is zero</span>
|
||||
<span style='background-color:#4A789C'>}</span>
|
||||
</pre>`
|
||||
</pre>
|
||||
|
||||
The zero counters allow the code coverage tool to display proper line execution
|
||||
counts for the unreachable lines and highlight the unreachable code.
|
||||
|
|
@ -235,8 +227,7 @@ code and refer to two coverage mapping counters, one to track the number of
|
|||
times the branch condition evaluated to "true", and one to track the number of
|
||||
times the branch condition evaluated to "false".
|
||||
|
||||
LLVM IR Representation
|
||||
======================
|
||||
## LLVM IR Representation
|
||||
|
||||
The coverage mapping data is stored in the LLVM IR using a global constant
|
||||
structure variable called *__llvm_coverage_mapping* with the *IPSK_covmap*
|
||||
|
|
@ -244,16 +235,16 @@ section specifier (i.e. ".lcovmap$M" on Windows and "__llvm_covmap" elsewhere).
|
|||
|
||||
For example, let’s consider a C file and how it gets compiled to LLVM:
|
||||
|
||||
.. _coverage mapping sample:
|
||||
(coverage mapping sample)=
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int foo() {
|
||||
return 42;
|
||||
}
|
||||
int bar() {
|
||||
return 13;
|
||||
}
|
||||
```c
|
||||
int foo() {
|
||||
return 42;
|
||||
}
|
||||
int bar() {
|
||||
return 13;
|
||||
}
|
||||
```
|
||||
|
||||
The coverage mapping variable generated by Clang has 2 fields:
|
||||
|
||||
|
|
@ -265,19 +256,19 @@ The variable has 8-byte alignment because ld64 cannot always pack symbols from
|
|||
different object files tightly (the word-level alignment assumption is baked in
|
||||
too deeply).
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
@__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [32 x i8] }
|
||||
```llvm
|
||||
@__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [32 x i8] }
|
||||
{
|
||||
{ i32, i32, i32, i32 } ; Coverage map header
|
||||
{
|
||||
{ i32, i32, i32, i32 } ; Coverage map header
|
||||
{
|
||||
i32 0, ; Always 0. In prior versions, the number of affixed function records
|
||||
i32 32, ; The length of the string that contains the encoded translation unit filenames
|
||||
i32 0, ; Always 0. In prior versions, the length of the affixed string that contains the encoded coverage mapping data
|
||||
i32 3, ; Coverage mapping format version
|
||||
},
|
||||
[32 x i8] c"..." ; Encoded data (dissected later)
|
||||
}, section "__llvm_covmap", align 8
|
||||
i32 0, ; Always 0. In prior versions, the number of affixed function records
|
||||
i32 32, ; The length of the string that contains the encoded translation unit filenames
|
||||
i32 0, ; Always 0. In prior versions, the length of the affixed string that contains the encoded coverage mapping data
|
||||
i32 3, ; Coverage mapping format version
|
||||
},
|
||||
[32 x i8] c"..." ; Encoded data (dissected later)
|
||||
}, section "__llvm_covmap", align 8
|
||||
```
|
||||
|
||||
The current version of the format is version 6.
|
||||
|
||||
|
|
@ -311,25 +302,24 @@ column end locations was introduced to indicate gap regions.
|
|||
|
||||
In version 1, the function record for *foo* was defined as follows:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
{ i8*, i32, i32, i64 } { i8* getelementptr inbounds ([3 x i8]* @__profn_foo, i32 0, i32 0), ; Function's name
|
||||
i32 3, ; Function's name length
|
||||
i32 9, ; Function's encoded coverage mapping data string length
|
||||
i64 0 ; Function's structural hash
|
||||
}
|
||||
```llvm
|
||||
{ i8*, i32, i32, i64 } { i8* getelementptr inbounds ([3 x i8]* @__profn_foo, i32 0, i32 0), ; Function's name
|
||||
i32 3, ; Function's name length
|
||||
i32 9, ; Function's encoded coverage mapping data string length
|
||||
i64 0 ; Function's structural hash
|
||||
}
|
||||
```
|
||||
|
||||
In version 2, the function record for *foo* was defined as follows:
|
||||
|
||||
.. code-block:: llvm
|
||||
```llvm
|
||||
{ i64, i32, i64 } {
|
||||
i64 0x5cf8c24cdb18bdac, ; Function's name MD5
|
||||
i32 9, ; Function's encoded coverage mapping data string length
|
||||
i64 0 ; Function's structural hash
|
||||
```
|
||||
|
||||
{ i64, i32, i64 } {
|
||||
i64 0x5cf8c24cdb18bdac, ; Function's name MD5
|
||||
i32 9, ; Function's encoded coverage mapping data string length
|
||||
i64 0 ; Function's structural hash
|
||||
|
||||
Coverage Mapping Header:
|
||||
------------------------
|
||||
### Coverage Mapping Header:
|
||||
|
||||
As shown above, the coverage mapping header has the following fields:
|
||||
|
||||
|
|
@ -341,33 +331,31 @@ As shown above, the coverage mapping header has the following fields:
|
|||
|
||||
* The format version. The current version is 6 (encoded as a 5).
|
||||
|
||||
.. _function records:
|
||||
(function records)=
|
||||
|
||||
Function record:
|
||||
----------------
|
||||
### Function record:
|
||||
|
||||
A function record is a structure of the following type:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
{ i64, i32, i64, i64, [? x i8] }
|
||||
```llvm
|
||||
{ i64, i32, i64, i64, [? x i8] }
|
||||
```
|
||||
|
||||
It contains the function name's MD5, the length of the encoded mapping data for
|
||||
that function, the function's structural hash value, the hash of the filenames
|
||||
in the function's translation unit, and the encoded mapping data.
|
||||
|
||||
Dissecting the sample:
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### Dissecting the sample:
|
||||
|
||||
Here's an overview of the encoded data that was stored in the
|
||||
IR for the `coverage mapping sample`_ that was shown earlier:
|
||||
IR for the {ref}`coverage mapping sample <coverage mapping sample>` that was shown earlier:
|
||||
|
||||
* The IR contains the following string constant that represents the encoded
|
||||
coverage mapping data for the sample translation unit:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
c"\01\15\1Dx\DA\13\D1\0F-N-*\D6/+\CE\D6/\C9-\D0O\CB\CF\D7K\06\00N+\07]"
|
||||
```llvm
|
||||
c"\01\15\1Dx\DA\13\D1\0F-N-*\D6/+\CE\D6/\C9-\D0O\CB\CF\D7K\06\00N+\07]"
|
||||
```
|
||||
|
||||
* The string contains values that are encoded in the LEB128 format, which is
|
||||
used throughout for storing integers. It also contains a compressed payload.
|
||||
|
|
@ -380,32 +368,23 @@ IR for the `coverage mapping sample`_ that was shown earlier:
|
|||
|
||||
* The coverage mapping from the first function record is encoded in this string:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
c"\01\00\00\01\01\01\0C\02\02"
|
||||
```llvm
|
||||
c"\01\00\00\01\01\01\0C\02\02"
|
||||
```
|
||||
|
||||
This string consists of the following bytes:
|
||||
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x01`` | The number of file ids used by this function. There is only one file id used by the mapping data in this function. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x00`` | An index into the filenames array which corresponds to the file "/Users/alex/test.c". |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x00`` | The number of counter expressions used by this function. This function doesn't use any expressions. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x01`` | The number of mapping regions that are stored in an array for the function's file id #0. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x01`` | The coverage mapping counter for the first region in this function. The value of 1 tells us that it's a coverage |
|
||||
| | mapping counter that is a reference to the profile instrumentation counter with an index of 0. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x01`` | The starting line of the first mapping region in this function. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x0C`` | The starting column of the first mapping region in this function. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x02`` | The ending line of the first mapping region in this function. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``0x02`` | The ending column of the first mapping region in this function. |
|
||||
+----------+-------------------------------------------------------------------------------------------------------------------------+
|
||||
| Byte | Meaning |
|
||||
| --- | --- |
|
||||
| `0x01` | The number of file ids used by this function. There is only one file id used by the mapping data in this function. |
|
||||
| `0x00` | An index into the filenames array which corresponds to the file "/Users/alex/test.c". |
|
||||
| `0x00` | The number of counter expressions used by this function. This function doesn't use any expressions. |
|
||||
| `0x01` | The number of mapping regions that are stored in an array for the function's file id #0. |
|
||||
| `0x01` | The coverage mapping counter for the first region in this function. The value of 1 tells us that it's a coverage mapping counter that is a reference to the profile instrumentation counter with an index of 0. |
|
||||
| `0x01` | The starting line of the first mapping region in this function. |
|
||||
| `0x0C` | The starting column of the first mapping region in this function. |
|
||||
| `0x02` | The ending line of the first mapping region in this function. |
|
||||
| `0x02` | The ending column of the first mapping region in this function. |
|
||||
|
||||
* The length of the substring that contains the encoded coverage mapping data
|
||||
for the second function record is also 9. It's structured like the mapping data
|
||||
|
|
@ -414,76 +393,69 @@ IR for the `coverage mapping sample`_ that was shown earlier:
|
|||
* The two trailing bytes are zeroes and are used to pad the coverage mapping
|
||||
data to give it the 8-byte alignment.
|
||||
|
||||
Encoding
|
||||
========
|
||||
## Encoding
|
||||
|
||||
The per-function coverage mapping data is encoded as a stream of bytes,
|
||||
with a simple structure. The structure consists of the encoding
|
||||
`types <cvmtypes_>`_ like variable-length unsigned integers, that
|
||||
are used to encode `File ID Mapping`_, `Counter Expressions`_ and
|
||||
the `Mapping Regions`_.
|
||||
{ref}`types <cvmtypes>` like variable-length unsigned integers, that
|
||||
are used to encode {ref}`File ID Mapping`, {ref}`Counter Expressions` and
|
||||
the {ref}`Mapping Regions`.
|
||||
|
||||
The format of the structure follows:
|
||||
|
||||
``[file id mapping, counter expressions, mapping regions]``
|
||||
`[file id mapping, counter expressions, mapping regions]`
|
||||
|
||||
The translation unit filenames are encoded using the same encoding
|
||||
`types <cvmtypes_>`_ as the per-function coverage mapping data, with the
|
||||
{ref}`types <cvmtypes>` as the per-function coverage mapping data, with the
|
||||
following structure:
|
||||
|
||||
``[numFilenames : LEB128, filename0 : string, filename1 : string, ...]``
|
||||
`[numFilenames : LEB128, filename0 : string, filename1 : string, ...]`
|
||||
|
||||
.. _cvmtypes:
|
||||
(cvmtypes)=
|
||||
|
||||
Types
|
||||
-----
|
||||
### Types
|
||||
|
||||
This section describes the basic types that are used by the encoding format
|
||||
and can appear after ``:`` in the ``[foo : type]`` description.
|
||||
and can appear after `:` in the `[foo : type]` description.
|
||||
|
||||
.. _LEB128:
|
||||
(LEB128)=
|
||||
|
||||
LEB128
|
||||
^^^^^^
|
||||
#### LEB128
|
||||
|
||||
LEB128 is an unsigned integer value that is encoded using DWARF's LEB128
|
||||
encoding, optimizing for the case where values are small
|
||||
(1 byte for values less than 128).
|
||||
|
||||
.. _CoverageStrings:
|
||||
(CoverageStrings)=
|
||||
|
||||
Strings
|
||||
^^^^^^^
|
||||
#### Strings
|
||||
|
||||
``[length : LEB128, characters...]``
|
||||
`[length : LEB128, characters...]`
|
||||
|
||||
String values are encoded with a `LEB value <LEB128_>`_ for the length
|
||||
String values are encoded with a {ref}`LEB value <LEB128>` for the length
|
||||
of the string and a sequence of bytes for its characters.
|
||||
|
||||
.. _file id mapping:
|
||||
(file id mapping)=
|
||||
|
||||
File ID Mapping
|
||||
---------------
|
||||
### File ID Mapping
|
||||
|
||||
``[numIndices : LEB128, filenameIndex0 : LEB128, filenameIndex1 : LEB128, ...]``
|
||||
`[numIndices : LEB128, filenameIndex0 : LEB128, filenameIndex1 : LEB128, ...]`
|
||||
|
||||
File id mapping in a function's coverage mapping stream
|
||||
contains the indices into the translation unit's filenames array.
|
||||
|
||||
Counter
|
||||
-------
|
||||
### Counter
|
||||
|
||||
``[value : LEB128]``
|
||||
`[value : LEB128]`
|
||||
|
||||
A `coverage mapping counter`_ is stored in a single `LEB value <LEB128_>`_.
|
||||
It is composed of two things --- the `tag <counter-tag_>`_
|
||||
which is stored in the lowest 2 bits, and the `counter data`_ which is stored
|
||||
A {ref}`coverage mapping counter` is stored in a single {ref}`LEB value <LEB128>`.
|
||||
It is composed of two things --- the {ref}`tag <counter-tag>`
|
||||
which is stored in the lowest 2 bits, and the {ref}`counter data` which is stored
|
||||
in the remaining bits.
|
||||
|
||||
.. _counter-tag:
|
||||
(counter-tag)=
|
||||
|
||||
Tag:
|
||||
^^^^
|
||||
#### Tag:
|
||||
|
||||
The counter's tag encodes the counter's kind
|
||||
and, if the counter is an expression, the expression's kind.
|
||||
|
|
@ -497,10 +469,9 @@ The possible tag values are:
|
|||
|
||||
* 3 - The counter is an addition expression.
|
||||
|
||||
.. _counter data:
|
||||
(counter data)=
|
||||
|
||||
Data:
|
||||
^^^^^
|
||||
#### Data:
|
||||
|
||||
The counter's data is interpreted in the following manner:
|
||||
|
||||
|
|
@ -509,24 +480,22 @@ The counter's data is interpreted in the following manner:
|
|||
* When the counter is an expression, then the counter's data
|
||||
is the index into the array of counter expressions.
|
||||
|
||||
.. _Counter Expressions:
|
||||
(Counter Expressions)=
|
||||
|
||||
Counter Expressions
|
||||
-------------------
|
||||
### Counter Expressions
|
||||
|
||||
``[numExpressions : LEB128, expr0LHS : LEB128, expr0RHS : LEB128, expr1LHS : LEB128, expr1RHS : LEB128, ...]``
|
||||
`[numExpressions : LEB128, expr0LHS : LEB128, expr0RHS : LEB128, expr1LHS : LEB128, expr1RHS : LEB128, ...]`
|
||||
|
||||
Counter expressions consist of two counters as they
|
||||
represent binary arithmetic operations.
|
||||
The expression's kind is determined from the `tag <counter-tag_>`_ of the
|
||||
The expression's kind is determined from the {ref}`tag <counter-tag>` of the
|
||||
counter that references this expression.
|
||||
|
||||
.. _Mapping Regions:
|
||||
(Mapping Regions)=
|
||||
|
||||
Mapping Regions
|
||||
---------------
|
||||
### Mapping Regions
|
||||
|
||||
``[numRegionArrays : LEB128, regionsForFile0, regionsForFile1, ...]``
|
||||
`[numRegionArrays : LEB128, regionsForFile0, regionsForFile1, ...]`
|
||||
|
||||
The mapping regions are stored in an array of sub-arrays where every
|
||||
region in a particular sub-array has the same file id.
|
||||
|
|
@ -535,34 +504,31 @@ The file id for a sub-array of regions is the index of that
|
|||
sub-array in the main array e.g. The first sub-array will have the file id
|
||||
of 0.
|
||||
|
||||
Sub-Array of Regions
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
#### Sub-Array of Regions
|
||||
|
||||
``[numRegions : LEB128, region0, region1, ...]``
|
||||
`[numRegions : LEB128, region0, region1, ...]`
|
||||
|
||||
The mapping regions for a specific file id are stored in an array that is
|
||||
sorted in an ascending order by the region's starting location.
|
||||
|
||||
Mapping Region
|
||||
^^^^^^^^^^^^^^
|
||||
#### Mapping Region
|
||||
|
||||
``[header, source range]``
|
||||
`[header, source range]`
|
||||
|
||||
The mapping region record contains two sub-records ---
|
||||
the `header`_, which stores the counter and/or the region's kind,
|
||||
and the `source range`_ that contains the starting and ending
|
||||
the {ref}`header`, which stores the counter and/or the region's kind,
|
||||
and the {ref}`source range` that contains the starting and ending
|
||||
location of this region.
|
||||
|
||||
.. _header:
|
||||
(header)=
|
||||
|
||||
Header
|
||||
^^^^^^
|
||||
#### Header
|
||||
|
||||
``[counter]``
|
||||
`[counter]`
|
||||
|
||||
or
|
||||
|
||||
``[pseudo-counter]``
|
||||
`[pseudo-counter]`
|
||||
|
||||
The header encodes the region's counter and the region's kind. A branch region
|
||||
will encode two counters.
|
||||
|
|
@ -571,18 +537,16 @@ The value of the counter's tag distinguishes between the counters and
|
|||
pseudo-counters --- if the tag is zero, than this header contains a
|
||||
pseudo-counter, otherwise this header contains an ordinary counter.
|
||||
|
||||
Counter:
|
||||
""""""""
|
||||
##### Counter:
|
||||
|
||||
A mapping region whose header has a counter with a non-zero tag is
|
||||
a code region.
|
||||
|
||||
Pseudo-Counter:
|
||||
"""""""""""""""
|
||||
##### Pseudo-Counter:
|
||||
|
||||
``[value : LEB128]``
|
||||
`[value : LEB128]`
|
||||
|
||||
A pseudo-counter is stored in a single `LEB value <LEB128_>`_, just like
|
||||
A pseudo-counter is stored in a single {ref}`LEB value <LEB128>`, just like
|
||||
the ordinary counter. It has the following interpretation:
|
||||
|
||||
* bits 0-1: tag, which is always 0.
|
||||
|
|
@ -600,12 +564,11 @@ the ordinary counter. It has the following interpretation:
|
|||
* 2 - This mapping region is a skipped region.
|
||||
* 4 - This mapping region is a branch region.
|
||||
|
||||
.. _source range:
|
||||
(source range)=
|
||||
|
||||
Source Range
|
||||
^^^^^^^^^^^^
|
||||
#### Source Range
|
||||
|
||||
``[deltaLineStart : LEB128, columnStart : LEB128, numLines : LEB128, columnEnd : LEB128]``
|
||||
`[deltaLineStart : LEB128, columnStart : LEB128, numLines : LEB128, columnEnd : LEB128]`
|
||||
|
||||
The source range record contains the following fields:
|
||||
|
||||
|
|
@ -624,69 +587,65 @@ The source range record contains the following fields:
|
|||
the current mapping region is a gap area. A count for a gap area is only used
|
||||
as the line execution count if there are no other regions on a line.
|
||||
|
||||
Testing Format
|
||||
==============
|
||||
## Testing Format
|
||||
|
||||
.. warning::
|
||||
This section is for the LLVM developers who are working on ``llvm-cov`` only.
|
||||
```{warning}
|
||||
This section is for the LLVM developers who are working on `llvm-cov` only.
|
||||
```
|
||||
|
||||
``llvm-cov`` uses a special file format (called ``.covmapping`` below) for
|
||||
`llvm-cov` uses a special file format (called `.covmapping` below) for
|
||||
testing purposes. This format is private and should have no use for general
|
||||
users. As a developer, you can get such files by the ``convert-for-testing``
|
||||
subcommand of ``llvm-cov``.
|
||||
users. As a developer, you can get such files by the `convert-for-testing`
|
||||
subcommand of `llvm-cov`.
|
||||
|
||||
The structure of the ``.covmapping`` files follows:
|
||||
The structure of the `.covmapping` files follows:
|
||||
|
||||
``[magicNumber : u64, version : u64, profileNames, coverageMapping, coverageRecords]``
|
||||
`[magicNumber : u64, version : u64, profileNames, coverageMapping, coverageRecords]`
|
||||
|
||||
Magic Number and Version
|
||||
------------------------
|
||||
### Magic Number and Version
|
||||
|
||||
The magic is ``0x6d766f636d766c6c``, which is the ASCII string
|
||||
``llvmcovm`` in little-endian.
|
||||
The magic is `0x6d766f636d766c6c`, which is the ASCII string
|
||||
`llvmcovm` in little-endian.
|
||||
|
||||
There are two versions for now:
|
||||
|
||||
- Version1, encoded as ``0x6174616474736574`` (ASCII string ``testdata``).
|
||||
- Version1, encoded as `0x6174616474736574` (ASCII string `testdata`).
|
||||
- Version2, encoded as 1.
|
||||
|
||||
The only difference between Version1 and Version2 is in the encoding of the
|
||||
``coverageMapping`` fields, which is explained later.
|
||||
`coverageMapping` fields, which is explained later.
|
||||
|
||||
Profile Names
|
||||
-------------
|
||||
### Profile Names
|
||||
|
||||
``profileNames``, ``coverageMapping`` and ``coverageRecords`` are 3 sections
|
||||
`profileNames`, `coverageMapping` and `coverageRecords` are 3 sections
|
||||
extracted from the original binary file.
|
||||
|
||||
``profileNames`` encodes the size, address and the raw data of the section:
|
||||
`profileNames` encodes the size, address and the raw data of the section:
|
||||
|
||||
``[profileNamesSize : LEB128, profileNamesAddr : LEB128, profileNamesData : bytes]``
|
||||
`[profileNamesSize : LEB128, profileNamesAddr : LEB128, profileNamesData : bytes]`
|
||||
|
||||
Coverage Mapping
|
||||
----------------
|
||||
### Coverage Mapping
|
||||
|
||||
This field is padded with zero bytes to make it 8-byte aligned.
|
||||
|
||||
``coverageMapping`` contains the records of the source files. In version 1,
|
||||
`coverageMapping` contains the records of the source files. In version 1,
|
||||
only one record is stored:
|
||||
|
||||
``[padding : bytes, coverageMappingData : bytes]``
|
||||
`[padding : bytes, coverageMappingData : bytes]`
|
||||
|
||||
Version 2 relaxes this restriction by encoding the size of
|
||||
``coverageMappingData`` as a LEB128 number before the data:
|
||||
`coverageMappingData` as a LEB128 number before the data:
|
||||
|
||||
``[coverageMappingSize : LEB128, padding : bytes, coverageMappingData : bytes]``
|
||||
`[coverageMappingSize : LEB128, padding : bytes, coverageMappingData : bytes]`
|
||||
|
||||
The current version is 2.
|
||||
|
||||
Coverage Records
|
||||
----------------
|
||||
### Coverage Records
|
||||
|
||||
This field is padded with zero bytes to make it 8-byte aligned.
|
||||
|
||||
``coverageRecords`` is encoded as:
|
||||
`coverageRecords` is encoded as:
|
||||
|
||||
``[padding : bytes, coverageRecordsData : bytes]``
|
||||
`[padding : bytes, coverageRecordsData : bytes]`
|
||||
|
||||
The rest data in the file is considered as the ``coverageRecordsData``.
|
||||
The rest data in the file is considered as the `coverageRecordsData`.
|
||||
|
|
|
|||
|
|
@ -1,125 +1,123 @@
|
|||
==============
|
||||
Debugging LLVM
|
||||
==============
|
||||
# Debugging LLVM
|
||||
|
||||
This document is a collection of tips and tricks for debugging LLVM
|
||||
using a source-level debugger. The assumption is that you are trying to
|
||||
figure out the root cause of a miscompilation in the program that you
|
||||
are compiling.
|
||||
|
||||
Extract and rerun the compile command
|
||||
=====================================
|
||||
## Extract and rerun the compile command
|
||||
|
||||
Extract the Clang command that produces the buggy code. The way to do
|
||||
this depends on the build system used by your program.
|
||||
|
||||
- For Ninja-based build systems, you can pass ``-t commands`` to Ninja
|
||||
- For Ninja-based build systems, you can pass `-t commands` to Ninja
|
||||
and filter the output by the targeted source file name. For example:
|
||||
``ninja -t commands myprogram | grep path/to/file.cpp``.
|
||||
`ninja -t commands myprogram | grep path/to/file.cpp`.
|
||||
|
||||
- For Bazel-based build systems using Bazel 9 or newer, you can pass
|
||||
``--output=commands`` to the ``bazel aquery`` subcommand for
|
||||
a similar result. For example: ``bazel aquery --output=commands
|
||||
'deps(//myprogram)' | grep path/to/file.cpp``. Build commands must
|
||||
`--output=commands` to the `bazel aquery` subcommand for
|
||||
a similar result. For example: `bazel aquery --output=commands
|
||||
'deps(//myprogram)' | grep path/to/file.cpp`. Build commands must
|
||||
generally be run from a subdirectory of the source directory named
|
||||
``bazel-$PROJECTNAME``. Bazel typically makes the target paths of
|
||||
``-o`` and ``-MF`` read-only when running commands outside of a build,
|
||||
`bazel-$PROJECTNAME`. Bazel typically makes the target paths of
|
||||
`-o` and `-MF` read-only when running commands outside of a build,
|
||||
so it may be necessary to change or remove these flags.
|
||||
|
||||
- A method that should work with any build system is to build your program
|
||||
under `Bear <https://github.com/rizsotto/Bear>`_ and look for the
|
||||
compile command in the resulting ``compile_commands.json`` file.
|
||||
under [Bear] and look for the
|
||||
compile command in the resulting `compile_commands.json` file.
|
||||
|
||||
Once you have the command you can use the following steps to debug
|
||||
it. Note that any flags mentioned later in this document are LLVM flags
|
||||
so they must be prefixed with ``-mllvm`` when passed to the Clang driver,
|
||||
e.g. ``-mllvm -print-after-all``.
|
||||
so they must be prefixed with `-mllvm` when passed to the Clang driver,
|
||||
e.g. `-mllvm -print-after-all`.
|
||||
|
||||
Understanding the source of the issue
|
||||
=====================================
|
||||
## Understanding the source of the issue
|
||||
|
||||
If you have a miscompilation introduced by a pass, it is
|
||||
frequently possible to identify the pass where things go wrong
|
||||
by searching a pass-by-pass printout, which is enabled using the
|
||||
``-print-after-all`` flag. Pipe stderr into ``less`` (append ``2>&1 |
|
||||
less`` to command line) and use text search to move between passes
|
||||
(e.g. type ``/Dump After<Enter>``, ``n`` to move to next pass,
|
||||
``N`` to move to previous pass). If the name of the function
|
||||
`-print-after-all` flag. Pipe stderr into `less` (append `2>&1 |
|
||||
less` to command line) and use text search to move between passes
|
||||
(e.g. type `/Dump After<Enter>`, `n` to move to next pass,
|
||||
`N` to move to previous pass). If the name of the function
|
||||
containing the buggy IR is known, you can filter the output by passing
|
||||
``-filter-print-funcs=functionname``. You can sometimes pass ``-debug`` to
|
||||
get useful details about what passes are doing. See also `PrintPasses.cpp
|
||||
<https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/PrintPasses.cpp>`_
|
||||
`-filter-print-funcs=functionname`. You can sometimes pass `-debug` to
|
||||
get useful details about what passes are doing. See also [PrintPasses.cpp]
|
||||
for more useful options.
|
||||
|
||||
Creating a debug build of LLVM
|
||||
==============================
|
||||
## Creating a debug build of LLVM
|
||||
|
||||
The subsequent debugging steps require a debug build of LLVM. Pass the
|
||||
``-DCMAKE_BUILD_TYPE=Debug`` to CMake in a separate build tree to create
|
||||
`-DCMAKE_BUILD_TYPE=Debug` to CMake in a separate build tree to create
|
||||
a debug build.
|
||||
|
||||
Understanding where an instruction came from
|
||||
============================================
|
||||
## Understanding where an instruction came from
|
||||
|
||||
A common debugging task involves understanding which part of the code
|
||||
introduced a buggy instruction. The pass-by-pass dump is sometimes enough,
|
||||
but for complex or unfamiliar passes, more information is often required.
|
||||
|
||||
The first step is to record a run of the debug build of Clang under `rr
|
||||
<https://rr-project.org>`_ passing the LLVM flag ``-print-inst-addrs``
|
||||
together with ``-print-after-all`` and any desired filters. This will
|
||||
The first step is to record a run of the debug build of Clang under [rr]
|
||||
passing the LLVM flag `-print-inst-addrs`
|
||||
together with `-print-after-all` and any desired filters. This will
|
||||
cause each instruction printed by LLVM to be suffixed with a comment
|
||||
showing the address of the ``Instruction`` object. You can then replay
|
||||
the run of Clang with ``rr replay``. Because ``rr`` is deterministic,
|
||||
showing the address of the `Instruction` object. You can then replay
|
||||
the run of Clang with `rr replay`. Because `rr` is deterministic,
|
||||
the instruction will receive the same address during the replay, so
|
||||
you can break on the instruction's construction using a conditional
|
||||
breakpoint that checks for the address printed by LLVM, with commands
|
||||
such as the following:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
b Instruction::Instruction if this == 0x12345678
|
||||
```text
|
||||
b Instruction::Instruction if this == 0x12345678
|
||||
```
|
||||
|
||||
When the breakpoint is hit, you will likely be at the location where
|
||||
the instruction was created, so you can unwind the stack with ``bt``
|
||||
the instruction was created, so you can unwind the stack with `bt`
|
||||
to see the stack trace. It is also possible that an instruction was
|
||||
created multiple times at the same address, so you may need to continue
|
||||
until reaching the desired location, but in the author's experience this
|
||||
is unlikely to occur.
|
||||
|
||||
Similar flags exist for the backend: ``-print-sdnode-addrs`` for
|
||||
printing ``SDNode`` addresses, and ``-print-mi-addrs`` for printing
|
||||
``MachineInstr`` addresses.
|
||||
Similar flags exist for the backend: `-print-sdnode-addrs` for
|
||||
printing `SDNode` addresses, and `-print-mi-addrs` for printing
|
||||
`MachineInstr` addresses.
|
||||
|
||||
Identifying the source locations of instructions
|
||||
================================================
|
||||
## Identifying the source locations of instructions
|
||||
|
||||
To identify the source location that caused a particular instruction
|
||||
to be created, you can pass the LLVM flag ``-print-inst-debug-locs``
|
||||
to be created, you can pass the LLVM flag `-print-inst-debug-locs`
|
||||
and each instruction printed by LLVM is suffixed with the file and line
|
||||
number of the instruction according to the debug information. Note that
|
||||
this requires debug information to be enabled (e.g. pass ``-g`` to Clang).
|
||||
this requires debug information to be enabled (e.g. pass `-g` to Clang).
|
||||
|
||||
LLDB Data Formatters
|
||||
====================
|
||||
## LLDB Data Formatters
|
||||
|
||||
A handful of `LLDB data formatters
|
||||
<https://lldb.llvm.org/resources/dataformatters.html>`__ are
|
||||
A handful of [LLDB data formatters] are
|
||||
provided for some of the core LLVM libraries. To use them, execute the
|
||||
following (or add it to your ``~/.lldbinit``)::
|
||||
following (or add it to your `~/.lldbinit`):
|
||||
|
||||
command script import /path/to/llvm/utils/lldbDataFormatters.py
|
||||
```text
|
||||
command script import /path/to/llvm/utils/lldbDataFormatters.py
|
||||
```
|
||||
|
||||
GDB pretty printers
|
||||
===================
|
||||
## GDB pretty printers
|
||||
|
||||
A handful of `GDB pretty printers
|
||||
<https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html>`__ are
|
||||
A handful of [GDB pretty printers] are
|
||||
provided for some of the core LLVM libraries. To use them, execute the
|
||||
following (or add it to your ``~/.gdbinit``)::
|
||||
following (or add it to your `~/.gdbinit`):
|
||||
|
||||
source /path/to/llvm/utils/gdb-scripts/prettyprinters.py
|
||||
```text
|
||||
source /path/to/llvm/utils/gdb-scripts/prettyprinters.py
|
||||
```
|
||||
|
||||
It also might be handy to enable the `print pretty
|
||||
<https://sourceware.org/gdb/current/onlinedocs/gdb.html/Print-Settings.html>`__
|
||||
It also might be handy to enable the [print pretty]
|
||||
option to avoid data structures being printed as a big block of text.
|
||||
|
||||
[Bear]: https://github.com/rizsotto/Bear
|
||||
[PrintPasses.cpp]: https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/PrintPasses.cpp
|
||||
[rr]: https://rr-project.org
|
||||
[LLDB data formatters]: https://lldb.llvm.org/resources/dataformatters.html
|
||||
[GDB pretty printers]: https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html
|
||||
[print pretty]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Print-Settings.html
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
346
llvm/docs/FAQ.md
346
llvm/docs/FAQ.md
|
|
@ -1,45 +1,44 @@
|
|||
================================
|
||||
Frequently Asked Questions (FAQ)
|
||||
================================
|
||||
# Frequently Asked Questions (FAQ)
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
|
||||
License
|
||||
=======
|
||||
## License
|
||||
|
||||
### Can I modify LLVM source code and redistribute the modified source?
|
||||
|
||||
Can I modify LLVM source code and redistribute the modified source?
|
||||
-------------------------------------------------------------------
|
||||
Yes. The modified source distribution must retain the copyright notice and
|
||||
follow the conditions listed in the `Apache License v2.0 with LLVM Exceptions
|
||||
<https://github.com/llvm/llvm-project/blob/main/llvm/LICENSE.TXT>`_.
|
||||
follow the conditions listed in the [Apache License v2.0 with LLVM
|
||||
Exceptions][llvm-license].
|
||||
|
||||
[llvm-license]: https://github.com/llvm/llvm-project/blob/main/llvm/LICENSE.TXT
|
||||
|
||||
|
||||
Can I modify the LLVM source code and redistribute binaries or other tools based on it, without redistributing the source?
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
### Can I modify the LLVM source code and redistribute binaries or other tools based on it, without redistributing the source?
|
||||
|
||||
Yes. This is why we distribute LLVM under a less restrictive license than GPL,
|
||||
as explained in the first question above.
|
||||
|
||||
|
||||
Can I use AI coding tools, such as GitHub co-pilot, to write LLVM patches?
|
||||
--------------------------------------------------------------------------
|
||||
### Can I use AI coding tools, such as GitHub co-pilot, to write LLVM patches?
|
||||
|
||||
Yes, as long as the resulting work can be licensed under the project license, as
|
||||
covered in the :doc:`DeveloperPolicy`. Using an AI tool to reproduce copyrighted
|
||||
covered in the {doc}`DeveloperPolicy`. Using an AI tool to reproduce copyrighted
|
||||
work does not rinse it of copyright and grant you the right to relicense it.
|
||||
|
||||
|
||||
Source Code
|
||||
===========
|
||||
## Source Code
|
||||
|
||||
### In what language is LLVM written?
|
||||
|
||||
In what language is LLVM written?
|
||||
---------------------------------
|
||||
All of the LLVM tools and libraries are written in C++ with extensive use of
|
||||
the STL.
|
||||
|
||||
|
||||
How portable is the LLVM source code?
|
||||
-------------------------------------
|
||||
### How portable is the LLVM source code?
|
||||
|
||||
The LLVM source code should be portable to most modern Unix-like operating
|
||||
systems. LLVM also has excellent support on Windows systems.
|
||||
Most of the code is written in standard C++ with operating system
|
||||
|
|
@ -47,45 +46,42 @@ services abstracted to a support library. The tools required to build and
|
|||
test LLVM have been ported to a plethora of platforms.
|
||||
|
||||
|
||||
What API do I use to store a value to one of the virtual registers in LLVM IR's SSA representation?
|
||||
---------------------------------------------------------------------------------------------------
|
||||
### What API do I use to store a value to one of the virtual registers in LLVM IR's SSA representation?
|
||||
|
||||
In short: you can't. It's actually kind of a silly question once you understand
|
||||
what's going on. Basically, in code like:
|
||||
|
||||
.. code-block:: llvm
|
||||
```llvm
|
||||
%result = add i32 %foo, %bar
|
||||
```
|
||||
|
||||
%result = add i32 %foo, %bar
|
||||
|
||||
, ``%result`` is just a name given to the ``Value`` of the ``add``
|
||||
instruction. In other words, ``%result`` *is* the add instruction. The
|
||||
, `%result` is just a name given to the `Value` of the `add`
|
||||
instruction. In other words, `%result` *is* the add instruction. The
|
||||
"assignment" doesn't explicitly "store" anything to any "virtual register";
|
||||
the "``=``" is more like the mathematical sense of equality.
|
||||
the "`=`" is more like the mathematical sense of equality.
|
||||
|
||||
Longer explanation: In order to generate a textual representation of the
|
||||
IR, some kind of name has to be given to each instruction so that other
|
||||
instructions can textually reference it. However, the isomorphic in-memory
|
||||
representation that you manipulate from C++ has no such restriction since
|
||||
instructions can simply keep pointers to any other ``Value``'s that they
|
||||
reference. In fact, the names of dummy numbered temporaries like ``%1`` are
|
||||
instructions can simply keep pointers to any other `Value`'s that they
|
||||
reference. In fact, the names of dummy numbered temporaries like `%1` are
|
||||
not explicitly represented in the in-memory representation at all (see
|
||||
``Value::getName()``).
|
||||
`Value::getName()`).
|
||||
|
||||
|
||||
Source Languages
|
||||
================
|
||||
## Source Languages
|
||||
|
||||
What source languages are supported?
|
||||
------------------------------------
|
||||
### What source languages are supported?
|
||||
|
||||
LLVM currently has full support for C and C++ source languages through
|
||||
`Clang <https://clang.llvm.org/>`_. Many other language frontends have
|
||||
[Clang](https://clang.llvm.org/). Many other language frontends have
|
||||
been written using LLVM; an incomplete list is available at
|
||||
`projects with LLVM <https://llvm.org/ProjectsWithLLVM/>`_.
|
||||
[projects with LLVM](https://llvm.org/ProjectsWithLLVM/).
|
||||
|
||||
|
||||
I'd like to write a self-hosting LLVM compiler. How should I interface with the LLVM middle-end optimizers and back-end code generators?
|
||||
----------------------------------------------------------------------------------------------------------------------------------------
|
||||
### I'd like to write a self-hosting LLVM compiler. How should I interface with the LLVM middle-end optimizers and back-end code generators?
|
||||
|
||||
Your compiler front-end will communicate with LLVM by creating a module in the
|
||||
LLVM intermediate representation (IR) format. Assuming you want to write your
|
||||
language's compiler in the language itself (rather than C++), there are 3
|
||||
|
|
@ -94,33 +90,33 @@ major ways to tackle generating LLVM IR from a front-end:
|
|||
1. **Call into the LLVM libraries using your language's FFI (foreign
|
||||
function interface).**
|
||||
|
||||
* *for:* best tracks changes to the LLVM IR, .ll syntax, and .bc format
|
||||
* *for:* best tracks changes to the LLVM IR, .ll syntax, and .bc format
|
||||
|
||||
* *for:* enables running LLVM optimization passes without an emit/parse
|
||||
overhead
|
||||
* *for:* enables running LLVM optimization passes without an emit/parse
|
||||
overhead
|
||||
|
||||
* *for:* adapts well to a JIT context
|
||||
* *for:* adapts well to a JIT context
|
||||
|
||||
* *against:* lots of ugly glue code to write
|
||||
* *against:* lots of ugly glue code to write
|
||||
|
||||
2. **Emit LLVM assembly from your compiler's native language.**
|
||||
|
||||
* *for:* very straightforward to get started
|
||||
* *for:* very straightforward to get started
|
||||
|
||||
* *against:* the .ll parser is slower than the bitcode reader when
|
||||
interfacing to the middle end
|
||||
* *against:* the .ll parser is slower than the bitcode reader when
|
||||
interfacing to the middle end
|
||||
|
||||
* *against:* it may be harder to track changes to the IR
|
||||
* *against:* it may be harder to track changes to the IR
|
||||
|
||||
3. **Emit LLVM bitcode from your compiler's native language.**
|
||||
|
||||
* *for:* can use the more-efficient bitcode reader when interfacing to the
|
||||
middle end
|
||||
* *for:* can use the more-efficient bitcode reader when interfacing to the
|
||||
middle end
|
||||
|
||||
* *against:* you'll have to re-engineer the LLVM IR object model and bitcode
|
||||
writer in your language
|
||||
* *against:* you'll have to re-engineer the LLVM IR object model and bitcode
|
||||
writer in your language
|
||||
|
||||
* *against:* it may be harder to track changes to the IR
|
||||
* *against:* it may be harder to track changes to the IR
|
||||
|
||||
If you go with the first option, the C bindings in include/llvm-c should help
|
||||
a lot, since most languages have strong support for interfacing with C. The
|
||||
|
|
@ -128,24 +124,23 @@ most common hurdle with calling C from managed code is interfacing with the
|
|||
garbage collector. The C interface was designed to require very little memory
|
||||
management, and so is straightforward in this regard.
|
||||
|
||||
What support is there for a higher-level source language constructs for building a compiler?
|
||||
--------------------------------------------------------------------------------------------
|
||||
### What support is there for a higher-level source language constructs for building a compiler?
|
||||
|
||||
Currently, there isn't much. LLVM supports an intermediate representation
|
||||
which is useful for code representation but will not support the high-level
|
||||
(abstract syntax tree) representation needed by most compilers. There are no
|
||||
facilities for lexical nor semantic analysis.
|
||||
|
||||
|
||||
I don't understand the ``GetElementPtr`` instruction. Help!
|
||||
-----------------------------------------------------------
|
||||
See `The Often Misunderstood GEP Instruction <GetElementPtr.html>`_.
|
||||
### I don't understand the `GetElementPtr` instruction. Help!
|
||||
|
||||
See {doc}`The Often Misunderstood GEP Instruction <GetElementPtr>`.
|
||||
|
||||
|
||||
Using the C and C++ Front Ends
|
||||
==============================
|
||||
## Using the C and C++ Front Ends
|
||||
|
||||
### Can I compile C or C++ code to platform-independent LLVM bitcode?
|
||||
|
||||
Can I compile C or C++ code to platform-independent LLVM bitcode?
|
||||
-----------------------------------------------------------------
|
||||
No. C and C++ are inherently platform-dependent languages. The most obvious
|
||||
example of this is the preprocessor. A very common way that C code is made
|
||||
portable is by using the preprocessor to include platform-specific code. In
|
||||
|
|
@ -153,8 +148,8 @@ practice, information about other platforms is lost after preprocessing, so
|
|||
the result is inherently dependent on the platform that the preprocessing was
|
||||
targeting.
|
||||
|
||||
Another example is ``sizeof``. It's common for ``sizeof(long)`` to vary
|
||||
between platforms. In most C front-ends, ``sizeof`` is expanded to a
|
||||
Another example is `sizeof`. It's common for `sizeof(long)` to vary
|
||||
between platforms. In most C front-ends, `sizeof` is expanded to a
|
||||
constant immediately, thus hard-wiring a platform-specific detail.
|
||||
|
||||
Also, since many platforms define their ABIs in terms of C, and since LLVM is
|
||||
|
|
@ -162,32 +157,31 @@ lower-level than C, front-ends currently must emit platform-specific IR in
|
|||
order to have the result conform to the platform ABI.
|
||||
|
||||
|
||||
Questions about code generated by the demo page
|
||||
===============================================
|
||||
## Questions about code generated by the demo page
|
||||
|
||||
What is this ``llvm.global_ctors`` and ``_GLOBAL__I_a...`` stuff that happens when I ``#include <iostream>``?
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
If you ``#include`` the ``<iostream>`` header into a C++ translation unit,
|
||||
the file will probably use the ``std::cin``/``std::cout``/... global objects.
|
||||
### What is this `llvm.global_ctors` and `_GLOBAL__I_a...` stuff that happens when I `#include <iostream>`?
|
||||
|
||||
If you `#include` the `<iostream>` header into a C++ translation unit,
|
||||
the file will probably use the `std::cin`/`std::cout`/... global objects.
|
||||
However, C++ does not guarantee an order of initialization between static
|
||||
objects in different translation units, so if a static ctor/dtor in your .cpp
|
||||
file used ``std::cout``, for example, the object would not necessarily be
|
||||
file used `std::cout`, for example, the object would not necessarily be
|
||||
automatically initialized before your use.
|
||||
|
||||
To make ``std::cout`` and friends work correctly in these scenarios, the STL
|
||||
To make `std::cout` and friends work correctly in these scenarios, the STL
|
||||
that we use declares a static object that gets created in every translation
|
||||
unit that includes ``<iostream>``. This object has a static constructor
|
||||
unit that includes `<iostream>`. This object has a static constructor
|
||||
and destructor that initializes and destroys the global iostream objects
|
||||
before they could possibly be used in the file. The code that you see in the
|
||||
``.ll`` file corresponds to the constructor and destructor registration code.
|
||||
`.ll` file corresponds to the constructor and destructor registration code.
|
||||
|
||||
If you would like to make it easier to *understand* the LLVM code generated
|
||||
by the compiler in the demo page, consider using ``printf()`` instead of
|
||||
``iostream``\s to print values.
|
||||
by the compiler in the demo page, consider using `printf()` instead of
|
||||
`iostream`s to print values.
|
||||
|
||||
|
||||
Where did all of my code go??
|
||||
-----------------------------
|
||||
### Where did all of my code go??
|
||||
|
||||
If you are using the LLVM demo page, you may often wonder what happened to
|
||||
all of the code that you typed in. Remember that the demo script is running
|
||||
the code through the LLVM optimizers, so if your code doesn't actually do
|
||||
|
|
@ -196,52 +190,52 @@ anything useful, it might all be deleted.
|
|||
To prevent this, make sure that the code is actually needed. For example, if
|
||||
you are computing some expression, return the value from the function instead
|
||||
of leaving it in a local variable. If you really want to constrain the
|
||||
optimizer, you can read from and assign to ``volatile`` global variables.
|
||||
optimizer, you can read from and assign to `volatile` global variables.
|
||||
|
||||
|
||||
What is this "``undef``" thing that shows up in my code?
|
||||
--------------------------------------------------------
|
||||
``undef`` is the LLVM way of representing a value that is not defined. You
|
||||
### What is this "`undef`" thing that shows up in my code?
|
||||
|
||||
`undef` is the LLVM way of representing a value that is not defined. You
|
||||
can get these if you do not initialize a variable before you use it. For
|
||||
example, the C function:
|
||||
|
||||
.. code-block:: c
|
||||
```c
|
||||
int X() { int i; return i; }
|
||||
```
|
||||
|
||||
int X() { int i; return i; }
|
||||
|
||||
Is compiled to "``ret i32 undef``" because "``i``" never has a value specified
|
||||
Is compiled to "`ret i32 undef`" because "`i`" never has a value specified
|
||||
for it.
|
||||
|
||||
|
||||
Why does instcombine + simplifycfg turn a call to a function with a mismatched calling convention into "unreachable"? Why not make the verifier reject it?
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
### Why does instcombine + simplifycfg turn a call to a function with a mismatched calling convention into "unreachable"? Why not make the verifier reject it?
|
||||
|
||||
This is a common problem encountered by authors of front-ends that are using
|
||||
custom calling conventions: you need to make sure to set the right calling
|
||||
convention on both the function and on each call to the function. For
|
||||
example, this code:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @bar() {
|
||||
call void @foo()
|
||||
ret void
|
||||
}
|
||||
```llvm
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @bar() {
|
||||
call void @foo()
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
Is optimized to:
|
||||
|
||||
.. code-block:: llvm
|
||||
```llvm
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @bar() {
|
||||
unreachable
|
||||
}
|
||||
```
|
||||
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @bar() {
|
||||
unreachable
|
||||
}
|
||||
|
||||
... with "``opt -instcombine -simplifycfg``". This often bites people because
|
||||
... with "`opt -instcombine -simplifycfg`". This often bites people because
|
||||
"all their code disappears". Setting the calling convention on the caller and
|
||||
callee is required for indirect calls to work, so people often ask why not
|
||||
make the verifier reject this sort of thing.
|
||||
|
|
@ -253,89 +247,89 @@ create this sort of construct (in dead code). The sorts of things that can
|
|||
cause this to happen are fairly contrived, but we still need to accept them.
|
||||
Here's an example:
|
||||
|
||||
.. code-block:: llvm
|
||||
```llvm
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define internal void @bar(void()* %FP, i1 %cond) {
|
||||
br i1 %cond, label %T, label %F
|
||||
T:
|
||||
call void %FP()
|
||||
ret void
|
||||
F:
|
||||
call fastcc void %FP()
|
||||
ret void
|
||||
}
|
||||
define void @test() {
|
||||
%X = or i1 false, false
|
||||
call void @bar(void()* @foo, i1 %X)
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define internal void @bar(void()* %FP, i1 %cond) {
|
||||
br i1 %cond, label %T, label %F
|
||||
T:
|
||||
call void %FP()
|
||||
ret void
|
||||
F:
|
||||
call fastcc void %FP()
|
||||
ret void
|
||||
}
|
||||
define void @test() {
|
||||
%X = or i1 false, false
|
||||
call void @bar(void()* @foo, i1 %X)
|
||||
ret void
|
||||
}
|
||||
|
||||
In this example, "test" always passes ``@foo``/``false`` into ``bar``, which
|
||||
In this example, "test" always passes `@foo`/`false` into `bar`, which
|
||||
ensures that it is dynamically called with the right calling convention (thus, the
|
||||
code is perfectly well defined). If you run this through the inliner, you
|
||||
get this (the explicit "or" is there so that the inliner doesn't dead code
|
||||
eliminate a bunch of stuff):
|
||||
|
||||
.. code-block:: llvm
|
||||
```llvm
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @test() {
|
||||
%X = or i1 false, false
|
||||
br i1 %X, label %T.i, label %F.i
|
||||
T.i:
|
||||
call void @foo()
|
||||
br label %bar.exit
|
||||
F.i:
|
||||
call fastcc void @foo()
|
||||
br label %bar.exit
|
||||
bar.exit:
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @test() {
|
||||
%X = or i1 false, false
|
||||
br i1 %X, label %T.i, label %F.i
|
||||
T.i:
|
||||
call void @foo()
|
||||
br label %bar.exit
|
||||
F.i:
|
||||
call fastcc void @foo()
|
||||
br label %bar.exit
|
||||
bar.exit:
|
||||
ret void
|
||||
}
|
||||
|
||||
Here you can see that the inlining pass made an undefined call to ``@foo``
|
||||
Here you can see that the inlining pass made an undefined call to `@foo`
|
||||
with the wrong calling convention. We really don't want to make the inliner
|
||||
have to know about this sort of thing, so it needs to be valid code. In this
|
||||
case, dead code elimination can trivially remove the undefined code. However,
|
||||
if ``%X`` was an input argument to ``@test``, the inliner would produce this:
|
||||
if `%X` was an input argument to `@test`, the inliner would produce this:
|
||||
|
||||
.. code-block:: llvm
|
||||
```llvm
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @test(i1 %X) {
|
||||
br i1 %X, label %T.i, label %F.i
|
||||
T.i:
|
||||
call void @foo()
|
||||
br label %bar.exit
|
||||
F.i:
|
||||
call fastcc void @foo()
|
||||
br label %bar.exit
|
||||
bar.exit:
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
define void @test(i1 %X) {
|
||||
br i1 %X, label %T.i, label %F.i
|
||||
T.i:
|
||||
call void @foo()
|
||||
br label %bar.exit
|
||||
F.i:
|
||||
call fastcc void @foo()
|
||||
br label %bar.exit
|
||||
bar.exit:
|
||||
ret void
|
||||
}
|
||||
|
||||
The interesting thing about this is that ``%X`` *must* be false for the
|
||||
The interesting thing about this is that `%X` *must* be false for the
|
||||
code to be well-defined, but no amount of dead code elimination will be able
|
||||
to delete the broken call as unreachable. However, since
|
||||
``instcombine``/``simplifycfg`` turns the undefined call into unreachable, we
|
||||
`instcombine`/`simplifycfg` turns the undefined call into unreachable, we
|
||||
end up with a branch on a condition that goes to unreachable: a branch to
|
||||
unreachable can never happen, so "``-inline -instcombine -simplifycfg``" is
|
||||
unreachable can never happen, so "`-inline -instcombine -simplifycfg`" is
|
||||
able to produce:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @test(i1 %X) {
|
||||
F.i:
|
||||
call fastcc void @foo()
|
||||
ret void
|
||||
}
|
||||
```llvm
|
||||
define fastcc void @foo() {
|
||||
ret void
|
||||
}
|
||||
define void @test(i1 %X) {
|
||||
F.i:
|
||||
call fastcc void @foo()
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
=====================================
|
||||
Garbage Collection with LLVM
|
||||
=====================================
|
||||
# Garbage Collection with LLVM
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
Abstract
|
||||
========
|
||||
## Abstract
|
||||
|
||||
This document covers how to integrate LLVM into a compiler for a language which
|
||||
supports garbage collection. **Note that LLVM itself does not provide a
|
||||
garbage collector.** You must provide your own.
|
||||
|
||||
Quick Start
|
||||
============
|
||||
## Quick Start
|
||||
|
||||
First, you should pick a collector strategy. LLVM includes a number of built-in
|
||||
ones, but you can also implement a loadable plugin with a custom definition.
|
||||
|
|
@ -24,21 +21,21 @@ of the collector itself.
|
|||
Next, mark your generated functions as using your chosen collector strategy.
|
||||
From C++, you can call:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
```c++
|
||||
F.setGC(<collector description name>);
|
||||
```
|
||||
|
||||
|
||||
This will produce IR like the following fragment:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
define void @foo() gc "<collector description name>" { ... }
|
||||
```
|
||||
|
||||
|
||||
When generating LLVM IR for your functions, you will need to:
|
||||
|
||||
* Use ``@llvm.gcread`` and/or ``@llvm.gcwrite`` in place of standard load and
|
||||
* Use `@llvm.gcread` and/or `@llvm.gcwrite` in place of standard load and
|
||||
store instructions. These intrinsics are used to represent load and store
|
||||
barriers. If your collector does not require such barriers, you can skip
|
||||
this step.
|
||||
|
|
@ -59,23 +56,21 @@ When generating LLVM IR for your functions, you will need to:
|
|||
You will need to identify roots (i.e. references to heap objects your collector
|
||||
needs to know about) in your generated IR, so that LLVM can encode them into
|
||||
your final stack maps. Depending on the collector strategy chosen, this is
|
||||
accomplished by using either the ``@llvm.gcroot`` intrinsics or a
|
||||
``gc.statepoint`` relocation sequence.
|
||||
accomplished by using either the `@llvm.gcroot` intrinsics or a
|
||||
`gc.statepoint` relocation sequence.
|
||||
|
||||
Don't forget to create a root for each intermediate value that is generated when
|
||||
evaluating an expression. In ``h(f(), g())``, the result of ``f()`` could
|
||||
easily be collected if evaluating ``g()`` triggers a collection.
|
||||
evaluating an expression. In `h(f(), g())`, the result of `f()` could
|
||||
easily be collected if evaluating `g()` triggers a collection.
|
||||
|
||||
Finally, you need to link your runtime library with the generated program
|
||||
executable (for a static compiler) or ensure the appropriate symbols are
|
||||
available for the runtime linker (for a JIT compiler).
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
## Introduction
|
||||
|
||||
What is Garbage Collection?
|
||||
---------------------------
|
||||
### What is Garbage Collection?
|
||||
|
||||
Garbage collection is a widely used technique that frees the programmer from
|
||||
having to know the lifetimes of heap objects, making software easier to produce
|
||||
|
|
@ -86,15 +81,14 @@ conservative and accurate.
|
|||
Conservative garbage collection often does not require any special support from
|
||||
either the language or the compiler: it can handle non-type-safe programming
|
||||
languages (such as C/C++) and does not require any special information from the
|
||||
compiler. The `Boehm collector
|
||||
<https://hboehm.info/gc/>`__ is an example of a
|
||||
compiler. The [Boehm collector](https://hboehm.info/gc/) is an example of a
|
||||
state-of-the-art conservative collector.
|
||||
|
||||
Accurate garbage collection requires the ability to identify all pointers in the
|
||||
program at run-time (which requires that the source-language be type-safe in
|
||||
most cases). Identifying pointers at run-time requires compiler support to
|
||||
locate all places that hold live pointer variables at run-time, including the
|
||||
:ref:`processor stack and registers <gcroot>`.
|
||||
{ref}`processor stack and registers <gcroot>`.
|
||||
|
||||
Conservative garbage collection is attractive because it does not require any
|
||||
special compiler support, but it does have problems. In particular, because the
|
||||
|
|
@ -115,11 +109,9 @@ techniques dominates any low-level losses.
|
|||
This document describes the mechanisms and interfaces provided by LLVM to
|
||||
support accurate garbage collection.
|
||||
|
||||
Goals and non-goals
|
||||
-------------------
|
||||
### Goals and non-goals
|
||||
|
||||
LLVM's intermediate representation provides :ref:`garbage collection intrinsics
|
||||
<gc_intrinsics>` that offer support for a broad class of collector models. For
|
||||
LLVM's intermediate representation provides {ref}`garbage collection intrinsics <gc_intrinsics>` that offer support for a broad class of collector models. For
|
||||
instance, the intrinsics permit:
|
||||
|
||||
* semi-space collectors
|
||||
|
|
@ -145,7 +137,7 @@ be part of your language's runtime library. LLVM provides a framework for
|
|||
describing the garbage collector's requirements to the compiler. In particular,
|
||||
LLVM provides support for generating stack maps at call sites, polling for a
|
||||
safepoint, and emitting load and store barriers. You can also extend LLVM -
|
||||
possibly through a loadable :ref:`code generation plugins <plugin>` - to
|
||||
possibly through a loadable {ref}`code generation plugins <plugin>` - to
|
||||
generate code and data structures which conform to the *binary interface*
|
||||
specified by the *runtime library*. This is similar to the relationship between
|
||||
LLVM and DWARF debugging info, for example. The difference primarily lies in
|
||||
|
|
@ -191,27 +183,24 @@ extension points. If you don't already have a specific binary interface
|
|||
you need to support, we recommend trying to use one of these built-in collector
|
||||
strategies.
|
||||
|
||||
.. _gc_intrinsics:
|
||||
(gc_intrinsics)=
|
||||
|
||||
LLVM IR Features
|
||||
================
|
||||
## LLVM IR Features
|
||||
|
||||
This section describes the garbage collection facilities provided by the
|
||||
:doc:`LLVM intermediate representation <LangRef>`. The exact behavior of these
|
||||
IR features is specified by the selected :ref:`GC strategy description
|
||||
<plugin>`.
|
||||
{doc}`LLVM intermediate representation <LangRef>`. The exact behavior of these
|
||||
IR features is specified by the selected {ref}`GC strategy description <plugin>`.
|
||||
|
||||
Specifying GC code generation: ``gc "..."``
|
||||
-------------------------------------------
|
||||
|
||||
.. code-block:: text
|
||||
### Specifying GC code generation: `gc "..."`
|
||||
|
||||
```text
|
||||
define <returntype> @name(...) gc "name" { ... }
|
||||
```
|
||||
|
||||
The ``gc`` function attribute is used to specify the desired GC strategy to the
|
||||
compiler. Its programmatic equivalent is the ``setGC`` method of ``Function``.
|
||||
The `gc` function attribute is used to specify the desired GC strategy to the
|
||||
compiler. Its programmatic equivalent is the `setGC` method of `Function`.
|
||||
|
||||
Setting ``gc "name"`` on a function triggers a search for a matching subclass
|
||||
Setting `gc "name"` on a function triggers a search for a matching subclass
|
||||
of GCStrategy. Some collector strategies are built in. You can add others
|
||||
using either the loadable plugin mechanism, or by patching your copy of LLVM.
|
||||
It is the selected GC strategy which defines the exact nature of the code
|
||||
|
|
@ -220,35 +209,32 @@ generated to support GC. If none is found, the compiler will raise an error.
|
|||
Specifying the GC style on a per-function basis allows LLVM to link together
|
||||
programs that use different garbage collection algorithms (or none at all).
|
||||
|
||||
.. _gcroot:
|
||||
(gcroot)=
|
||||
|
||||
Identifying GC roots on the stack
|
||||
----------------------------------
|
||||
### Identifying GC roots on the stack
|
||||
|
||||
LLVM currently supports two different mechanisms for describing references in
|
||||
compiled code at safepoints. ``llvm.gcroot`` is the older mechanism;
|
||||
``gc.statepoint`` has been added more recently. At the moment, you can choose
|
||||
either implementation (on a per :ref:`GC strategy <plugin>` basis). Longer
|
||||
term, we will probably either migrate away from ``llvm.gcroot`` entirely, or
|
||||
compiled code at safepoints. `llvm.gcroot` is the older mechanism;
|
||||
`gc.statepoint` has been added more recently. At the moment, you can choose
|
||||
either implementation (on a per {ref}`GC strategy <plugin>` basis). Longer
|
||||
term, we will probably either migrate away from `llvm.gcroot` entirely, or
|
||||
substantially merge their implementations. Note that most new development
|
||||
work is focused on ``gc.statepoint``.
|
||||
work is focused on `gc.statepoint`.
|
||||
|
||||
Using ``gc.statepoint``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
:doc:`This page <Statepoints>` contains detailed documentation for
|
||||
``gc.statepoint``.
|
||||
#### Using `gc.statepoint`
|
||||
{doc}`This page <Statepoints>` contains detailed documentation for
|
||||
`gc.statepoint`.
|
||||
|
||||
Using ``llvm.gcwrite``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: llvm
|
||||
#### Using `llvm.gcwrite`
|
||||
|
||||
```llvm
|
||||
void @llvm.gcroot(i8** %ptrloc, i8* %metadata)
|
||||
```
|
||||
|
||||
The ``llvm.gcroot`` intrinsic is used to inform LLVM that a stack variable
|
||||
The `llvm.gcroot` intrinsic is used to inform LLVM that a stack variable
|
||||
references an object on the heap and is to be tracked for garbage collection.
|
||||
The exact impact on generated code is specified by the Function's selected
|
||||
:ref:`GC strategy <plugin>`. All calls to ``llvm.gcroot`` **must** reside
|
||||
{ref}`GC strategy <plugin>`. All calls to `llvm.gcroot` **must** reside
|
||||
inside the first basic block.
|
||||
|
||||
The first argument **must** be a value referring to an alloca instruction or a
|
||||
|
|
@ -258,35 +244,34 @@ address. If your target collector uses tags, use a null pointer for metadata.
|
|||
|
||||
A compiler which performs manual SSA construction **must** ensure that SSA
|
||||
values representing GC references are stored into the alloca passed to the
|
||||
respective ``gcroot`` before every call site and reloaded after every call.
|
||||
A compiler which uses mem2reg to raise imperative code using ``alloca`` into
|
||||
SSA form need only add a call to ``@llvm.gcroot`` for those variables which
|
||||
respective `gcroot` before every call site and reloaded after every call.
|
||||
A compiler which uses mem2reg to raise imperative code using `alloca` into
|
||||
SSA form need only add a call to `@llvm.gcroot` for those variables which
|
||||
are pointers into the GC heap.
|
||||
|
||||
It is also important to mark intermediate values with ``llvm.gcroot``. For
|
||||
example, consider ``h(f(), g())``. Beware leaking the result of ``f()`` in the
|
||||
case that ``g()`` triggers a collection. Note that stack variables must be
|
||||
initialized and marked with ``llvm.gcroot`` in the function's prologue.
|
||||
It is also important to mark intermediate values with `llvm.gcroot`. For
|
||||
example, consider `h(f(), g())`. Beware leaking the result of `f()` in the
|
||||
case that `g()` triggers a collection. Note that stack variables must be
|
||||
initialized and marked with `llvm.gcroot` in the function's prologue.
|
||||
|
||||
The ``%metadata`` argument can be used to avoid requiring heap objects to have
|
||||
'isa' pointers or tag bits. [Appel89_, Goldberg91_, Tolmach94_] If specified,
|
||||
The `%metadata` argument can be used to avoid requiring heap objects to have
|
||||
'isa' pointers or tag bits. [[Appel89](#appel89), [Goldberg91](#goldberg91), [Tolmach94](#tolmach94)] If specified,
|
||||
its value will be tracked along with the location of the pointer in the stack
|
||||
frame.
|
||||
|
||||
Consider the following fragment of Java code:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
```java
|
||||
{
|
||||
Object X; // A null-initialized reference to an object
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This block (which may be located in the middle of a function or in a loop nest),
|
||||
could be compiled to this LLVM code:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
Entry:
|
||||
;; In the entry block for the function, allocate the
|
||||
;; stack space for X, which is an LLVM pointer.
|
||||
|
|
@ -310,9 +295,9 @@ could be compiled to this LLVM code:
|
|||
;; it, to indicate that the value is no longer live.
|
||||
store %Object* null, %Object** %X
|
||||
...
|
||||
```
|
||||
|
||||
Reading and writing references in the heap
|
||||
------------------------------------------
|
||||
### Reading and writing references in the heap
|
||||
|
||||
Some collectors need to be informed when the mutator (the program that needs
|
||||
garbage collection) either reads a pointer from or writes a pointer to a field
|
||||
|
|
@ -324,11 +309,10 @@ computation, so the overall performance impact of the barrier is tolerable.
|
|||
Barriers often require access to the *object pointer* rather than the *derived
|
||||
pointer* (which is a pointer to the field within the object). Accordingly,
|
||||
these intrinsics take both pointers as separate arguments for completeness. In
|
||||
this snippet, ``%object`` is the object pointer, and ``%derived`` is the derived
|
||||
this snippet, `%object` is the object pointer, and `%derived` is the derived
|
||||
pointer:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
;; An array type.
|
||||
%class.Array = type { %class.Object, i32, [0 x %class.Object*] }
|
||||
...
|
||||
|
|
@ -338,73 +322,70 @@ pointer:
|
|||
|
||||
;; Compute the derived pointer.
|
||||
%derived = getelementptr %object, i32 0, i32 2, i32 %n
|
||||
```
|
||||
|
||||
LLVM does not enforce this relationship between the object and derived pointer
|
||||
(although a particular :ref:`collector strategy <plugin>` might). However, it
|
||||
(although a particular {ref}`collector strategy <plugin>` might). However, it
|
||||
would be an unusual collector that violated it.
|
||||
|
||||
The use of these intrinsics is naturally optional if the target GC does not
|
||||
require the corresponding barrier. The GC strategy used with such a collector
|
||||
should replace the intrinsic calls with the corresponding ``load`` or
|
||||
``store`` instruction if they are used.
|
||||
should replace the intrinsic calls with the corresponding `load` or
|
||||
`store` instruction if they are used.
|
||||
|
||||
One known deficiency with the current design is that the barrier intrinsics do
|
||||
not include the size or alignment of the underlying operation performed. It is
|
||||
currently assumed that the operation is of pointer size and the alignment is
|
||||
assumed to be the target machine's default alignment.
|
||||
|
||||
Write barrier: ``llvm.gcwrite``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: llvm
|
||||
#### Write barrier: `llvm.gcwrite`
|
||||
|
||||
```llvm
|
||||
void @llvm.gcwrite(i8* %value, i8* %object, i8** %derived)
|
||||
```
|
||||
|
||||
For write barriers, LLVM provides the ``llvm.gcwrite`` intrinsic function. It
|
||||
has exactly the same semantics as a non-volatile ``store`` to the derived
|
||||
For write barriers, LLVM provides the `llvm.gcwrite` intrinsic function. It
|
||||
has exactly the same semantics as a non-volatile `store` to the derived
|
||||
pointer (the third argument). The exact code generated is specified by the
|
||||
Function's selected :ref:`GC strategy <plugin>`.
|
||||
Function's selected {ref}`GC strategy <plugin>`.
|
||||
|
||||
Many important algorithms require write barriers, including generational and
|
||||
concurrent collectors. Additionally, write barriers could be used to implement
|
||||
reference counting.
|
||||
|
||||
Read barrier: ``llvm.gcread``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: llvm
|
||||
#### Read barrier: `llvm.gcread`
|
||||
|
||||
```llvm
|
||||
i8* @llvm.gcread(i8* %object, i8** %derived)
|
||||
```
|
||||
|
||||
For read barriers, LLVM provides the ``llvm.gcread`` intrinsic function. It has
|
||||
exactly the same semantics as a non-volatile ``load`` from the derived pointer
|
||||
For read barriers, LLVM provides the `llvm.gcread` intrinsic function. It has
|
||||
exactly the same semantics as a non-volatile `load` from the derived pointer
|
||||
(the second argument). The exact code generated is specified by the Function's
|
||||
selected :ref:`GC strategy <plugin>`.
|
||||
selected {ref}`GC strategy <plugin>`.
|
||||
|
||||
Read barriers are needed by fewer algorithms than write barriers, and may have a
|
||||
greater performance impact since pointer reads are more frequent than writes.
|
||||
|
||||
.. _plugin:
|
||||
(plugin)=
|
||||
|
||||
.. _builtin-gc-strategies:
|
||||
(builtin-gc-strategies)=
|
||||
|
||||
Built-In GC Strategies
|
||||
======================
|
||||
## Built-In GC Strategies
|
||||
|
||||
LLVM includes built-in support for several varieties of garbage collectors.
|
||||
|
||||
The Shadow Stack GC
|
||||
----------------------
|
||||
### The Shadow Stack GC
|
||||
|
||||
To use this collector strategy, mark your functions with:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
```c++
|
||||
F.setGC("shadow-stack");
|
||||
```
|
||||
|
||||
Unlike many GC algorithms which rely on a cooperative code generator to compile
|
||||
stack maps, this algorithm carefully maintains a linked list of stack roots
|
||||
[:ref:`Henderson2002 <henderson02>`]. This so-called "shadow stack" mirrors the
|
||||
[[Henderson2002](#henderson02)]. This so-called "shadow stack" mirrors the
|
||||
machine stack. Maintaining this data structure is slower than using a stack map
|
||||
compiled into the executable as constant data, but has a significant portability
|
||||
advantage because it requires no special support from the target code generator,
|
||||
|
|
@ -417,13 +398,13 @@ The tradeoff for this simplicity and portability is:
|
|||
* Not thread-safe.
|
||||
|
||||
Still, it's an easy way to get started. After your compiler and runtime are up
|
||||
and running, writing a :ref:`plugin <plugin>` will allow you to take advantage
|
||||
of :ref:`more advanced GC features <collector-algos>` of LLVM in order to
|
||||
and running, writing a {ref}`plugin <plugin>` will allow you to take advantage
|
||||
of {ref}`more advanced GC features <collector-algos>` of LLVM in order to
|
||||
improve performance.
|
||||
|
||||
|
||||
The shadow stack doesn't imply a memory allocation algorithm. A semispace
|
||||
collector or building atop ``malloc`` are great places to start, and can be
|
||||
collector or building atop `malloc` are great places to start, and can be
|
||||
implemented with very little code.
|
||||
|
||||
When it comes time to collect, however, your runtime needs to traverse the stack
|
||||
|
|
@ -431,8 +412,7 @@ roots, and for this it needs to integrate with the shadow stack. Luckily, doing
|
|||
so is very simple. (This code is heavily commented to help you understand the
|
||||
data structure, but there are only 20 lines of meaningful code.)
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
```c++
|
||||
/// The map for a single function's stack frame. One of these is
|
||||
/// compiled as constant data into the executable for each function.
|
||||
///
|
||||
|
|
@ -479,58 +459,55 @@ data structure, but there are only 20 lines of meaningful code.)
|
|||
Visitor(&R->Roots[i], NULL);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The 'Erlang' and 'OCaml' GCs
|
||||
-----------------------------
|
||||
### The 'Erlang' and 'OCaml' GCs
|
||||
|
||||
LLVM ships with two example collectors which leverage the ``gcroot``
|
||||
LLVM ships with two example collectors which leverage the `gcroot`
|
||||
mechanisms. To our knowledge, these are not actually used by any language
|
||||
runtime, but they do provide a reasonable starting point for someone interested
|
||||
in writing a ``gcroot`` compatible GC plugin. In particular, these are the
|
||||
in writing a `gcroot` compatible GC plugin. In particular, these are the
|
||||
only in-tree examples of how to produce a custom binary stack map format using
|
||||
a ``gcroot`` strategy.
|
||||
a `gcroot` strategy.
|
||||
|
||||
As their names imply, the binary format produced is intended to model that
|
||||
used by the Erlang and OCaml compilers respectively.
|
||||
|
||||
.. _statepoint_example_gc:
|
||||
(statepoint_example_gc)=
|
||||
|
||||
The Statepoint Example GC
|
||||
-------------------------
|
||||
|
||||
.. code-block:: c++
|
||||
### The Statepoint Example GC
|
||||
|
||||
```c++
|
||||
F.setGC("statepoint-example");
|
||||
```
|
||||
|
||||
This GC provides an example of how one might use the infrastructure provided
|
||||
by ``gc.statepoint``. This example GC is compatible with the
|
||||
:ref:`PlaceSafepoints` and :ref:`RewriteStatepointsForGC` utility passes
|
||||
which simplify ``gc.statepoint`` sequence insertion. If you need to build a
|
||||
custom GC strategy around the ``gc.statepoints`` mechanisms, it is recommended
|
||||
by `gc.statepoint`. This example GC is compatible with the
|
||||
{ref}`PlaceSafepoints` and {ref}`RewriteStatepointsForGC` utility passes
|
||||
which simplify `gc.statepoint` sequence insertion. If you need to build a
|
||||
custom GC strategy around the `gc.statepoints` mechanisms, it is recommended
|
||||
that you use this one as a starting point.
|
||||
|
||||
This GC strategy does not support read or write barriers. As a result, these
|
||||
intrinsics are lowered to normal loads and stores.
|
||||
|
||||
The stack map format generated by this GC strategy can be found in the
|
||||
:ref:`stackmap-section` using a format documented :ref:`here
|
||||
<statepoint-stackmap-format>`. This format is intended to be the standard
|
||||
{ref}`stackmap-section` using a format documented {ref}`here <statepoint-stackmap-format>`. This format is intended to be the standard
|
||||
format supported by LLVM going forward.
|
||||
|
||||
The CoreCLR GC
|
||||
-------------------------
|
||||
|
||||
.. code-block:: c++
|
||||
### The CoreCLR GC
|
||||
|
||||
```c++
|
||||
F.setGC("coreclr");
|
||||
```
|
||||
|
||||
This GC leverages the ``gc.statepoint`` mechanism to support the
|
||||
`CoreCLR <https://github.com/dotnet/coreclr>`__ runtime.
|
||||
This GC leverages the `gc.statepoint` mechanism to support the
|
||||
[CoreCLR](https://github.com/dotnet/coreclr) runtime.
|
||||
|
||||
Support for this GC strategy is a work in progress. This strategy will
|
||||
differ from
|
||||
:ref:`statepoint-example GC<statepoint_example_gc>` strategy in
|
||||
{ref}`statepoint-example GC <statepoint_example_gc>` strategy in
|
||||
certain aspects like:
|
||||
|
||||
* Base-pointers of interior pointers are not explicitly
|
||||
|
|
@ -541,8 +518,7 @@ certain aspects like:
|
|||
* Safe-point polls are only needed before loop-back edges
|
||||
and before tail-calls (not needed at function-entry).
|
||||
|
||||
Custom GC Strategies
|
||||
====================
|
||||
## Custom GC Strategies
|
||||
|
||||
If none of the built-in GC strategy descriptions met your needs above, you will
|
||||
need to define a custom GCStrategy and possibly, a custom LLVM pass to perform
|
||||
|
|
@ -557,38 +533,36 @@ you need a patched build, please ask for advice on llvm-dev. There may be an
|
|||
easy way we can extend the support to make it work for your use case without
|
||||
requiring a custom build.
|
||||
|
||||
Collector Requirements
|
||||
----------------------
|
||||
### Collector Requirements
|
||||
|
||||
You should be able to leverage any existing collector library that includes the following elements:
|
||||
|
||||
#. A memory allocator which exposes an allocation function your compiled
|
||||
1. A memory allocator which exposes an allocation function your compiled
|
||||
code can call.
|
||||
|
||||
#. A binary format for the stack map. A stack map describes the location
|
||||
1. A binary format for the stack map. A stack map describes the location
|
||||
of references at a safepoint and is used by precise collectors to identify
|
||||
references within a stack frame on the machine stack. Note that collectors
|
||||
which conservatively scan the stack don't require such a structure.
|
||||
|
||||
#. A stack crawler to discover functions on the call stack, and enumerate the
|
||||
1. A stack crawler to discover functions on the call stack, and enumerate the
|
||||
references listed in the stack map for each call site.
|
||||
|
||||
#. A mechanism for identifying references in global locations (e.g. global
|
||||
1. A mechanism for identifying references in global locations (e.g. global
|
||||
variables).
|
||||
|
||||
#. If your collector requires them, an LLVM IR implementation of your collector's
|
||||
1. If your collector requires them, an LLVM IR implementation of your collector's
|
||||
load and store barriers. Note that since many collectors don't require
|
||||
barriers at all, LLVM defaults to lowering such barriers to normal loads
|
||||
and stores unless you arrange otherwise.
|
||||
|
||||
|
||||
Implementing a collector plugin
|
||||
-------------------------------
|
||||
### Implementing a collector plugin
|
||||
|
||||
User code specifies which GC code generation to use with the ``gc`` function
|
||||
attribute or, equivalently, with the ``setGC`` method of ``Function``.
|
||||
User code specifies which GC code generation to use with the `gc` function
|
||||
attribute or, equivalently, with the `setGC` method of `Function`.
|
||||
|
||||
To implement a GC plugin, it is necessary to subclass ``llvm::GCStrategy``,
|
||||
To implement a GC plugin, it is necessary to subclass `llvm::GCStrategy`,
|
||||
which can be accomplished in a few lines of boilerplate code. LLVM's
|
||||
infrastructure provides access to several important algorithms. For an
|
||||
uncontroversial collector, all that remains may be to compile LLVM's computed
|
||||
|
|
@ -598,13 +572,11 @@ runtime library). This can be accomplished in about 100 lines of code.
|
|||
This is not the appropriate place to implement a garbage collected heap or a
|
||||
garbage collector itself. That code should exist in the language's runtime
|
||||
library. The compiler plugin is responsible for generating code which conforms
|
||||
to the binary interface defined by the library, most essentially the :ref:`stack map
|
||||
<stack-map>`.
|
||||
to the binary interface defined by the library, most essentially the {ref}`stack map <stack-map>`.
|
||||
|
||||
To subclass ``llvm::GCStrategy`` and register it with the compiler:
|
||||
|
||||
.. code-block:: c++
|
||||
To subclass `llvm::GCStrategy` and register it with the compiler:
|
||||
|
||||
```c++
|
||||
// lib/MyGC/MyGC.cpp - Example LLVM GC plugin
|
||||
|
||||
#include "llvm/CodeGen/GCStrategy.h"
|
||||
|
|
@ -622,13 +594,14 @@ To subclass ``llvm::GCStrategy`` and register it with the compiler:
|
|||
GCRegistry::Add<MyGC>
|
||||
X("mygc", "My bespoke garbage collector.");
|
||||
}
|
||||
```
|
||||
|
||||
This boilerplate collector does nothing. More specifically:
|
||||
|
||||
* ``llvm.gcread`` calls are replaced with the corresponding ``load``
|
||||
* `llvm.gcread` calls are replaced with the corresponding `load`
|
||||
instruction.
|
||||
|
||||
* ``llvm.gcwrite`` calls are replaced with the corresponding ``store``
|
||||
* `llvm.gcwrite` calls are replaced with the corresponding `store`
|
||||
instruction.
|
||||
|
||||
* No safe points are added to the code.
|
||||
|
|
@ -638,8 +611,7 @@ This boilerplate collector does nothing. More specifically:
|
|||
Using the LLVM makefiles, this code
|
||||
can be compiled as a plugin using a simple makefile:
|
||||
|
||||
.. code-block:: make
|
||||
|
||||
```make
|
||||
# lib/MyGC/Makefile
|
||||
|
||||
LEVEL := ../..
|
||||
|
|
@ -647,132 +619,91 @@ can be compiled as a plugin using a simple makefile:
|
|||
LOADABLE_MODULE = 1
|
||||
|
||||
include $(LEVEL)/Makefile.common
|
||||
```
|
||||
|
||||
Once the plugin is compiled, code using it may be compiled using ``llc
|
||||
-load=MyGC.so`` (though MyGC.so may have some other platform-specific
|
||||
Once the plugin is compiled, code using it may be compiled using `llc`
|
||||
`-load=MyGC.so` (though MyGC.so may have some other platform-specific
|
||||
extension):
|
||||
|
||||
::
|
||||
|
||||
```console
|
||||
$ cat sample.ll
|
||||
define void @f() gc "mygc" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
$ llvm-as < sample.ll | llc -load=MyGC.so
|
||||
```
|
||||
|
||||
It is also possible to statically link the collector plugin into tools, such as
|
||||
a language-specific compiler front-end.
|
||||
|
||||
.. _collector-algos:
|
||||
(collector-algos)=
|
||||
|
||||
Overview of available features
|
||||
------------------------------
|
||||
### Overview of available features
|
||||
|
||||
``GCStrategy`` provides a range of features through which a plugin may do useful
|
||||
`GCStrategy` provides a range of features through which a plugin may do useful
|
||||
work. Some of these are callbacks, some are algorithms that can be enabled,
|
||||
disabled, or customized. This matrix summarizes the supported (and planned)
|
||||
features and correlates them with the collection techniques which typically
|
||||
require them.
|
||||
|
||||
.. |v| unicode:: 0x2714
|
||||
:trim:
|
||||
|
||||
.. |x| unicode:: 0x2718
|
||||
:trim:
|
||||
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| Algorithm | Done | Shadow | refcount | mark- | copying | incremental | threaded | concurrent |
|
||||
| | | stack | | sweep | | | | |
|
||||
+============+======+========+==========+=======+=========+=============+==========+============+
|
||||
| stack map | |v| | | | |x| | |x| | |x| | |x| | |x| |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| initialize | |v| | |x| | |x| | |x| | |x| | |x| | |x| | |x| |
|
||||
| roots | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| derived | NO | | | | | | **N**\* | **N**\* |
|
||||
| pointers | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| **custom | |v| | | | | | | | |
|
||||
| lowering** | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *gcroot* | |v| | |x| | |x| | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *gcwrite* | |v| | | |x| | | | |x| | | |x| |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *gcread* | |v| | | | | | | | |x| |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| **safe | | | | | | | | |
|
||||
| points** | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *in | |v| | | | |x| | |x| | |x| | |x| | |x| |
|
||||
| calls* | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *before | |v| | | | | | | |x| | |x| |
|
||||
| calls* | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *for | NO | | | | | | **N** | **N** |
|
||||
| loops* | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *before | |v| | | | | | | |x| | |x| |
|
||||
| escape* | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| emit code | NO | | | | | | **N** | **N** |
|
||||
| at safe | | | | | | | | |
|
||||
| points | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| **output** | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *assembly* | |v| | | | |x| | |x| | |x| | |x| | |x| |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *JIT* | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| *obj* | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| live | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
| analysis | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| register | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
| map | | | | | | | | |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| \* Derived pointers only pose a hazard to copying collections. |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| **?** denotes a feature which could be utilized if available. |
|
||||
+------------+------+--------+----------+-------+---------+-------------+----------+------------+
|
||||
| Algorithm | Done | Shadow stack | refcount | mark-sweep | copying | incremental | threaded | concurrent |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| stack map | ✓ | | | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| initialize<br>roots | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| derived<br>pointers | NO | | | | | | **N**\* | **N**\* |
|
||||
| **custom<br>lowering** | ✓ | | | | | | | |
|
||||
| *gcroot* | ✓ | ✘ | ✘ | | | | | |
|
||||
| *gcwrite* | ✓ | | ✘ | | | ✘ | | ✘ |
|
||||
| *gcread* | ✓ | | | | | | | ✘ |
|
||||
| **safe<br>points** | | | | | | | | |
|
||||
| *in<br>calls* | ✓ | | | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| *before<br>calls* | ✓ | | | | | | ✘ | ✘ |
|
||||
| *for<br>loops* | NO | | | | | | **N** | **N** |
|
||||
| *before<br>escape* | ✓ | | | | | | ✘ | ✘ |
|
||||
| emit code<br>at safe<br>points | NO | | | | | | **N** | **N** |
|
||||
| **output** | | | | | | | | |
|
||||
| *assembly* | ✓ | | | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| *JIT* | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
| *obj* | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
| live<br>analysis | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
| register<br>map | NO | | | **?** | **?** | **?** | **?** | **?** |
|
||||
| \* Derived pointers only pose a hazard to copying collections. | | | | | | | | |
|
||||
| **?** denotes a feature which could be utilized if available. | | | | | | | | |
|
||||
|
||||
To be clear, the collection techniques above are defined as:
|
||||
|
||||
Shadow Stack
|
||||
The mutator carefully maintains a linked list of stack roots.
|
||||
: The mutator carefully maintains a linked list of stack roots.
|
||||
|
||||
Reference Counting
|
||||
The mutator maintains a reference count for each object and frees an object
|
||||
: The mutator maintains a reference count for each object and frees an object
|
||||
when its count falls to zero.
|
||||
|
||||
Mark-Sweep
|
||||
When the heap is exhausted, the collector marks reachable objects starting
|
||||
: When the heap is exhausted, the collector marks reachable objects starting
|
||||
from the roots, then deallocates unreachable objects in a sweep phase.
|
||||
|
||||
Copying
|
||||
As reachability analysis proceeds, the collector copies objects from one heap
|
||||
: As reachability analysis proceeds, the collector copies objects from one heap
|
||||
area to another, compacting them in the process. Copying collectors enable
|
||||
highly efficient "bump pointer" allocation and can improve locality of
|
||||
reference.
|
||||
|
||||
Incremental
|
||||
(Including generational collectors.) Incremental collectors generally have all
|
||||
: (Including generational collectors.) Incremental collectors generally have all
|
||||
the properties of a copying collector (regardless of whether the mature heap
|
||||
is compacting), but bring the added complexity of requiring write barriers.
|
||||
|
||||
Threaded
|
||||
Denotes a multithreaded mutator; the collector must still stop the mutator
|
||||
: Denotes a multithreaded mutator; the collector must still stop the mutator
|
||||
("stop the world") before beginning reachability analysis. Stopping a
|
||||
multithreaded mutator is a complicated problem. It generally requires highly
|
||||
platform-specific code in the runtime, and the production of carefully
|
||||
designed machine code at safe points.
|
||||
|
||||
Concurrent
|
||||
In this technique, the mutator and the collector run concurrently, with the
|
||||
: In this technique, the mutator and the collector run concurrently, with the
|
||||
goal of eliminating pause times. In a *cooperative* collector, the mutator
|
||||
further aids with collection should a pause occur, allowing collection to take
|
||||
advantage of multiprocessor hosts. The "stop the world" problem of threaded
|
||||
|
|
@ -784,37 +715,35 @@ suitable for a wide variety of collectors, but does not currently extend to
|
|||
multithreaded programs. This will be added in the future as there is
|
||||
interest.
|
||||
|
||||
.. _stack-map:
|
||||
(stack-map)=
|
||||
|
||||
Computing stack maps
|
||||
--------------------
|
||||
### Computing stack maps
|
||||
|
||||
LLVM automatically computes a stack map. One of the most important features
|
||||
of a ``GCStrategy`` is to compile this information into the executable in
|
||||
of a `GCStrategy` is to compile this information into the executable in
|
||||
the binary representation expected by the runtime library.
|
||||
|
||||
The stack map consists of the location and identity of each GC root in the
|
||||
each function in the module. For each root:
|
||||
|
||||
* ``RootNum``: The index of the root.
|
||||
* `RootNum`: The index of the root.
|
||||
|
||||
* ``StackOffset``: The offset of the object relative to the frame pointer.
|
||||
* `StackOffset`: The offset of the object relative to the frame pointer.
|
||||
|
||||
* ``RootMetadata``: The value passed as the ``%metadata`` parameter to the
|
||||
``@llvm.gcroot`` intrinsic.
|
||||
* `RootMetadata`: The value passed as the `%metadata` parameter to the
|
||||
`@llvm.gcroot` intrinsic.
|
||||
|
||||
Also, for the function as a whole:
|
||||
|
||||
* ``getFrameSize()``: The overall size of the function's initial stack frame,
|
||||
* `getFrameSize()`: The overall size of the function's initial stack frame,
|
||||
not accounting for any dynamic allocation.
|
||||
|
||||
* ``roots_size()``: The count of roots in the function.
|
||||
* `roots_size()`: The count of roots in the function.
|
||||
|
||||
To access the stack map, use ``GCFunctionMetadata::roots_begin()`` and
|
||||
-``end()`` from the :ref:`GCMetadataPrinter <assembly>`:
|
||||
|
||||
.. code-block:: c++
|
||||
To access the stack map, use `GCFunctionMetadata::roots_begin()` and
|
||||
-`end()` from the {ref}`GCMetadataPrinter <assembly>`:
|
||||
|
||||
```c++
|
||||
for (iterator I = begin(), E = end(); I != E; ++I) {
|
||||
GCFunctionInfo *FI = *I;
|
||||
unsigned FrameSize = FI->getFrameSize();
|
||||
|
|
@ -828,26 +757,25 @@ To access the stack map, use ``GCFunctionMetadata::roots_begin()`` and
|
|||
Constant *RootMetadata = RI->Metadata;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the ``llvm.gcroot`` intrinsic is eliminated before code generation by a
|
||||
If the `llvm.gcroot` intrinsic is eliminated before code generation by a
|
||||
custom lowering pass, LLVM will compute an empty stack map. This may be useful
|
||||
for collector plugins which implement reference counting or a shadow stack.
|
||||
|
||||
.. _init-roots:
|
||||
(init-roots)=
|
||||
|
||||
Initializing roots to null
|
||||
---------------------------
|
||||
### Initializing roots to null
|
||||
|
||||
It is recommended that frontends initialize roots explicitly to avoid
|
||||
potentially confusing the optimizer. This prevents the GC from visiting
|
||||
uninitialized pointers, which will almost certainly cause it to crash.
|
||||
|
||||
As a fallback, LLVM will automatically initialize each root to ``null``
|
||||
As a fallback, LLVM will automatically initialize each root to `null`
|
||||
upon entry to the function. Support for this mode in code generation is
|
||||
largely a legacy detail to keep old collector implementations working.
|
||||
|
||||
Custom lowering of intrinsics
|
||||
------------------------------
|
||||
### Custom lowering of intrinsics
|
||||
|
||||
For GCs which use barriers or unusual treatment of stack roots, the
|
||||
implementor is responsible for providing a custom pass to lower the
|
||||
|
|
@ -860,10 +788,9 @@ ShadowStackGCLowering pass.
|
|||
There is currently no way to register such a custom lowering pass
|
||||
without building a custom copy of LLVM.
|
||||
|
||||
.. _safe-points:
|
||||
(safe-points)=
|
||||
|
||||
Generating safe points
|
||||
-----------------------
|
||||
### Generating safe points
|
||||
|
||||
LLVM provides support for associating stackmaps with the return address of
|
||||
a call. Any loop or return safepoints required by a given collector design
|
||||
|
|
@ -871,10 +798,9 @@ can be modeled via calls to runtime routines, or potentially patchable call
|
|||
sequences. Using gcroot, all call instructions are inferred to be possible
|
||||
safepoints and will thus have an associated stackmap.
|
||||
|
||||
.. _assembly:
|
||||
(assembly)=
|
||||
|
||||
Emitting assembly code: ``GCMetadataPrinter``
|
||||
---------------------------------------------
|
||||
### Emitting assembly code: `GCMetadataPrinter`
|
||||
|
||||
LLVM allows a plugin to print arbitrary assembly code before and after the rest
|
||||
of a module's assembly code. At the end of the module, the GC can compile the
|
||||
|
|
@ -883,22 +809,21 @@ yet computed.)
|
|||
|
||||
Since AsmWriter and CodeGen are separate components of LLVM, a separate abstract
|
||||
base class and registry is provided for printing assembly code, the
|
||||
``GCMetadaPrinter`` and ``GCMetadataPrinterRegistry``. The AsmWriter will look
|
||||
for such a subclass if the ``GCStrategy`` sets ``UsesMetadata``:
|
||||
|
||||
.. code-block:: c++
|
||||
`GCMetadaPrinter` and `GCMetadataPrinterRegistry`. The AsmWriter will look
|
||||
for such a subclass if the `GCStrategy` sets `UsesMetadata`:
|
||||
|
||||
```c++
|
||||
MyGC::MyGC() {
|
||||
UsesMetadata = true;
|
||||
}
|
||||
```
|
||||
|
||||
This separation allows JIT-only clients to be smaller.
|
||||
|
||||
Note that LLVM does not currently have analogous APIs to support code generation
|
||||
in the JIT, nor using the object writers.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
```c++
|
||||
// lib/MyGC/MyGCPrinter.cpp - Example LLVM GC printer
|
||||
|
||||
#include "llvm/CodeGen/GCMetadataPrinter.h"
|
||||
|
|
@ -917,14 +842,14 @@ in the JIT, nor using the object writers.
|
|||
GCMetadataPrinterRegistry::Add<MyGCPrinter>
|
||||
X("mygc", "My bespoke garbage collector.");
|
||||
}
|
||||
```
|
||||
|
||||
The collector should use ``AsmPrinter`` to print portable assembly code. The
|
||||
The collector should use `AsmPrinter` to print portable assembly code. The
|
||||
collector itself contains the stack map for the entire module, and may access
|
||||
the ``GCFunctionInfo`` using its own ``begin()`` and ``end()`` methods. Here's
|
||||
the `GCFunctionInfo` using its own `begin()` and `end()` methods. Here's
|
||||
a realistic example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
```c++
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
|
|
@ -1002,27 +927,26 @@ a realistic example:
|
|||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
References
|
||||
==========
|
||||
## References
|
||||
|
||||
.. _appel89:
|
||||
(appel89)=
|
||||
|
||||
[Appel89] Runtime Tags Aren't Necessary. Andrew W. Appel. Lisp and Symbolic
|
||||
Computation 19(7):703-705, July 1989.
|
||||
|
||||
.. _goldberg91:
|
||||
(goldberg91)=
|
||||
|
||||
[Goldberg91] Tag-free garbage collection for strongly typed programming
|
||||
languages. Benjamin Goldberg. ACM SIGPLAN PLDI'91.
|
||||
|
||||
.. _tolmach94:
|
||||
(tolmach94)=
|
||||
|
||||
[Tolmach94] Tag-free garbage collection using explicit type parameters. Andrew
|
||||
Tolmach. Proceedings of the 1994 ACM conference on LISP and functional
|
||||
programming.
|
||||
|
||||
.. _henderson02:
|
||||
(henderson02)=
|
||||
|
||||
[Henderson2002] `Accurate Garbage Collection in an Uncooperative Environment
|
||||
<http://citeseer.ist.psu.edu/henderson02accurate.html>`__
|
||||
[Henderson2002] [Accurate Garbage Collection in an Uncooperative Environment](http://citeseer.ist.psu.edu/henderson02accurate.html)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
==================================================================
|
||||
Getting Started with the LLVM System using Microsoft Visual Studio
|
||||
==================================================================
|
||||
# Getting Started with the LLVM System using Microsoft Visual Studio
|
||||
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
## Overview
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
Welcome to LLVM on Windows! This document only covers LLVM on Windows using
|
||||
Visual Studio, not WSL, mingw or cygwin. In order to get started, you first need
|
||||
to know some basic information.
|
||||
|
|
@ -19,7 +16,7 @@ to use LLVM. It contains an assembler, disassembler, bitcode analyzer, and
|
|||
bitcode optimizer. It also contains basic regression tests that can be used to
|
||||
test the LLVM tools and the Clang front end.
|
||||
|
||||
The second piece is the `Clang <https://clang.llvm.org/>`_ front end. This
|
||||
The second piece is the [Clang](https://clang.llvm.org/) front end. This
|
||||
component compiles C, C++, Objective C, and Objective C++ code into LLVM
|
||||
bitcode. Clang typically uses LLVM libraries to optimize the bitcode and emit
|
||||
machine code. LLVM fully supports the COFF object file format, which is
|
||||
|
|
@ -28,14 +25,14 @@ compatible with all other existing Windows toolchains.
|
|||
This document does not discuss other LLVM projects.
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
## Requirements
|
||||
|
||||
Before you begin to use the LLVM system, review the requirements given
|
||||
below. This may save you some trouble by knowing ahead of time what hardware
|
||||
and software you will need.
|
||||
|
||||
Hardware
|
||||
--------
|
||||
### Hardware
|
||||
|
||||
Any system that can adequately run Visual Studio 2019 is fine. The LLVM
|
||||
source tree, including the git index, consumes approximately 3GB.
|
||||
Object files, libraries, and executables consume approximately 5GB in
|
||||
|
|
@ -43,44 +40,44 @@ Release mode and much more in Debug mode. An SSD drive and >16GB RAM are
|
|||
recommended.
|
||||
|
||||
|
||||
Software
|
||||
--------
|
||||
You will need `Visual Studio <https://visualstudio.microsoft.com/>`_ 2019 or
|
||||
### Software
|
||||
|
||||
You will need [Visual Studio](https://visualstudio.microsoft.com/) 2019 or
|
||||
later, with the latest Update installed. Visual Studio Community Edition
|
||||
suffices.
|
||||
|
||||
You will also need the `CMake <http://www.cmake.org/>`_ build system since it
|
||||
You will also need the [CMake](http://www.cmake.org/) build system since it
|
||||
generates the project files you will use to build with. CMake is bundled with
|
||||
Visual Studio 2019 so a separate installation is not required. If you do install
|
||||
CMake separately, Visual Studio 2022 will require CMake Version 3.21 or later.
|
||||
|
||||
If you would like to run the LLVM tests you will need `Python
|
||||
<http://www.python.org/>`_. Version 3.8 and newer are known to work. You can
|
||||
If you would like to run the LLVM tests you will need [Python](http://www.python.org/).
|
||||
Version 3.8 and newer are known to work. You can
|
||||
install Python with Visual Studio 2019, from the Microsoft store or from
|
||||
the `Python web site <http://www.python.org/>`_. We recommend the latter since it
|
||||
the [Python web site](http://www.python.org/). We recommend the latter since it
|
||||
allows you to adjust installation options.
|
||||
|
||||
You will need `Git for Windows <https://git-scm.com/>`_ with bash tools, too.
|
||||
You will need [Git for Windows](https://git-scm.com/) with bash tools, too.
|
||||
Git for Windows is also bundled with Visual Studio 2019.
|
||||
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
## Getting Started
|
||||
|
||||
Here's the short story for getting up and running quickly with LLVM.
|
||||
These instructions were tested with Visual Studio 2019 and Python 3.9.6:
|
||||
|
||||
1. Download and install `Visual Studio <https://visualstudio.microsoft.com/>`_.
|
||||
1. Download and install [Visual Studio](https://visualstudio.microsoft.com/).
|
||||
2. In the Visual Studio installer, Workloads tab, select the
|
||||
**Desktop development with C++** workload. Under Individual components tab,
|
||||
select **Git for Windows**.
|
||||
3. Complete the Visual Studio installation.
|
||||
4. Download and install the latest `Python 3 release <http://www.python.org/>`_.
|
||||
4. Download and install the latest [Python 3 release](http://www.python.org/).
|
||||
5. In the first install screen, select both **Install launcher for all users**
|
||||
and **Add Python to the PATH**. This will allow installing psutil for all
|
||||
users for the regression tests and make Python available from the command
|
||||
line.
|
||||
6. In the second install screen, select (again) **Install for all users** and
|
||||
if you want to develop `lldb <https://lldb.llvm.org/>`_, selecting
|
||||
if you want to develop [lldb](https://lldb.llvm.org/), selecting
|
||||
**Download debug binaries** is useful.
|
||||
7. Complete the Python installation.
|
||||
8. Run a "Developer Command Prompt for VS 2019" **as administrator**. This command
|
||||
|
|
@ -88,176 +85,177 @@ These instructions were tested with Visual Studio 2019 and Python 3.9.6:
|
|||
the installed tools.
|
||||
9. In the terminal window, type the commands:
|
||||
|
||||
.. code-block:: bat
|
||||
```bat
|
||||
c:
|
||||
cd \
|
||||
```
|
||||
|
||||
c:
|
||||
cd \
|
||||
|
||||
You may install the llvm sources in a location other than ``c:\llvm`` but do not
|
||||
install into a path containing spaces (e.g., ``c:\Documents and Settings\...``)
|
||||
as it will fail.
|
||||
You may install the llvm sources in a location other than `c:\llvm` but do not
|
||||
install into a path containing spaces (e.g., `c:\Documents and Settings\...`)
|
||||
as it will fail.
|
||||
|
||||
10. Register the Microsoft Debug Interface Access (DIA) DLLs:
|
||||
|
||||
.. code-block:: bat
|
||||
```bat
|
||||
regsvr32 "%VSINSTALLDIR%\DIA SDK\bin\msdia140.dll"
|
||||
regsvr32 "%VSINSTALLDIR%\DIA SDK\bin\amd64\msdia140.dll"
|
||||
```
|
||||
|
||||
regsvr32 "%VSINSTALLDIR%\DIA SDK\bin\msdia140.dll"
|
||||
regsvr32 "%VSINSTALLDIR%\DIA SDK\bin\amd64\msdia140.dll"
|
||||
|
||||
The DIA library is required for LLVM PDB tests and
|
||||
`LLDB development <https://lldb.llvm.org/resources/build.html>`_.
|
||||
The DIA library is required for LLVM PDB tests and
|
||||
[LLDB development](https://lldb.llvm.org/resources/build.html).
|
||||
|
||||
11. Install psutil and obtain the LLVM source code:
|
||||
|
||||
.. code-block:: bat
|
||||
```bat
|
||||
pip install psutil
|
||||
git clone https://github.com/llvm/llvm-project.git llvm
|
||||
```
|
||||
|
||||
pip install psutil
|
||||
git clone https://github.com/llvm/llvm-project.git llvm
|
||||
|
||||
Instead of ``git clone`` you may download a compressed source distribution
|
||||
from the `releases page <https://github.com/llvm/llvm-project/releases>`_.
|
||||
Select the last link: ``Source code (zip)`` and unpack the downloaded file using
|
||||
Windows Explorer built-in zip support or any other unzip tool.
|
||||
Instead of `git clone` you may download a compressed source distribution
|
||||
from the [releases page](https://github.com/llvm/llvm-project/releases).
|
||||
Select the last link: `Source code (zip)` and unpack the downloaded file using
|
||||
Windows Explorer built-in zip support or any other unzip tool.
|
||||
|
||||
12. Finally, configure LLVM using CMake:
|
||||
|
||||
.. code-block:: bat
|
||||
```bat
|
||||
cmake -S llvm\llvm -B build -DLLVM_ENABLE_PROJECTS=clang -DLLVM_TARGETS_TO_BUILD=X86 -Thost=x64
|
||||
exit
|
||||
```
|
||||
|
||||
cmake -S llvm\llvm -B build -DLLVM_ENABLE_PROJECTS=clang -DLLVM_TARGETS_TO_BUILD=X86 -Thost=x64
|
||||
exit
|
||||
```{note}
|
||||
By default, the Visual Studio project files generated by CMake use the
|
||||
32-bit toolset. If you are developing on a 64-bit version of Windows and
|
||||
want to use the 64-bit toolset, pass the `-Thost=x64` flag when
|
||||
generating the Visual Studio solution. This requires CMake 3.8.0 or later.
|
||||
|
||||
.. note::
|
||||
By default, the Visual Studio project files generated by CMake use the
|
||||
32-bit toolset. If you are developing on a 64-bit version of Windows and
|
||||
want to use the 64-bit toolset, pass the ``-Thost=x64`` flag when
|
||||
generating the Visual Studio solution. This requires CMake 3.8.0 or later.
|
||||
For Windows on Arm the equivalent is `-Thost=ARM64`, but this the default
|
||||
for those hosts, so you do not have to use this option.
|
||||
```
|
||||
|
||||
For Windows on Arm the equivalent is ``-Thost=ARM64``, but this the default
|
||||
for those hosts, so you do not have to use this option.
|
||||
`LLVM_ENABLE_PROJECTS` specifies any additional LLVM projects you want to
|
||||
build while `LLVM_TARGETS_TO_BUILD` selects the compiler targets. If
|
||||
`LLVM_TARGETS_TO_BUILD` is omitted by default all targets are built
|
||||
slowing compilation and using more disk space.
|
||||
See the {doc}`LLVM CMake guide <CMake>` for detailed information about
|
||||
how to configure the LLVM build.
|
||||
|
||||
``LLVM_ENABLE_PROJECTS`` specifies any additional LLVM projects you want to
|
||||
build while ``LLVM_TARGETS_TO_BUILD`` selects the compiler targets. If
|
||||
``LLVM_TARGETS_TO_BUILD`` is omitted by default all targets are built
|
||||
slowing compilation and using more disk space.
|
||||
See the :doc:`LLVM CMake guide <CMake>` for detailed information about
|
||||
how to configure the LLVM build.
|
||||
The `cmake` command line tool is bundled with Visual Studio but its GUI is
|
||||
not. You may install [CMake](http://www.cmake.org/) to use its GUI to change
|
||||
CMake variables or modify the above command line.
|
||||
|
||||
The ``cmake`` command line tool is bundled with Visual Studio but its GUI is
|
||||
not. You may install `CMake <http://www.cmake.org/>`_ to use its GUI to change
|
||||
CMake variables or modify the above command line.
|
||||
|
||||
* Once CMake is installed then the simplest way is to just start the
|
||||
CMake GUI, select the directory where you have LLVM extracted to, and
|
||||
the default options should all be fine. One option you may really
|
||||
want to change, regardless of anything else, might be the
|
||||
``CMAKE_INSTALL_PREFIX`` setting to select a directory to INSTALL to
|
||||
once compiling is complete, although installation is not mandatory for
|
||||
using LLVM. Another important option is ``LLVM_TARGETS_TO_BUILD``,
|
||||
which controls the LLVM target architectures that are included on the
|
||||
build.
|
||||
* CMake generates project files for all build types. To select a specific
|
||||
build type, use the Configuration manager from the VS IDE or the
|
||||
``/property:Configuration`` command-line option when using MSBuild.
|
||||
* Once CMake is installed then the simplest way is to just start the
|
||||
CMake GUI, select the directory where you have LLVM extracted to, and
|
||||
the default options should all be fine. One option you may really
|
||||
want to change, regardless of anything else, might be the
|
||||
`CMAKE_INSTALL_PREFIX` setting to select a directory to INSTALL to
|
||||
once compiling is complete, although installation is not mandatory for
|
||||
using LLVM. Another important option is `LLVM_TARGETS_TO_BUILD`,
|
||||
which controls the LLVM target architectures that are included on the
|
||||
build.
|
||||
* CMake generates project files for all build types. To select a specific
|
||||
build type, use the Configuration manager from the VS IDE or the
|
||||
`/property:Configuration` command-line option when using MSBuild.
|
||||
|
||||
13. Start Visual Studio and select configuration:
|
||||
|
||||
In the directory you created the project files will have an ``llvm.sln``
|
||||
file, just double-click on that to open Visual Studio. The default Visual
|
||||
Studio configuration is **Debug** which is slow and generates a huge amount
|
||||
of debug information on disk. For now, we recommend selecting **Release**
|
||||
configuration for the LLVM project which will build the fastest or
|
||||
**RelWithDebInfo** which is also several time larger than Release.
|
||||
Another technique is to build all of LLVM in Release mode and change
|
||||
compiler flags, disabling optimization and enabling debug information, only
|
||||
for specific libraries or source files you actually need to debug.
|
||||
In the directory you created the project files will have an `llvm.sln`
|
||||
file, just double-click on that to open Visual Studio. The default Visual
|
||||
Studio configuration is **Debug** which is slow and generates a huge amount
|
||||
of debug information on disk. For now, we recommend selecting **Release**
|
||||
configuration for the LLVM project which will build the fastest or
|
||||
**RelWithDebInfo** which is also several time larger than Release.
|
||||
Another technique is to build all of LLVM in Release mode and change
|
||||
compiler flags, disabling optimization and enabling debug information, only
|
||||
for specific libraries or source files you actually need to debug.
|
||||
|
||||
14. Test LLVM in Visual Studio:
|
||||
|
||||
You can run LLVM tests by merely building the project "check-all". The test
|
||||
results will be shown in the VS output window. Once the build succeeds, you
|
||||
have verified a working LLVM development environment!
|
||||
You can run LLVM tests by merely building the project "check-all". The test
|
||||
results will be shown in the VS output window. Once the build succeeds, you
|
||||
have verified a working LLVM development environment!
|
||||
|
||||
You should not see any unexpected failures, but you will see many unsupported
|
||||
tests and expected failures:
|
||||
|
||||
::
|
||||
You should not see any unexpected failures, but you will see many unsupported
|
||||
tests and expected failures:
|
||||
|
||||
```
|
||||
114>Testing Time: 1124.66s
|
||||
114> Skipped : 39
|
||||
114> Unsupported : 21649
|
||||
114> Passed : 51615
|
||||
114> Expectedly Failed: 93
|
||||
========== Build: 114 succeeded, 0 failed, 321 up-to-date, 0 skipped ==========``
|
||||
```
|
||||
|
||||
## Alternatives to manual installation
|
||||
|
||||
Alternatives to manual installation
|
||||
===================================
|
||||
Instead of the steps above, to simplify the installation procedure you can use
|
||||
`Chocolatey <https://chocolatey.org/>`_ as package manager.
|
||||
After the `installation <https://chocolatey.org/install>`_ of Chocolatey,
|
||||
[Chocolatey](https://chocolatey.org/) as package manager.
|
||||
After the [installation](https://chocolatey.org/install) of Chocolatey,
|
||||
run these commands in an admin shell to install the required tools:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
choco install -y git cmake python3
|
||||
pip3 install psutil
|
||||
```bat
|
||||
choco install -y git cmake python3
|
||||
pip3 install psutil
|
||||
```
|
||||
|
||||
There is also a Windows
|
||||
`Dockerfile <https://github.com/llvm/llvm-zorg/blob/main/buildbot/google/docker/windows-base-vscode2019/Dockerfile>`_
|
||||
[Dockerfile](https://github.com/llvm/llvm-zorg/blob/main/buildbot/google/docker/windows-base-vscode2019/Dockerfile)
|
||||
with the entire build toolchain. This can be used to test the build with a
|
||||
toolchain different from your host installation or to create build servers.
|
||||
|
||||
Next steps
|
||||
==========
|
||||
## Next steps
|
||||
|
||||
1. Read the documentation.
|
||||
2. Seriously, read the documentation.
|
||||
3. Remember that you were warned twice about reading the documentation.
|
||||
|
||||
Test LLVM on the command line:
|
||||
------------------------------
|
||||
### Test LLVM on the command line:
|
||||
|
||||
The LLVM tests can be run by changing directory to the llvm source
|
||||
directory and running:
|
||||
|
||||
.. code-block:: bat
|
||||
```bat
|
||||
c:\llvm> python ..\build\Release\bin\llvm-lit.py llvm\test
|
||||
```
|
||||
|
||||
c:\llvm> python ..\build\Release\bin\llvm-lit.py llvm\test
|
||||
|
||||
This example assumes that Python is in your ``PATH`` variable, which would be
|
||||
This example assumes that Python is in your `PATH` variable, which would be
|
||||
after **Add Python to the PATH** was selected during Python installation.
|
||||
If you had opened a command window prior to Python installation, you would
|
||||
have to close and reopen it to get the updated ``PATH``.
|
||||
have to close and reopen it to get the updated `PATH`.
|
||||
|
||||
A specific test or test directory can be run with:
|
||||
|
||||
.. code-block:: bat
|
||||
```bat
|
||||
c:\llvm> python ..\build\Release\bin\llvm-lit.py llvm\test\Transforms\Util
|
||||
```
|
||||
|
||||
c:\llvm> python ..\build\Release\bin\llvm-lit.py llvm\test\Transforms\Util
|
||||
### Build the LLVM Suite:
|
||||
|
||||
Build the LLVM Suite:
|
||||
---------------------
|
||||
* The projects may still be built individually, but to build them all do
|
||||
not just select all of them in batch build (as some are meant as
|
||||
configuration projects), but rather select and build just the
|
||||
``ALL_BUILD`` project to build everything, or the ``INSTALL`` project,
|
||||
which first builds the ``ALL_BUILD`` project, then installs the LLVM
|
||||
`ALL_BUILD` project to build everything, or the `INSTALL` project,
|
||||
which first builds the `ALL_BUILD` project, then installs the LLVM
|
||||
headers, libs, and other useful things to the directory set by the
|
||||
``CMAKE_INSTALL_PREFIX`` setting when you first configured CMake.
|
||||
`CMAKE_INSTALL_PREFIX` setting when you first configured CMake.
|
||||
* The Fibonacci project is a sample program that uses the JIT. Modify the
|
||||
project's debugging properties to provide a numeric command-line argument
|
||||
or run it from the command line. The program will print the
|
||||
corresponding Fibonacci value.
|
||||
|
||||
|
||||
Links
|
||||
=====
|
||||
## Links
|
||||
|
||||
This document is just an **introduction** to how to use LLVM to do some simple
|
||||
things... there are many more interesting and complicated things that you can
|
||||
do that aren't documented here (but we'll gladly accept a patch if you want to
|
||||
write something up!). For more information about LLVM, check out:
|
||||
|
||||
* `LLVM homepage <https://llvm.org/>`_
|
||||
* `LLVM doxygen tree <https://llvm.org/doxygen/>`_
|
||||
* [LLVM homepage](https://llvm.org/)
|
||||
* [LLVM doxygen tree](https://llvm.org/doxygen/)
|
||||
* Additional information about the LLVM directory structure and tool chain
|
||||
can be found on the main :doc:`GettingStarted` page.
|
||||
can be found on the main {doc}`GettingStarted` page.
|
||||
* If you are having problems building or using LLVM, or if you have any other
|
||||
general questions about LLVM, please consult the
|
||||
:doc:`Frequently Asked Questions <FAQ>` page.
|
||||
{doc}`Frequently Asked Questions <FAQ>` page.
|
||||
|
|
|
|||
|
|
@ -1,371 +1,343 @@
|
|||
===================================
|
||||
Instrumentation Profile Format
|
||||
===================================
|
||||
# Instrumentation Profile Format
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
Overview
|
||||
=========
|
||||
|
||||
Clang supports two types of profiling via instrumentation [1]_: frontend-based
|
||||
and IR-based, and both could support a variety of use cases [2]_ .
|
||||
Clang supports two types of profiling via instrumentation [^1]: frontend-based
|
||||
and IR-based, and both could support a variety of use cases [^2] .
|
||||
This document describes two binary serialization formats (raw and indexed) to
|
||||
store instrumented profiles with a specific emphasis on IRPGO use case, in the
|
||||
sense that when specific header fields and payload sections have different ways
|
||||
of interpretation across use cases, the documentation is based on IRPGO.
|
||||
|
||||
.. note::
|
||||
Frontend-generated profiles are used together with coverage mapping for
|
||||
`source-based code coverage`_. The `coverage mapping format`_ is different from
|
||||
profile format.
|
||||
```{note}
|
||||
Frontend-generated profiles are used together with coverage mapping for
|
||||
[source-based code coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html). The [coverage mapping format](https://llvm.org/docs/CoverageMappingFormat.html) is different from
|
||||
profile format.
|
||||
```
|
||||
|
||||
.. _`source-based code coverage`: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
|
||||
.. _`coverage mapping format`: https://llvm.org/docs/CoverageMappingFormat.html
|
||||
|
||||
Raw Profile Format
|
||||
===================
|
||||
## Raw Profile Format
|
||||
|
||||
The raw profile is generated by running the instrumented binary. The raw profile
|
||||
data from an executable or a shared library [3]_ consists of a header and
|
||||
data from an executable or a shared library [^3] consists of a header and
|
||||
multiple sections, with each section as a memory dump. The raw profile data needs
|
||||
to be reasonably compact and fast to generate.
|
||||
|
||||
There are no backward or forward version compatibility guarantees for the raw profile
|
||||
format. That is, compilers and tools `require`_ a specific raw profile version
|
||||
format. That is, compilers and tools [require](https://github.com/llvm/llvm-project/blob/bffdde8b8e5d9a76a47949cd0f574f3ce656e181/llvm/lib/ProfileData/InstrProfReader.cpp#L551-L558) a specific raw profile version
|
||||
to parse the profiles.
|
||||
|
||||
.. _`require`: https://github.com/llvm/llvm-project/blob/bffdde8b8e5d9a76a47949cd0f574f3ce656e181/llvm/lib/ProfileData/InstrProfReader.cpp#L551-L558
|
||||
|
||||
To feed profiles back into compilers for an optimized build (e.g., via
|
||||
``-fprofile-use`` for IR instrumentation), a raw profile must to be converted into
|
||||
`-fprofile-use` for IR instrumentation), a raw profile must to be converted into
|
||||
indexed format.
|
||||
|
||||
General Storage Layout
|
||||
-----------------------
|
||||
### General Storage Layout
|
||||
|
||||
The storage layout of raw profile data format is illustrated below. Basically,
|
||||
when the raw profile is read into an memory buffer, the actual byte offset of a
|
||||
section is inferred from the section's order in the layout and size information
|
||||
of all the sections ahead of it.
|
||||
|
||||
::
|
||||
```
|
||||
+----+-----------------------+
|
||||
| | Magic |
|
||||
| +-----------------------+
|
||||
| | Version |
|
||||
| +-----------------------+
|
||||
H | Size Info for |
|
||||
E | Section 1 |
|
||||
A +-----------------------+
|
||||
D | Size Info for |
|
||||
E | Section 2 |
|
||||
R +-----------------------+
|
||||
| | ... |
|
||||
| +-----------------------+
|
||||
| | Size Info for |
|
||||
| | Section N |
|
||||
+----+-----------------------+
|
||||
P | Section 1 |
|
||||
A +-----------------------+
|
||||
Y | Section 2 |
|
||||
L +-----------------------+
|
||||
O | ... |
|
||||
A +-----------------------+
|
||||
D | Section N |
|
||||
+----+-----------------------+
|
||||
```
|
||||
|
||||
+----+-----------------------+
|
||||
| | Magic |
|
||||
| +-----------------------+
|
||||
| | Version |
|
||||
| +-----------------------+
|
||||
H | Size Info for |
|
||||
E | Section 1 |
|
||||
A +-----------------------+
|
||||
D | Size Info for |
|
||||
E | Section 2 |
|
||||
R +-----------------------+
|
||||
| | ... |
|
||||
| +-----------------------+
|
||||
| | Size Info for |
|
||||
| | Section N |
|
||||
+----+-----------------------+
|
||||
P | Section 1 |
|
||||
A +-----------------------+
|
||||
Y | Section 2 |
|
||||
L +-----------------------+
|
||||
O | ... |
|
||||
A +-----------------------+
|
||||
D | Section N |
|
||||
+----+-----------------------+
|
||||
```{note}
|
||||
Sections might be padded to meet specific alignment requirements. For
|
||||
simplicity, header fields and data sections solely for padding purposes are
|
||||
omitted in the data layout graph above and the rest of this document.
|
||||
```
|
||||
|
||||
### Header
|
||||
|
||||
.. note::
|
||||
Sections might be padded to meet specific alignment requirements. For
|
||||
simplicity, header fields and data sections solely for padding purposes are
|
||||
omitted in the data layout graph above and the rest of this document.
|
||||
**`Magic`**
|
||||
Magic number encodes profile format (raw, indexed or text). For the raw format,
|
||||
the magic number also encodes the endianness (big or little) and C pointer
|
||||
size (4 or 8 bytes) of the platform on which the profile is generated.
|
||||
|
||||
Header
|
||||
-------
|
||||
A factory method reads the magic number to construct reader properly and returns
|
||||
error upon unrecognized format. Specifically, the factory method and raw profile
|
||||
reader implementation make sure that a raw profile file could be read back on
|
||||
a platform with the opposite endianness and/or the other C pointer size.
|
||||
|
||||
``Magic``
|
||||
Magic number encodes profile format (raw, indexed or text). For the raw format,
|
||||
the magic number also encodes the endianness (big or little) and C pointer
|
||||
size (4 or 8 bytes) of the platform on which the profile is generated.
|
||||
**`Version`**
|
||||
The lower 32 bits specify the actual version and the most significant 32 bits
|
||||
specify the variant types of the profile. IR-based instrumentation PGO and
|
||||
context-sensitive IR-based instrumentation PGO are two variant types.
|
||||
|
||||
A factory method reads the magic number to construct reader properly and returns
|
||||
error upon unrecognized format. Specifically, the factory method and raw profile
|
||||
reader implementation make sure that a raw profile file could be read back on
|
||||
a platform with the opposite endianness and/or the other C pointer size.
|
||||
**`BinaryIdsSize`**
|
||||
The byte size of [binary id] section.
|
||||
|
||||
``Version``
|
||||
The lower 32 bits specify the actual version and the most significant 32 bits
|
||||
specify the variant types of the profile. IR-based instrumentation PGO and
|
||||
context-sensitive IR-based instrumentation PGO are two variant types.
|
||||
**`NumData`**
|
||||
The number of profile metadata. The byte size of [profile metadata](#profile-metadata) section
|
||||
could be computed with this field.
|
||||
|
||||
``BinaryIdsSize``
|
||||
The byte size of `binary id`_ section.
|
||||
**`NumCounter`**
|
||||
The number of entries in the profile counter section. The byte size of [counter](#counter)
|
||||
section could be computed with this field.
|
||||
|
||||
``NumData``
|
||||
The number of profile metadata. The byte size of `profile metadata`_ section
|
||||
could be computed with this field.
|
||||
**`NumBitmapBytes`**
|
||||
The number of bytes in the profile [bitmap](#bitmap) section.
|
||||
|
||||
``NumCounter``
|
||||
The number of entries in the profile counter section. The byte size of `counter`_
|
||||
section could be computed with this field.
|
||||
**`NamesSize`**
|
||||
The number of bytes in the name section.
|
||||
|
||||
``NumBitmapBytes``
|
||||
The number of bytes in the profile `bitmap`_ section.
|
||||
(CountersDelta)=
|
||||
|
||||
``NamesSize``
|
||||
The number of bytes in the name section.
|
||||
**`CountersDelta`**
|
||||
This field records the in-memory address difference between the [profile metadata](#profile-metadata)
|
||||
and counter section in the instrumented binary, i.e., `start(__llvm_prf_cnts) - start(__llvm_prf_data)`.
|
||||
|
||||
.. _`CountersDelta`:
|
||||
It's used jointly with the [CounterPtr](#CounterPtr) field to compute the counter offset
|
||||
relative to `start(__llvm_prf_cnts)`. Check out [calculation-of-counter-offset](#calculation-of-counter-offset)
|
||||
for a visualized explanation.
|
||||
|
||||
``CountersDelta``
|
||||
This field records the in-memory address difference between the `profile metadata`_
|
||||
and counter section in the instrumented binary, i.e., ``start(__llvm_prf_cnts) - start(__llvm_prf_data)``.
|
||||
```{note}
|
||||
The `__llvm_prf_data` object file section might not be loaded into memory
|
||||
when instrumented binary runs or might not get generated in the instrumented
|
||||
binary in the first place. In those cases, `CountersDelta` is not used and
|
||||
other mechanisms are used to match counters with instrumented code. See
|
||||
[lightweight instrumentation] and [binary profile correlation] for examples.
|
||||
```
|
||||
|
||||
It's used jointly with the `CounterPtr`_ field to compute the counter offset
|
||||
relative to ``start(__llvm_prf_cnts)``. Check out calculation-of-counter-offset_
|
||||
for a visualized explanation.
|
||||
**`BitmapDelta`**
|
||||
This field records the in-memory address difference between the [profile metadata](#profile-metadata)
|
||||
and bitmap section in the instrumented binary, i.e., `start(__llvm_prf_bits) - start(__llvm_prf_data)`.
|
||||
|
||||
.. note::
|
||||
The ``__llvm_prf_data`` object file section might not be loaded into memory
|
||||
when instrumented binary runs or might not get generated in the instrumented
|
||||
binary in the first place. In those cases, ``CountersDelta`` is not used and
|
||||
other mechanisms are used to match counters with instrumented code. See
|
||||
`lightweight instrumentation`_ and `binary profile correlation`_ for examples.
|
||||
It's used jointly with the [BitmapPtr](#BitmapPtr) to find the bitmap of a profile data
|
||||
record, in a similar way to how counters are referenced as explained by
|
||||
[calculation-of-counter-offset](#calculation-of-counter-offset) .
|
||||
|
||||
``BitmapDelta``
|
||||
This field records the in-memory address difference between the `profile metadata`_
|
||||
and bitmap section in the instrumented binary, i.e., ``start(__llvm_prf_bits) - start(__llvm_prf_data)``.
|
||||
Similar to [CountersDelta](#CountersDelta) field, this field may not be used in non-PGO variants
|
||||
of profiles.
|
||||
|
||||
It's used jointly with the `BitmapPtr`_ to find the bitmap of a profile data
|
||||
record, in a similar way to how counters are referenced as explained by
|
||||
calculation-of-counter-offset_ .
|
||||
**`NamesDelta`**
|
||||
Records the in-memory address of name section. Not used except for raw profile
|
||||
reader error checking.
|
||||
|
||||
Similar to `CountersDelta`_ field, this field may not be used in non-PGO variants
|
||||
of profiles.
|
||||
**`NumVTables`**
|
||||
Records the number of instrumented vtable entries in the binary. Used for
|
||||
[type profiling].
|
||||
|
||||
``NamesDelta``
|
||||
Records the in-memory address of name section. Not used except for raw profile
|
||||
reader error checking.
|
||||
**`VNamesSize`**
|
||||
Records the byte size in the virtual table names section. Used for [type profiling].
|
||||
|
||||
``NumVTables``
|
||||
Records the number of instrumented vtable entries in the binary. Used for
|
||||
`type profiling`_.
|
||||
**`ValueKindLast`**
|
||||
Records the number of value kinds. Macro [VALUE_PROF_KIND](https://github.com/llvm/llvm-project/blob/7e405eb722e40c79b7726201d0f76b5dab34ba0f/compiler-rt/include/profile/InstrProfData.inc#L184-L186) defines the value
|
||||
kinds with a description of the kind.
|
||||
|
||||
``VNamesSize``
|
||||
Records the byte size in the virtual table names section. Used for `type profiling`_.
|
||||
### Payload Sections
|
||||
|
||||
``ValueKindLast``
|
||||
Records the number of value kinds. Macro `VALUE_PROF_KIND`_ defines the value
|
||||
kinds with a description of the kind.
|
||||
|
||||
.. _`VALUE_PROF_KIND`: https://github.com/llvm/llvm-project/blob/7e405eb722e40c79b7726201d0f76b5dab34ba0f/compiler-rt/include/profile/InstrProfData.inc#L184-L186
|
||||
|
||||
Payload Sections
|
||||
------------------
|
||||
|
||||
Binary Ids
|
||||
^^^^^^^^^^^
|
||||
#### Binary Ids
|
||||
Stores the binary ids of the instrumented binaries to associate binaries with
|
||||
profiles for source code coverage. See `binary id`_ RFC for the design.
|
||||
profiles for source code coverage. See [binary id] RFC for the design.
|
||||
|
||||
.. _`profile metadata`:
|
||||
(profile-metadata)=
|
||||
|
||||
Profile Metadata
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
#### Profile Metadata
|
||||
|
||||
This section stores the metadata to map counters and value profiles back to
|
||||
instrumented code regions (e.g., LLVM IR for IRPGO).
|
||||
|
||||
The in-memory representation of the metadata is `__llvm_profile_data`_.
|
||||
The in-memory representation of the metadata is [__llvm_profile_data](https://github.com/llvm/llvm-project/blob/7c3b67d2038cfb48a80299089f6a1308eee1df7f/compiler-rt/include/profile/InstrProfData.inc#L65-L95).
|
||||
Some fields are used to reference data from other sections in the profile.
|
||||
The fields are documented as follows:
|
||||
|
||||
.. _`__llvm_profile_data`: https://github.com/llvm/llvm-project/blob/7c3b67d2038cfb48a80299089f6a1308eee1df7f/compiler-rt/include/profile/InstrProfData.inc#L65-L95
|
||||
**`NameRef`**
|
||||
The MD5 of the function's PGO name. PGO name has the format
|
||||
`[<filepath><delimiter>]<mangled-name>` where `<filepath>` and
|
||||
`<delimiter>` are provided for local-linkage functions to tell possibly
|
||||
identical functions.
|
||||
|
||||
``NameRef``
|
||||
The MD5 of the function's PGO name. PGO name has the format
|
||||
``[<filepath><delimiter>]<mangled-name>`` where ``<filepath>`` and
|
||||
``<delimiter>`` are provided for local-linkage functions to tell possibly
|
||||
identical functions.
|
||||
(FuncHash)=
|
||||
|
||||
.. _FuncHash:
|
||||
**`FuncHash`**
|
||||
A checksum of the function's IR, taking control flow graph and instrumented
|
||||
value sites into account. See [computeCFGHash](https://github.com/llvm/llvm-project/blob/7c3b67d2038cfb48a80299089f6a1308eee1df7f/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp#L616-L685) for details.
|
||||
|
||||
``FuncHash``
|
||||
A checksum of the function's IR, taking control flow graph and instrumented
|
||||
value sites into account. See `computeCFGHash`_ for details.
|
||||
(CounterPtr)=
|
||||
|
||||
.. _`computeCFGHash`: https://github.com/llvm/llvm-project/blob/7c3b67d2038cfb48a80299089f6a1308eee1df7f/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp#L616-L685
|
||||
**`CounterPtr`**
|
||||
The in-memory address difference between profile data and the start of corresponding
|
||||
counters. Counter position is stored this way (as a link-time constant) to reduce
|
||||
instrumented binary size compared with snapshotting the address of symbols directly.
|
||||
See [commit a1532ed](https://github.com/llvm/llvm-project/commit/a1532ed27582038e2d9588108ba0fe8237f01844) for further information.
|
||||
|
||||
.. _`CounterPtr`:
|
||||
```{note}
|
||||
`CounterPtr` might represent a different value for non-IRPGO use cases. For
|
||||
example, for [binary profile correlation], it represents the absolute address of counter.
|
||||
When in doubt, check source code.
|
||||
```
|
||||
|
||||
``CounterPtr``
|
||||
The in-memory address difference between profile data and the start of corresponding
|
||||
counters. Counter position is stored this way (as a link-time constant) to reduce
|
||||
instrumented binary size compared with snapshotting the address of symbols directly.
|
||||
See `commit a1532ed`_ for further information.
|
||||
(BitmapPtr)=
|
||||
|
||||
.. _`commit a1532ed`: https://github.com/llvm/llvm-project/commit/a1532ed27582038e2d9588108ba0fe8237f01844
|
||||
**`BitmapPtr`**
|
||||
The in-memory address difference between profile data and the start address of
|
||||
corresponding bitmap.
|
||||
|
||||
.. note::
|
||||
``CounterPtr`` might represent a different value for non-IRPGO use cases. For
|
||||
example, for `binary profile correlation`_, it represents the absolute address of counter.
|
||||
When in doubt, check source code.
|
||||
```{note}
|
||||
Similar to [CounterPtr](#CounterPtr), this field may represent a different value for non-IRPGO use cases.
|
||||
```
|
||||
|
||||
.. _`BitmapPtr`:
|
||||
**`FunctionPointer`**
|
||||
Records the function address when instrumented binary runs. This is used to
|
||||
map the profiled callee address of indirect calls to the `NameRef` during
|
||||
conversion from raw to indexed profiles.
|
||||
|
||||
``BitmapPtr``
|
||||
The in-memory address difference between profile data and the start address of
|
||||
corresponding bitmap.
|
||||
**`Values`**
|
||||
Represents value profiles in a two dimensional array. The number of elements
|
||||
in the first dimension is the number of instrumented value sites across all
|
||||
kinds. Each element in the first dimension is the head of a linked list, and
|
||||
the each element in the second dimension is linked list element, carrying
|
||||
`<profiled-value, count>` as payload. This is used by compiler runtime when
|
||||
writing out value profiles.
|
||||
|
||||
.. note::
|
||||
Similar to `CounterPtr`_, this field may represent a different value for non-IRPGO use cases.
|
||||
```{note}
|
||||
Value profiling is supported by frontend and IR PGO instrumentation,
|
||||
but it's not supported in all cases (e.g., [lightweight instrumentation]).
|
||||
```
|
||||
|
||||
``FunctionPointer``
|
||||
Records the function address when instrumented binary runs. This is used to
|
||||
map the profiled callee address of indirect calls to the ``NameRef`` during
|
||||
conversion from raw to indexed profiles.
|
||||
**`NumCounters`**
|
||||
The number of counters for the instrumented function.
|
||||
|
||||
``Values``
|
||||
Represents value profiles in a two dimensional array. The number of elements
|
||||
in the first dimension is the number of instrumented value sites across all
|
||||
kinds. Each element in the first dimension is the head of a linked list, and
|
||||
the each element in the second dimension is linked list element, carrying
|
||||
``<profiled-value, count>`` as payload. This is used by compiler runtime when
|
||||
writing out value profiles.
|
||||
**`NumValueSites`**
|
||||
This is an array of counters, and each counter represents the number of
|
||||
instrumented sites for a kind of value in the function.
|
||||
|
||||
.. note::
|
||||
Value profiling is supported by frontend and IR PGO instrumentation,
|
||||
but it's not supported in all cases (e.g., `lightweight instrumentation`_).
|
||||
**`NumBitmapBytes`**
|
||||
The number of bitmap bytes for the function.
|
||||
|
||||
``NumCounters``
|
||||
The number of counters for the instrumented function.
|
||||
(counter)=
|
||||
|
||||
``NumValueSites``
|
||||
This is an array of counters, and each counter represents the number of
|
||||
instrumented sites for a kind of value in the function.
|
||||
#### Profile Counters
|
||||
|
||||
``NumBitmapBytes``
|
||||
The number of bitmap bytes for the function.
|
||||
|
||||
.. _`counter`:
|
||||
|
||||
Profile Counters
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
For PGO [4]_, the counters within an instrumented function of a specific `FuncHash`_
|
||||
For PGO [^4], the counters within an instrumented function of a specific [FuncHash](#FuncHash)
|
||||
are stored contiguously and in an order that is consistent with instrumentation points selection.
|
||||
|
||||
.. _calculation-of-counter-offset:
|
||||
(calculation-of-counter-offset)=
|
||||
|
||||
As mentioned above, the recorded counter offset is relative to the profile metadata.
|
||||
So how are function counters located in the raw profile data?
|
||||
|
||||
Basically, the profile reader iterates profile metadata (from the `profile metadata`_
|
||||
Basically, the profile reader iterates profile metadata (from the [profile metadata](#profile-metadata)
|
||||
section) and makes use of the recorded relative distances, as illustrated below.
|
||||
|
||||
::
|
||||
|
||||
+ --> start(__llvm_prf_data) --> +---------------------+ ------------+
|
||||
| | Data 1 | |
|
||||
| +---------------------+ =====|| |
|
||||
| | Data 2 | || |
|
||||
| +---------------------+ || |
|
||||
| | ... | || |
|
||||
Counter| +---------------------+ || |
|
||||
Delta | | Data N | || |
|
||||
| +---------------------+ || | CounterPtr1
|
||||
| || |
|
||||
| CounterPtr2 || |
|
||||
| || |
|
||||
| || |
|
||||
+ --> start(__llvm_prf_cnts) --> +---------------------+ || |
|
||||
| ... | || |
|
||||
+---------------------+ -----||----+
|
||||
| Counter for | ||
|
||||
| Data 1 | ||
|
||||
+---------------------+ ||
|
||||
| ... | ||
|
||||
+---------------------+ =====||
|
||||
| Counter for |
|
||||
| Data 2 |
|
||||
+---------------------+
|
||||
| ... |
|
||||
+---------------------+
|
||||
| Counter for |
|
||||
| Data N |
|
||||
+---------------------+
|
||||
|
||||
```
|
||||
+ --> start(__llvm_prf_data) --> +---------------------+ ------------+
|
||||
| | Data 1 | |
|
||||
| +---------------------+ =====|| |
|
||||
| | Data 2 | || |
|
||||
| +---------------------+ || |
|
||||
| | ... | || |
|
||||
Counter| +---------------------+ || |
|
||||
Delta | | Data N | || |
|
||||
| +---------------------+ || | CounterPtr1
|
||||
| || |
|
||||
| CounterPtr2 || |
|
||||
| || |
|
||||
| || |
|
||||
+ --> start(__llvm_prf_cnts) --> +---------------------+ || |
|
||||
| ... | || |
|
||||
+---------------------+ -----||----+
|
||||
| Counter for | ||
|
||||
| Data 1 | ||
|
||||
+---------------------+ ||
|
||||
| ... | ||
|
||||
+---------------------+ =====||
|
||||
| Counter for |
|
||||
| Data 2 |
|
||||
+---------------------+
|
||||
| ... |
|
||||
+---------------------+
|
||||
| Counter for |
|
||||
| Data N |
|
||||
+---------------------+
|
||||
```
|
||||
|
||||
In the graph,
|
||||
|
||||
* The profile header records ``CounterDelta`` with the value as ``start(__llvm_prf_cnts) - start(__llvm_prf_data)``.
|
||||
We will call it ``CounterDeltaInitVal`` below for convenience.
|
||||
* For each profile data record ``ProfileDataN``, ``CounterPtr`` is recorded as
|
||||
``start(CounterN) - start(ProfileDataN)``, where ``ProfileDataN`` is the N-th
|
||||
entry in ``__llvm_prf_data``, and ``CounterN`` represents the corresponding
|
||||
* The profile header records `CounterDelta` with the value as `start(__llvm_prf_cnts) - start(__llvm_prf_data)`.
|
||||
We will call it `CounterDeltaInitVal` below for convenience.
|
||||
* For each profile data record `ProfileDataN`, `CounterPtr` is recorded as
|
||||
`start(CounterN) - start(ProfileDataN)`, where `ProfileDataN` is the N-th
|
||||
entry in `__llvm_prf_data`, and `CounterN` represents the corresponding
|
||||
profile counters.
|
||||
|
||||
Each time the reader advances to the next data record, it `updates`_ ``CounterDelta``
|
||||
to minus the size of one ``ProfileData``.
|
||||
|
||||
.. _`updates`: https://github.com/llvm/llvm-project/blob/17ff25a58ee4f29816d932fdb75f0d305718069f/llvm/include/llvm/ProfileData/InstrProfReader.h#L439-L444
|
||||
Each time the reader advances to the next data record, it [updates](https://github.com/llvm/llvm-project/blob/17ff25a58ee4f29816d932fdb75f0d305718069f/llvm/include/llvm/ProfileData/InstrProfReader.h#L439-L444) `CounterDelta`
|
||||
to minus the size of one `ProfileData`.
|
||||
|
||||
For the counter corresponding to the first data record, the byte offset
|
||||
relative to the start of the counter section is calculated as ``CounterPtr1 - CounterDeltaInitVal``.
|
||||
When profile reader advances to the second data record, note ``CounterDelta``
|
||||
is updated to ``CounterDeltaInitVal - sizeof(ProfileData)``.
|
||||
relative to the start of the counter section is calculated as `CounterPtr1 - CounterDeltaInitVal`.
|
||||
When profile reader advances to the second data record, note `CounterDelta`
|
||||
is updated to `CounterDeltaInitVal - sizeof(ProfileData)`.
|
||||
Thus the byte offset relative to the start of the counter section is calculated
|
||||
as ``CounterPtr2 - (CounterDeltaInitVal - sizeof(ProfileData))``.
|
||||
as `CounterPtr2 - (CounterDeltaInitVal - sizeof(ProfileData))`.
|
||||
|
||||
.. _`bitmap`:
|
||||
(bitmap)=
|
||||
|
||||
Bitmap
|
||||
^^^^^^^
|
||||
This section is used for source-based `Modified Condition/Decision Coverage`_ code coverage. Check out `Bitmap RFC`_
|
||||
#### Bitmap
|
||||
This section is used for source-based [Modified Condition/Decision Coverage](https://en.wikipedia.org/wiki/Modified_condition/decision_coverage) code coverage. Check out [Bitmap RFC](https://discourse.llvm.org/t/rfc-source-based-mc-dc-code-coverage/59244)
|
||||
for the design.
|
||||
|
||||
.. _`Modified Condition/Decision Coverage`: https://en.wikipedia.org/wiki/Modified_condition/decision_coverage
|
||||
.. _`Bitmap RFC`: https://discourse.llvm.org/t/rfc-source-based-mc-dc-code-coverage/59244
|
||||
(function-names)=
|
||||
|
||||
.. _`function names`:
|
||||
|
||||
Names
|
||||
^^^^^^
|
||||
#### Names
|
||||
|
||||
This section contains possibly compressed concatenated string of functions' PGO
|
||||
names. If compressed, zlib library is used.
|
||||
|
||||
Function names serve as keys in the PGO data hash table when raw profiles are
|
||||
converted into indexed profiles. They are also crucial for ``llvm-profdata`` to
|
||||
converted into indexed profiles. They are also crucial for `llvm-profdata` to
|
||||
show the profiles in a human-readable way.
|
||||
|
||||
Virtual Table Profile Data
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### Virtual Table Profile Data
|
||||
|
||||
This section is used for `type profiling`_. Each entry corresponds to one virtual
|
||||
This section is used for [type profiling]. Each entry corresponds to one virtual
|
||||
table and is defined by the following C++ struct
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
struct VTableProfData {
|
||||
// The start address of the vtable, collected at runtime.
|
||||
uint64_t StartAddress;
|
||||
// The byte size of the vtable. `StartAddress` and `ByteSize` specifies an address range to look up.
|
||||
uint32_t ByteSize;
|
||||
// The hash of vtable's (PGO) name
|
||||
uint64_t MD5HashOfName;
|
||||
};
|
||||
```c++
|
||||
struct VTableProfData {
|
||||
// The start address of the vtable, collected at runtime.
|
||||
uint64_t StartAddress;
|
||||
// The byte size of the vtable. `StartAddress` and `ByteSize` specifies an address range to look up.
|
||||
uint32_t ByteSize;
|
||||
// The hash of vtable's (PGO) name
|
||||
uint64_t MD5HashOfName;
|
||||
};
|
||||
```
|
||||
|
||||
At profile use time, the compiler looks up a profiled address in the sorted vtable
|
||||
address ranges and maps the address to a specific vtable through hashed name.
|
||||
|
||||
Virtual Table Names
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
#### Virtual Table Names
|
||||
|
||||
This section is similar to `function names`_ section above, except it contains the PGO
|
||||
This section is similar to [function names](#function-names) section above, except it contains the PGO
|
||||
names of profiled virtual tables. It's a standalone section such that raw profile
|
||||
readers could directly find each name set by accessing the corresponding profile
|
||||
data section.
|
||||
|
|
@ -373,23 +345,19 @@ data section.
|
|||
This section is stored in raw profiles such that `llvm-profdata` could show the
|
||||
profiles in a human-readable way.
|
||||
|
||||
Value Profile Data
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
#### Value Profile Data
|
||||
|
||||
This section contains the profile data for value profiling.
|
||||
|
||||
The value profiles corresponding to a profile metadata are serialized contiguously
|
||||
as one record, and value profile records are stored in the same order as the
|
||||
respective profile data, such that a raw profile reader `advances`_ the pointer to
|
||||
profile data and the pointer to value profile records simultaneously [5]_ to find
|
||||
value profiles for a per function, per `FuncHash`_ profile data.
|
||||
respective profile data, such that a raw profile reader [advances](https://github.com/llvm/llvm-project/blob/7e15fa9161eda7497a5d6abf0d951a1d12d86550/llvm/include/llvm/ProfileData/InstrProfReader.h#L456-L457) the pointer to
|
||||
profile data and the pointer to value profile records simultaneously [^5] to find
|
||||
value profiles for a per function, per [FuncHash](#FuncHash) profile data.
|
||||
|
||||
.. _`advances`: https://github.com/llvm/llvm-project/blob/7e15fa9161eda7497a5d6abf0d951a1d12d86550/llvm/include/llvm/ProfileData/InstrProfReader.h#L456-L457
|
||||
## Indexed Profile Format
|
||||
|
||||
Indexed Profile Format
|
||||
===========================
|
||||
|
||||
Indexed profiles are generated from ``llvm-profdata``. In the indexed profiles,
|
||||
Indexed profiles are generated from `llvm-profdata`. In the indexed profiles,
|
||||
function data are organized as on-disk hash table such that compilers can
|
||||
look up profile data for functions in an IR module.
|
||||
|
||||
|
|
@ -397,135 +365,118 @@ Compilers and tools must retain backward compatibility with indexed profiles.
|
|||
That is, a tool or a compiler built at newer versions of code must understand
|
||||
profiles generated by older tools or compilers.
|
||||
|
||||
General Storage Layout
|
||||
-----------------------
|
||||
### General Storage Layout
|
||||
|
||||
The ASCII art depicts the general storage layout of indexed profiles.
|
||||
Specifically, the indexed profile header describes the byte offset of individual
|
||||
payload sections.
|
||||
|
||||
::
|
||||
```
|
||||
+-----------------------+---+
|
||||
| Magic | |
|
||||
+-----------------------+ |
|
||||
| Version | |
|
||||
+-----------------------+ |
|
||||
| HashType | H
|
||||
+-----------------------+ E
|
||||
| Byte Offset | A
|
||||
+------ | of section A | D
|
||||
| +-----------------------+ E
|
||||
| | Byte Of fset | R
|
||||
+-----------| of section B | |
|
||||
| | +-----------------------+ |
|
||||
| | | ... | |
|
||||
| | +-----------------------+ |
|
||||
| | | Byte Offset | |
|
||||
+---------------| of section Z | |
|
||||
| | | +-----------------------+---+
|
||||
| | | | Profile Summary | |
|
||||
| | | +-----------------------+ P
|
||||
| | +------>| Section A | A
|
||||
| | +-----------------------+ Y
|
||||
| +---------->| Section B | L
|
||||
| +-----------------------+ O
|
||||
| | ... | A
|
||||
| +-----------------------+ D
|
||||
+-------------->| Section Z | |
|
||||
+-----------------------+---+
|
||||
```
|
||||
|
||||
+-----------------------+---+
|
||||
| Magic | |
|
||||
+-----------------------+ |
|
||||
| Version | |
|
||||
+-----------------------+ |
|
||||
| HashType | H
|
||||
+-----------------------+ E
|
||||
| Byte Offset | A
|
||||
+------ | of section A | D
|
||||
| +-----------------------+ E
|
||||
| | Byte Of fset | R
|
||||
+-----------| of section B | |
|
||||
| | +-----------------------+ |
|
||||
| | | ... | |
|
||||
| | +-----------------------+ |
|
||||
| | | Byte Offset | |
|
||||
+---------------| of section Z | |
|
||||
| | | +-----------------------+---+
|
||||
| | | | Profile Summary | |
|
||||
| | | +-----------------------+ P
|
||||
| | +------>| Section A | A
|
||||
| | +-----------------------+ Y
|
||||
| +---------->| Section B | L
|
||||
| +-----------------------+ O
|
||||
| | ... | A
|
||||
| +-----------------------+ D
|
||||
+-------------->| Section Z | |
|
||||
+-----------------------+---+
|
||||
```{note}
|
||||
Profile summary section is at the beginning of payload. It's right after the
|
||||
header so its position is implicitly known after reading the header.
|
||||
```
|
||||
|
||||
.. note::
|
||||
### Header
|
||||
|
||||
Profile summary section is at the beginning of payload. It's right after the
|
||||
header so its position is implicitly known after reading the header.
|
||||
|
||||
Header
|
||||
--------
|
||||
|
||||
The `Header struct`_ is the source of truth and struct fields should explain
|
||||
The [Header struct](https://github.com/llvm/llvm-project/blob/1a2960bab6381f2b288328e2371829b460ac020c/llvm/include/llvm/ProfileData/InstrProf.h#L1053-L1080) is the source of truth and struct fields should explain
|
||||
what's in the header. At a high level, `*Offset` fields record section byte
|
||||
offsets, which are used by readers to locate interesting sections and skip
|
||||
uninteresting ones.
|
||||
|
||||
.. note::
|
||||
```{note}
|
||||
To maintain backward compatibility of the indexed profiles, existing fields
|
||||
shouldn't be deleted from struct definition; the field order shouldn't be
|
||||
modified. New fields should be appended.
|
||||
```
|
||||
|
||||
To maintain backward compatibility of the indexed profiles, existing fields
|
||||
shouldn't be deleted from struct definition; the field order shouldn't be
|
||||
modified. New fields should be appended.
|
||||
### Payload Sections
|
||||
|
||||
.. _`Header struct`: https://github.com/llvm/llvm-project/blob/1a2960bab6381f2b288328e2371829b460ac020c/llvm/include/llvm/ProfileData/InstrProf.h#L1053-L1080
|
||||
|
||||
|
||||
Payload Sections
|
||||
------------------
|
||||
|
||||
(CS) Profile Summary
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
#### (CS) Profile Summary
|
||||
This section is right after profile header. It stores the serialized profile
|
||||
summary. For context-sensitive IR-based instrumentation PGO, this section stores
|
||||
an additional profile summary corresponding to the context-sensitive profiles.
|
||||
|
||||
.. _`function data`:
|
||||
(function-data)=
|
||||
|
||||
Function data
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
#### Function data
|
||||
This section stores functions and their profiling data as an on-disk hash table.
|
||||
Profile data for functions with the same name are grouped together and share one
|
||||
hash table entry (the functions may come from different shared libraries for
|
||||
instance). The profile data for them are organized as a sequence of key-value
|
||||
pair where the key is `FuncHash`_, and the value is profiled information (represented
|
||||
by `InstrProfRecord`_) for the function.
|
||||
pair where the key is [FuncHash](#FuncHash), and the value is profiled information (represented
|
||||
by [InstrProfRecord](https://github.com/llvm/llvm-project/blob/7e405eb722e40c79b7726201d0f76b5dab34ba0f/llvm/include/llvm/ProfileData/InstrProf.h#L693)) for the function.
|
||||
|
||||
.. _`InstrProfRecord`: https://github.com/llvm/llvm-project/blob/7e405eb722e40c79b7726201d0f76b5dab34ba0f/llvm/include/llvm/ProfileData/InstrProf.h#L693
|
||||
|
||||
MemProf Profile data
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### MemProf Profile data
|
||||
This section stores function's memory profiling data. See
|
||||
`MemProf binary serialization format RFC`_ for the design.
|
||||
[MemProf binary serialization format RFC](https://lists.llvm.org/pipermail/llvm-dev/2021-September/153007.html) for the design.
|
||||
|
||||
.. _`MemProf binary serialization format RFC`: https://lists.llvm.org/pipermail/llvm-dev/2021-September/153007.html
|
||||
#### Binary Ids
|
||||
The section is used to carry on [binary id] information from raw profiles.
|
||||
|
||||
Binary Ids
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
The section is used to carry on `binary id`_ information from raw profiles.
|
||||
|
||||
Temporal Profile Traces
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### Temporal Profile Traces
|
||||
The section is used to carry on temporal profile information from raw profiles.
|
||||
See `temporal profiling`_ for the design.
|
||||
See [temporal profiling] for the design.
|
||||
|
||||
Virtual Table Names
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
#### Virtual Table Names
|
||||
This section is used to store the names of vtables from raw profile in the indexed
|
||||
profile.
|
||||
|
||||
Unlike function names which are stored as keys of `function data`_ hash table,
|
||||
Unlike function names which are stored as keys of [function data](#function-data) hash table,
|
||||
vtable names need to be stored in a standalone section in indexed profiles.
|
||||
This way, `llvm-profdata` could show the profiled vtable information in a
|
||||
human-readable way.
|
||||
|
||||
Profile Data Usage
|
||||
=======================================
|
||||
## Profile Data Usage
|
||||
|
||||
``llvm-profdata`` is the command line tool to display and process instrumentation-
|
||||
based profile data. For supported usages, check out `llvm-profdata documentation <https://llvm.org/docs/CommandGuide/llvm-profdata.html>`_.
|
||||
`llvm-profdata` is the command line tool to display and process instrumentation-
|
||||
based profile data. For supported usages, check out [llvm-profdata documentation](https://llvm.org/docs/CommandGuide/llvm-profdata.html).
|
||||
|
||||
.. [1] For usage, see https://clang.llvm.org/docs/UsersManual.html#profiling-with-instrumentation
|
||||
.. [2] For example, IR-based instrumentation supports `lightweight instrumentation`_
|
||||
and `temporal profiling`_. Frontend instrumentation could support `single-byte counters`_.
|
||||
.. [3] A raw profile file could contain the concatenation of multiple raw
|
||||
[^1]: For usage, see https://clang.llvm.org/docs/UsersManual.html#profiling-with-instrumentation
|
||||
[^2]: For example, IR-based instrumentation supports [lightweight instrumentation]
|
||||
and [temporal profiling]. Frontend instrumentation could support [single-byte counters].
|
||||
[^3]: A raw profile file could contain the concatenation of multiple raw
|
||||
profiles, for example, from an executable and its shared libraries. Raw
|
||||
profile reader could parse all raw profiles from the file correctly.
|
||||
.. [4] The counter section is used by a few variant types (like temporal
|
||||
[^4]: The counter section is used by a few variant types (like temporal
|
||||
profiling) and might have different semantics there.
|
||||
.. [5] The step size of data pointer is the ``sizeof(ProfileData)``, and the step
|
||||
[^5]: The step size of data pointer is the `sizeof(ProfileData)`, and the step
|
||||
size of value profile pointer is calculated based on the number of collected
|
||||
values.
|
||||
|
||||
.. _`lightweight instrumentation`: https://groups.google.com/g/llvm-dev/c/r03Z6JoN7d4
|
||||
.. _`temporal profiling`: https://discourse.llvm.org/t/rfc-temporal-profiling-extension-for-irpgo/68068
|
||||
.. _`single-byte counters`: https://discourse.llvm.org/t/rfc-single-byte-counters-for-source-based-code-coverage/75685
|
||||
.. _`binary profile correlation`: https://discourse.llvm.org/t/rfc-add-binary-profile-correlation-to-not-load-profile-metadata-sections-into-memory-at-runtime/74565
|
||||
.. _`binary id`: https://lists.llvm.org/pipermail/llvm-dev/2021-June/151154.html
|
||||
.. _`type profiling`: https://discourse.llvm.org/t/rfc-dynamic-type-profiling-and-optimizations-in-llvm/74600
|
||||
[lightweight instrumentation]: https://groups.google.com/g/llvm-dev/c/r03Z6JoN7d4
|
||||
[temporal profiling]: https://discourse.llvm.org/t/rfc-temporal-profiling-extension-for-irpgo/68068
|
||||
[single-byte counters]: https://discourse.llvm.org/t/rfc-single-byte-counters-for-source-based-code-coverage/75685
|
||||
[binary profile correlation]: https://discourse.llvm.org/t/rfc-add-binary-profile-correlation-to-not-load-profile-metadata-sections-into-memory-at-runtime/74565
|
||||
[binary id]: https://lists.llvm.org/pipermail/llvm-dev/2021-June/151154.html
|
||||
[type profiling]: https://discourse.llvm.org/t/rfc-dynamic-type-profiling-and-optimizations-in-llvm/74600
|
||||
|
|
|
|||
|
|
@ -1,22 +1,18 @@
|
|||
================
|
||||
The LLVM Lexicon
|
||||
================
|
||||
# The LLVM Lexicon
|
||||
|
||||
.. note::
|
||||
```{note}
|
||||
This document is a work in progress!
|
||||
```
|
||||
|
||||
This document is a work in progress!
|
||||
## Definitions
|
||||
|
||||
Definitions
|
||||
===========
|
||||
|
||||
A
|
||||
-
|
||||
### A
|
||||
|
||||
**ADCE**
|
||||
Aggressive Dead Code Elimination
|
||||
: Aggressive Dead Code Elimination
|
||||
|
||||
**AST**
|
||||
Abstract Syntax Tree.
|
||||
: Abstract Syntax Tree.
|
||||
|
||||
Due to Clang's influence (mostly the fact that parsing and semantic
|
||||
analysis are so intertwined for C and especially C++), the typical
|
||||
|
|
@ -30,31 +26,29 @@ A
|
|||
|
||||
Consult your favorite compiler book or search engine for more details.
|
||||
|
||||
B
|
||||
-
|
||||
### B
|
||||
|
||||
.. _lexicon-bb-vectorization:
|
||||
(lexicon-bb-vectorization)=
|
||||
|
||||
**BB Vectorization**
|
||||
Basic-Block Vectorization
|
||||
: Basic-Block Vectorization
|
||||
|
||||
**BDCE**
|
||||
Bit-tracking dead code elimination. Some bit-wise instructions (shifts,
|
||||
: Bit-tracking dead code elimination. Some bit-wise instructions (shifts,
|
||||
ands, ors, etc.) "kill" some of their input bits -- that is, they make it
|
||||
such that those bits can be either zero or one without affecting control or
|
||||
data flow of a program. The BDCE pass removes instructions that only
|
||||
compute these dead bits.
|
||||
|
||||
**BURS**
|
||||
Bottom Up Rewriting System --- A method of instruction selection for code
|
||||
generation. An example is the `BURG
|
||||
<http://www.program-transformation.org/Transform/BURG>`_ tool.
|
||||
: Bottom Up Rewriting System --- A method of instruction selection for code
|
||||
generation. An example is the
|
||||
[BURG](http://www.program-transformation.org/Transform/BURG) tool.
|
||||
|
||||
C
|
||||
-
|
||||
### C
|
||||
|
||||
**CFI**
|
||||
This abbreviation has two meanings.
|
||||
: This abbreviation has two meanings.
|
||||
Either:
|
||||
Call Frame Information. Used in DWARF debug info and in C++ unwind info
|
||||
to show how the function prologue lays out the stack frame.
|
||||
|
|
@ -65,132 +59,126 @@ C
|
|||
of execution (the control flow) of a program.
|
||||
|
||||
**CIE**
|
||||
Common Information Entry. A kind of CFI used to reduce the size of FDEs.
|
||||
: Common Information Entry. A kind of CFI used to reduce the size of FDEs.
|
||||
The compiler creates a CIE which contains the information common across all
|
||||
the FDEs. Each FDE then points to its CIE.
|
||||
|
||||
**CSE**
|
||||
Common Subexpression Elimination. An optimization that removes common
|
||||
subexpression computation. For example ``(a+b)*(a+b)`` has two
|
||||
subexpressions that are the same: ``(a+b)``. This optimization would
|
||||
: Common Subexpression Elimination. An optimization that removes common
|
||||
subexpression computation. For example `(a+b)*(a+b)` has two
|
||||
subexpressions that are the same: `(a+b)`. This optimization would
|
||||
perform the addition only once and then perform the multiply (but only if
|
||||
it's computationally correct/safe).
|
||||
|
||||
D
|
||||
-
|
||||
### D
|
||||
|
||||
**DAG**
|
||||
Directed Acyclic Graph
|
||||
: Directed Acyclic Graph
|
||||
|
||||
.. _derived pointer:
|
||||
.. _derived pointers:
|
||||
(derived pointer)=
|
||||
(derived pointers)=
|
||||
|
||||
**Derived Pointer**
|
||||
A pointer to the interior of an object, such that a garbage collector is
|
||||
: A pointer to the interior of an object, such that a garbage collector is
|
||||
unable to use the pointer for reachability analysis. While a derived pointer
|
||||
is live, the corresponding object pointer must be kept in a root, otherwise
|
||||
the collector might free the referenced object. With copying collectors,
|
||||
derived pointers pose an additional hazard that they may be invalidated at
|
||||
any `safe point`_. This term is used in opposition to `object pointer`_.
|
||||
any {ref}`safe point <safe point>`. This term is used in opposition to
|
||||
{ref}`object pointer <object pointer>`.
|
||||
|
||||
**DSA**
|
||||
Data Structure Analysis
|
||||
: Data Structure Analysis
|
||||
|
||||
**DSE**
|
||||
Dead Store Elimination
|
||||
: Dead Store Elimination
|
||||
|
||||
E
|
||||
-
|
||||
### E
|
||||
|
||||
**ento**
|
||||
This namespace houses the
|
||||
`Clang Static Analyzer <https://clang.llvm.org/docs/ClangStaticAnalyzer.html>`_.
|
||||
It is an abbreviation of `entomology <https://en.wikipedia.org/wiki/Entomology>`_.
|
||||
: This namespace houses the
|
||||
[Clang Static Analyzer](https://clang.llvm.org/docs/ClangStaticAnalyzer.html).
|
||||
It is an abbreviation of [entomology](https://en.wikipedia.org/wiki/Entomology).
|
||||
|
||||
*"Entomology is the scientific study of insects."*
|
||||
|
||||
In the past, this namespace had not only the name `GR` (aka. Graph Reachability)
|
||||
but also `entoSA`.
|
||||
In the past, this namespace had not only the name `GR` (aka. Graph
|
||||
Reachability) but also `entoSA`.
|
||||
|
||||
F
|
||||
-
|
||||
### F
|
||||
|
||||
**FCA**
|
||||
First Class Aggregate
|
||||
: First Class Aggregate
|
||||
|
||||
**FDE**
|
||||
Frame Description Entry. A kind of CFI used to describe the stack frame of
|
||||
: Frame Description Entry. A kind of CFI used to describe the stack frame of
|
||||
one function.
|
||||
|
||||
G
|
||||
-
|
||||
### G
|
||||
|
||||
**GC**
|
||||
Garbage Collection. The practice of using reachability analysis instead of
|
||||
: Garbage Collection. The practice of using reachability analysis instead of
|
||||
explicit memory management to reclaim unused memory.
|
||||
|
||||
**GEP**
|
||||
``GetElementPtr``. An LLVM IR instruction that is used to get the address
|
||||
: `GetElementPtr`. An LLVM IR instruction that is used to get the address
|
||||
of a subelement of an aggregate data structure. It is documented in detail
|
||||
`here <https://llvm.org/docs/GetElementPtr.html>`_.
|
||||
[here](https://llvm.org/docs/GetElementPtr.html).
|
||||
|
||||
**GVN**
|
||||
Global Value Numbering. GVN is a pass that partitions values computed by a
|
||||
: Global Value Numbering. GVN is a pass that partitions values computed by a
|
||||
function into congruence classes. Values ending up in the same congruence
|
||||
class are guaranteed to be the same for every execution of the program.
|
||||
In that respect, congruency is a compile-time approximation of equivalence
|
||||
of values at runtime.
|
||||
|
||||
H
|
||||
-
|
||||
### H
|
||||
|
||||
.. _heap:
|
||||
(heap)=
|
||||
|
||||
**Heap**
|
||||
In garbage collection, the region of memory which is managed using
|
||||
: In garbage collection, the region of memory which is managed using
|
||||
reachability analysis.
|
||||
|
||||
I
|
||||
-
|
||||
### I
|
||||
|
||||
**ICE**
|
||||
Internal Compiler Error. This abbreviation is used to describe errors
|
||||
: Internal Compiler Error. This abbreviation is used to describe errors
|
||||
that occur in LLVM or Clang as they are compiling source code. For example,
|
||||
if a valid C++ source program were to trigger an assert in Clang when
|
||||
compiled, that could be referred to as an "ICE".
|
||||
|
||||
**ICF**
|
||||
Identical Code Folding
|
||||
: Identical Code Folding
|
||||
|
||||
**ICP**
|
||||
Indirect Call Promotion
|
||||
: Indirect Call Promotion
|
||||
|
||||
**IPA**
|
||||
Inter-Procedural Analysis. Refers to any variety of code analysis that
|
||||
: Inter-Procedural Analysis. Refers to any variety of code analysis that
|
||||
occurs between procedures, functions or compilation units (modules).
|
||||
|
||||
**IPO**
|
||||
Inter-Procedural Optimization. Refers to any variety of code optimization
|
||||
: Inter-Procedural Optimization. Refers to any variety of code optimization
|
||||
that occurs between procedures, functions or compilation units (modules).
|
||||
|
||||
**ISel**
|
||||
Instruction Selection
|
||||
: Instruction Selection
|
||||
|
||||
L
|
||||
-
|
||||
### L
|
||||
|
||||
**LCSSA**
|
||||
Loop-Closed Static Single Assignment Form
|
||||
: Loop-Closed Static Single Assignment Form
|
||||
|
||||
**LGTM**
|
||||
"Looks Good To Me". In a review thread, this indicates that the
|
||||
: "Looks Good To Me". In a review thread, this indicates that the
|
||||
reviewer thinks that the patch is okay to commit.
|
||||
|
||||
**LICM**
|
||||
Loop Invariant Code Motion
|
||||
: Loop Invariant Code Motion
|
||||
|
||||
**LSDA**
|
||||
Language Specific Data Area. C++ "zero cost" unwinding is built on top a
|
||||
: Language Specific Data Area. C++ "zero cost" unwinding is built on top a
|
||||
generic unwinding mechanism. As the unwinder walks each frame, it calls
|
||||
a "personality" function to do language-specific analysis. Each function's
|
||||
FDE points to an optional LSDA which is passed to the personality function.
|
||||
|
|
@ -198,115 +186,114 @@ L
|
|||
statements in that function.
|
||||
|
||||
**Load-VN**
|
||||
Load Value Numbering
|
||||
: Load Value Numbering
|
||||
|
||||
**LTO**
|
||||
Link-Time Optimization
|
||||
: Link-Time Optimization
|
||||
|
||||
M
|
||||
-
|
||||
### M
|
||||
|
||||
**MC**
|
||||
Machine Code
|
||||
: Machine Code
|
||||
|
||||
N
|
||||
-
|
||||
.. _nfc:
|
||||
### N
|
||||
|
||||
(nfc)=
|
||||
|
||||
**NFC**
|
||||
"No functional change". Used in a commit message to indicate that a patch
|
||||
is a pure refactoring/cleanup.
|
||||
Usually used in the first line, so it is visible without opening the
|
||||
actual commit email.
|
||||
: "No functional change". Used in a commit message to indicate that a patch
|
||||
is a pure refactoring/cleanup.
|
||||
Usually used in the first line, so it is visible without opening the
|
||||
actual commit email.
|
||||
|
||||
O
|
||||
-
|
||||
.. _object pointer:
|
||||
.. _object pointers:
|
||||
### O
|
||||
|
||||
(object pointer)=
|
||||
(object pointers)=
|
||||
|
||||
**Object Pointer**
|
||||
A pointer to an object such that the garbage collector is able to trace
|
||||
: A pointer to an object such that the garbage collector is able to trace
|
||||
references contained within the object. This term is used in opposition to
|
||||
`derived pointer`_.
|
||||
{ref}`derived pointer <derived pointer>`.
|
||||
|
||||
P
|
||||
-
|
||||
### P
|
||||
|
||||
**PGO**
|
||||
Profile-Guided Optimization
|
||||
: Profile-Guided Optimization
|
||||
|
||||
**PR**
|
||||
Problem report. A bug filed on `the LLVM Bug Tracking System
|
||||
<https://bugs.llvm.org/enter_bug.cgi>`_.
|
||||
: Problem report. A bug filed on
|
||||
[the LLVM Bug Tracking System](https://bugs.llvm.org/enter_bug.cgi).
|
||||
|
||||
**PRE**
|
||||
Partial Redundancy Elimination
|
||||
: Partial Redundancy Elimination
|
||||
|
||||
R
|
||||
-
|
||||
### R
|
||||
|
||||
**RAUW**
|
||||
|
||||
Replace All Uses With. The functions ``User::replaceUsesOfWith()``,
|
||||
``Value::replaceAllUsesWith()``, and
|
||||
``Constant::replaceUsesOfWithOnConstant()`` implement the replacement of one
|
||||
: Replace All Uses With. The functions `User::replaceUsesOfWith()`,
|
||||
`Value::replaceAllUsesWith()`, and
|
||||
`Constant::replaceUsesOfWithOnConstant()` implement the replacement of one
|
||||
Value with another by iterating over its def/use chain and fixing up all of
|
||||
the pointers to point to the new value. See
|
||||
also `def/use chains <ProgrammersManual.html#iterating-over-def-use-use-def-chains>`_.
|
||||
also
|
||||
{ref}`def/use chains <iterate_chains>`.
|
||||
|
||||
**Reassociation**
|
||||
Rearranging associative expressions to promote better redundancy elimination
|
||||
and other optimizations. For example, changing ``(A+B-A)`` into ``(B+A-A)``,
|
||||
permitting it to be optimized into ``(B+0)`` then ``(B)``.
|
||||
: Rearranging associative expressions to promote better redundancy elimination
|
||||
and other optimizations. For example, changing `(A+B-A)` into `(B+A-A)`,
|
||||
permitting it to be optimized into `(B+0)` then `(B)`.
|
||||
|
||||
**RFC**
|
||||
Request for Comment. An email sent to a project mailing list in order to
|
||||
solicit feedback on a proposed change. See also: the :doc:`RFC process <RFCProcess>`
|
||||
documentation.
|
||||
: Request for Comment. An email sent to a project mailing list in order to
|
||||
solicit feedback on a proposed change. See also: the {doc}`RFC process
|
||||
<RFCProcess>` documentation.
|
||||
|
||||
.. _roots:
|
||||
.. _stack roots:
|
||||
(roots)=
|
||||
(stack roots)=
|
||||
|
||||
**Root**
|
||||
In garbage collection, a pointer variable lying outside of the `heap`_ from
|
||||
: In garbage collection, a pointer variable lying outside of the
|
||||
{ref}`heap <heap>` from
|
||||
which the collector begins its reachability analysis. In the context of code
|
||||
generation, "root" almost always refers to a "stack root" --- a local or
|
||||
temporary variable within an executing function.
|
||||
|
||||
**RPO**
|
||||
Reverse postorder
|
||||
: Reverse postorder
|
||||
|
||||
**RTTI**
|
||||
Run-time Type Information
|
||||
: Run-time Type Information
|
||||
|
||||
S
|
||||
-
|
||||
### S
|
||||
|
||||
.. _safe point:
|
||||
(safe point)=
|
||||
|
||||
**Safe Point**
|
||||
In garbage collection, it is necessary to identify `stack roots`_ so that
|
||||
: In garbage collection, it is necessary to identify
|
||||
{ref}`stack roots <stack roots>` so that
|
||||
reachability analysis may proceed. It may be infeasible to provide this
|
||||
information for every instruction, so instead the information is
|
||||
calculated only at designated safe points. With a copying collector,
|
||||
`derived pointers`_ must not be retained across safe points and `object
|
||||
pointers`_ must be reloaded from stack roots.
|
||||
{ref}`derived pointers <derived pointers>` must not be retained across safe
|
||||
points and {ref}`object pointers <object pointers>` must be reloaded from
|
||||
stack roots.
|
||||
|
||||
**SDISel**
|
||||
Selection DAG Instruction Selection.
|
||||
: Selection DAG Instruction Selection.
|
||||
|
||||
**SCC**
|
||||
Strongly Connected Component
|
||||
: Strongly Connected Component
|
||||
|
||||
**SCCP**
|
||||
Sparse Conditional Constant Propagation
|
||||
: Sparse Conditional Constant Propagation
|
||||
|
||||
**SLP**
|
||||
Superword-Level Parallelism, same as :ref:`Basic-Block Vectorization
|
||||
: Superword-Level Parallelism, same as {ref}`Basic-Block Vectorization
|
||||
<lexicon-bb-vectorization>`.
|
||||
|
||||
**Splat**
|
||||
Splat refers to a vector of identical scalar elements.
|
||||
: Splat refers to a vector of identical scalar elements.
|
||||
|
||||
The term is based on the PowerPC Altivec instructions that provided
|
||||
this functionality in hardware. For example, "vsplth" and the corresponding
|
||||
|
|
@ -314,25 +301,23 @@ S
|
|||
action include "duplicate" (ARM) and "broadcast" (x86).
|
||||
|
||||
**SRoA**
|
||||
Scalar Replacement of Aggregates
|
||||
: Scalar Replacement of Aggregates
|
||||
|
||||
**SSA**
|
||||
Static Single Assignment
|
||||
: Static Single Assignment
|
||||
|
||||
**Stack Map**
|
||||
In garbage collection, metadata emitted by the code generator which
|
||||
identifies `roots`_ within the stack frame of an executing function.
|
||||
: In garbage collection, metadata emitted by the code generator which
|
||||
identifies {ref}`roots <roots>` within the stack frame of an executing
|
||||
function.
|
||||
|
||||
T
|
||||
-
|
||||
### T
|
||||
|
||||
**TBAA**
|
||||
Type-Based Alias Analysis
|
||||
: Type-Based Alias Analysis
|
||||
|
||||
|
||||
W
|
||||
-
|
||||
### W
|
||||
|
||||
**WPD**
|
||||
Whole Program Devirtualization
|
||||
|
||||
: Whole Program Devirtualization
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +1,14 @@
|
|||
.. _loop-terminology:
|
||||
(loop-terminology)=
|
||||
# LLVM Loop Terminology (and Canonical Forms)
|
||||
|
||||
===========================================
|
||||
LLVM Loop Terminology (and Canonical Forms)
|
||||
===========================================
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Loop Definition
|
||||
===============
|
||||
## Loop Definition
|
||||
|
||||
Loops are an important concept for a code optimizer. In LLVM, detection
|
||||
of loops in a control-flow graph is done by :ref:`loopinfo`. It is based
|
||||
of loops in a control-flow graph is done by {ref}`loopinfo`. It is based
|
||||
on the following definition.
|
||||
|
||||
A loop is a subset of nodes from the control-flow graph (CFG; where
|
||||
|
|
@ -33,11 +30,10 @@ nodes represent basic blocks) with the following properties:
|
|||
|
||||
In computer science literature, this is often called a *natural loop*.
|
||||
In LLVM, a more generalized definition is called a
|
||||
:ref:`cycle <cycle-terminology>`.
|
||||
{ref}`cycle <cycle-terminology>`.
|
||||
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
### Terminology
|
||||
|
||||
The definition of a loop comes with some additional terminology:
|
||||
|
||||
|
|
@ -55,12 +51,12 @@ The definition of a loop comes with some additional terminology:
|
|||
of the loop. The source of such an edge is called an **exiting block**, its
|
||||
target is an **exit block**.
|
||||
|
||||
.. image:: ./loop-terminology.svg
|
||||
:width: 400 px
|
||||
```{image} ./loop-terminology.svg
|
||||
:width: 400 px
|
||||
```
|
||||
|
||||
|
||||
Important Notes
|
||||
---------------
|
||||
### Important Notes
|
||||
|
||||
This loop definition has some noteworthy consequences:
|
||||
|
||||
|
|
@ -80,25 +76,26 @@ This loop definition has some noteworthy consequences:
|
|||
A single block that has no branch to itself is not considered a loop,
|
||||
even though it is trivially strongly connected.
|
||||
|
||||
.. image:: ./loop-single.svg
|
||||
:width: 300 px
|
||||
```{image} ./loop-single.svg
|
||||
:width: 300 px
|
||||
```
|
||||
|
||||
In this case, the role of header, exiting block and latch fall to the
|
||||
same node. :ref:`loopinfo` reports this as:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ opt input.ll -passes='print<loops>'
|
||||
Loop at depth 1 containing: %for.body<header><latch><exiting>
|
||||
same node. {ref}`loopinfo` reports this as:
|
||||
|
||||
```console
|
||||
$ opt input.ll -passes='print<loops>'
|
||||
Loop at depth 1 containing: %for.body<header><latch><exiting>
|
||||
```
|
||||
|
||||
* Loops can be nested inside each other. That is, a loop's node set can
|
||||
be a subset of another loop with a different loop header. The loop
|
||||
hierarchy in a function forms a forest: Each top-level loop is the
|
||||
root of the tree of the loops nested inside it.
|
||||
|
||||
.. image:: ./loop-nested.svg
|
||||
:width: 350 px
|
||||
```{image} ./loop-nested.svg
|
||||
:width: 350 px
|
||||
```
|
||||
|
||||
|
||||
* It is not possible that two loops share only a few of their nodes.
|
||||
|
|
@ -106,43 +103,47 @@ same node. :ref:`loopinfo` reports this as:
|
|||
the example below the left and right subsets both violate the
|
||||
maximality condition. Only the merge of both sets is considered a loop.
|
||||
|
||||
.. image:: ./loop-nonmaximal.svg
|
||||
:width: 250 px
|
||||
```{image} ./loop-nonmaximal.svg
|
||||
:width: 250 px
|
||||
```
|
||||
|
||||
|
||||
* It is also possible that two logical loops share a header, but are
|
||||
considered a single loop by LLVM:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (int i = 0; i < 128; ++i)
|
||||
for (int j = 0; j < 128; ++j)
|
||||
body(i,j);
|
||||
```c
|
||||
for (int i = 0; i < 128; ++i)
|
||||
for (int j = 0; j < 128; ++j)
|
||||
body(i,j);
|
||||
```
|
||||
|
||||
which might be represented in LLVM-IR as follows. Note that there is
|
||||
only a single header and hence just a single loop.
|
||||
|
||||
.. image:: ./loop-merge.svg
|
||||
:width: 400 px
|
||||
```{image} ./loop-merge.svg
|
||||
:width: 400 px
|
||||
```
|
||||
|
||||
The :ref:`LoopSimplify <loop-terminology-loop-simplify>` pass will
|
||||
The {ref}`LoopSimplify <loop-terminology-loop-simplify>` pass will
|
||||
detect the loop and ensure separate headers for the outer and inner loop.
|
||||
|
||||
.. image:: ./loop-separate.svg
|
||||
:width: 400 px
|
||||
```{image} ./loop-separate.svg
|
||||
:width: 400 px
|
||||
```
|
||||
|
||||
* A cycle in the CFG does not imply there is a loop. The example below
|
||||
shows such a CFG, where there is no header node that dominates all
|
||||
other nodes in the cycle. This is called **irreducible control-flow**.
|
||||
|
||||
.. image:: ./loop-irreducible.svg
|
||||
:width: 150 px
|
||||
```{image} ./loop-irreducible.svg
|
||||
:width: 150 px
|
||||
```
|
||||
|
||||
The term reducible results from the ability to collapse the CFG into a
|
||||
single node by successively replacing one of three base structures with
|
||||
a single node: A sequential execution of basic blocks, acyclic conditional
|
||||
branches (or switches), and a basic block looping on itself.
|
||||
`Wikipedia <https://en.wikipedia.org/wiki/Control-flow_graph#Reducibility>`_
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/Control-flow_graph#Reducibility)
|
||||
has a more formal definition, which basically says that every cycle has
|
||||
a dominating header.
|
||||
|
||||
|
|
@ -152,11 +153,11 @@ a dominating header.
|
|||
cyclic control flow in its body; a loop that is not nested inside
|
||||
another loop can still be part of an outer cycle; and there can be
|
||||
additional cycles between any two loops where one is contained in the other.
|
||||
However, an LLVM :ref:`cycle<cycle-terminology>` covers both, loops and
|
||||
However, an LLVM {ref}`cycle <cycle-terminology>` covers both, loops and
|
||||
irreducible control flow.
|
||||
|
||||
|
||||
* The `FixIrreducible <https://llvm.org/doxygen/FixIrreducible_8h.html>`_
|
||||
* The [FixIrreducible](https://llvm.org/doxygen/FixIrreducible_8h.html)
|
||||
pass can transform irreducible control flow into loops by inserting
|
||||
new loop headers. It is not included in any default optimization pass
|
||||
pipeline, but is required for some back-end targets.
|
||||
|
|
@ -171,31 +172,30 @@ a dominating header.
|
|||
loop (i.e. to a latch or header) is not considered part of the loop.
|
||||
This is illustrated by the following code.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (unsigned i = 0; i <= n; ++i) {
|
||||
if (c1) {
|
||||
// When reaching this block, we will have exited the loop.
|
||||
```c
|
||||
for (unsigned i = 0; i <= n; ++i) {
|
||||
if (c1) {
|
||||
// When reaching this block, we will have exited the loop.
|
||||
do_something();
|
||||
break;
|
||||
}
|
||||
if (c2) {
|
||||
// abort(), never returns, so we have exited the loop.
|
||||
abort();
|
||||
}
|
||||
if (c3) {
|
||||
// The unreachable allows the compiler to assume that this will not rejoin the loop.
|
||||
do_something();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
if (c4) {
|
||||
// This statically infinite loop is not nested because control-flow will not continue with the for-loop.
|
||||
while(true) {
|
||||
do_something();
|
||||
break;
|
||||
}
|
||||
if (c2) {
|
||||
// abort(), never returns, so we have exited the loop.
|
||||
abort();
|
||||
}
|
||||
if (c3) {
|
||||
// The unreachable allows the compiler to assume that this will not rejoin the loop.
|
||||
do_something();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
if (c4) {
|
||||
// This statically infinite loop is not nested because control-flow will not continue with the for-loop.
|
||||
while(true) {
|
||||
do_something();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
* There is no requirement for the control flow to eventually leave the
|
||||
loop, i.e. a loop can be infinite. A **statically infinite loop** is a
|
||||
|
|
@ -204,10 +204,10 @@ a dominating header.
|
|||
only under some circumstances, such as when n == UINT_MAX in the code
|
||||
below.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (unsigned i = 0; i <= n; ++i)
|
||||
body(i);
|
||||
```c
|
||||
for (unsigned i = 0; i <= n; ++i)
|
||||
body(i);
|
||||
```
|
||||
|
||||
It is possible for the optimizer to turn a dynamically infinite loop
|
||||
into a statically infinite loop, for instance when it can prove that the
|
||||
|
|
@ -216,46 +216,46 @@ taken, the optimizer can change the conditional branch into an
|
|||
unconditional one.
|
||||
|
||||
If a is loop is annotated with
|
||||
:ref:`llvm.loop.mustprogress <langref_llvm_loop_mustprogress>` metadata,
|
||||
{ref}`llvm.loop.mustprogress <langref_llvm_loop_mustprogress>` metadata,
|
||||
the compiler is allowed to assume that it will eventually terminate, even
|
||||
if it cannot prove it. For instance, it may remove a mustprogress-loop
|
||||
that does not have any side-effect in its body even though the program
|
||||
could be stuck in that loop forever. Languages such as C and
|
||||
`C++ <https://eel.is/c++draft/intro.progress#1>`_ have such
|
||||
[C++](https://eel.is/c++draft/intro.progress#1) have such
|
||||
forward-progress guarantees for some loops. Also see the
|
||||
:ref:`mustprogress <langref_mustprogress>` and
|
||||
:ref:`willreturn <langref_willreturn>` function attributes, as well as
|
||||
the older :ref:`llvm.sideeffect <llvm_sideeffect>` intrinsic.
|
||||
{ref}`mustprogress <langref_mustprogress>` and
|
||||
{ref}`willreturn <langref_willreturn>` function attributes, as well as
|
||||
the older {ref}`llvm.sideeffect <llvm_sideeffect>` intrinsic.
|
||||
|
||||
* The number of executions of the loop header before leaving the loop is
|
||||
the **loop trip count** (or **iteration count**). If the loop should
|
||||
not be executed at all, a **loop guard** must skip the entire loop:
|
||||
|
||||
.. image:: ./loop-guard.svg
|
||||
:width: 500 px
|
||||
```{image} ./loop-guard.svg
|
||||
:width: 500 px
|
||||
```
|
||||
|
||||
Since the first thing a loop header might do is to check whether there
|
||||
is another execution and if not, immediately exit without doing any work
|
||||
(also see :ref:`loop-terminology-loop-rotate`), loop trip count is not
|
||||
(also see {ref}`loop-terminology-loop-rotate`), loop trip count is not
|
||||
the best measure of a loop's number of iterations. For instance, the
|
||||
number of header executions of the code below for a non-positive n
|
||||
(before loop rotation) is 1, even though the loop body is not executed
|
||||
at all.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
body(i);
|
||||
```c
|
||||
for (int i = 0; i < n; ++i)
|
||||
body(i);
|
||||
```
|
||||
|
||||
A better measure is the **backedge-taken count**, which is the number of
|
||||
times any of the backedges is taken before the loop. It is one less than
|
||||
the trip count for executions that enter the header.
|
||||
|
||||
|
||||
.. _loopinfo:
|
||||
(loopinfo)=
|
||||
|
||||
LoopInfo
|
||||
========
|
||||
## LoopInfo
|
||||
|
||||
LoopInfo is the core analysis for obtaining information about loops.
|
||||
There are few key implications of the definitions given above which
|
||||
|
|
@ -275,18 +275,17 @@ are important for working successfully with this interface.
|
|||
reachability of the loop.
|
||||
|
||||
|
||||
.. _loop-terminology-loop-simplify:
|
||||
(loop-terminology-loop-simplify)=
|
||||
|
||||
Loop Simplify Form
|
||||
==================
|
||||
## Loop Simplify Form
|
||||
|
||||
The Loop Simplify Form is a canonical form that makes
|
||||
several analyses and transformations simpler and more effective.
|
||||
It is ensured by the LoopSimplify
|
||||
(:ref:`-loop-simplify <passes-loop-simplify>`) pass and is automatically
|
||||
({ref}`-loop-simplify <passes-loop-simplify>`) pass and is automatically
|
||||
added by the pass managers when scheduling a LoopPass.
|
||||
This pass is implemented in
|
||||
`LoopSimplify.h <https://llvm.org/doxygen/LoopSimplify_8h_source.html>`_.
|
||||
[LoopSimplify.h](https://llvm.org/doxygen/LoopSimplify_8h_source.html).
|
||||
When it is successful, the loop has:
|
||||
|
||||
* A preheader.
|
||||
|
|
@ -295,10 +294,9 @@ When it is successful, the loop has:
|
|||
has a predecessor that is outside the loop. This implies
|
||||
that all exit blocks are dominated by the loop header.
|
||||
|
||||
.. _loop-terminology-lcssa:
|
||||
(loop-terminology-lcssa)=
|
||||
|
||||
Loop Closed SSA (LCSSA)
|
||||
=======================
|
||||
## Loop Closed SSA (LCSSA)
|
||||
|
||||
A program is in Loop Closed SSA Form if it is in SSA form
|
||||
and all values that are defined in a loop are used only inside
|
||||
|
|
@ -307,52 +305,52 @@ this loop.
|
|||
Programs written in LLVM IR are always in SSA form but not necessarily
|
||||
in LCSSA. To achieve the latter, for each value that is live across the
|
||||
loop boundary, single entry PHI nodes are inserted to each of the exit blocks
|
||||
[#lcssa-construction]_ in order to "close" these values inside the loop.
|
||||
[^lcssa-construction] in order to "close" these values inside the loop.
|
||||
In particular, consider the following loop:
|
||||
|
||||
.. code-block:: C
|
||||
```c
|
||||
c = ...;
|
||||
for (...) {
|
||||
if (c)
|
||||
X1 = ...
|
||||
else
|
||||
X2 = ...
|
||||
X3 = phi(X1, X2); // X3 defined
|
||||
}
|
||||
|
||||
c = ...;
|
||||
for (...) {
|
||||
if (c)
|
||||
X1 = ...
|
||||
else
|
||||
X2 = ...
|
||||
X3 = phi(X1, X2); // X3 defined
|
||||
}
|
||||
|
||||
... = X3 + 4; // X3 used, i.e. live
|
||||
// outside the loop
|
||||
... = X3 + 4; // X3 used, i.e. live
|
||||
// outside the loop
|
||||
```
|
||||
|
||||
In the inner loop, the X3 is defined inside the loop, but used
|
||||
outside of it. In Loop Closed SSA form, this would be represented as follows:
|
||||
|
||||
.. code-block:: C
|
||||
```c
|
||||
c = ...;
|
||||
for (...) {
|
||||
if (c)
|
||||
X1 = ...
|
||||
else
|
||||
X2 = ...
|
||||
X3 = phi(X1, X2);
|
||||
}
|
||||
X4 = phi(X3);
|
||||
|
||||
c = ...;
|
||||
for (...) {
|
||||
if (c)
|
||||
X1 = ...
|
||||
else
|
||||
X2 = ...
|
||||
X3 = phi(X1, X2);
|
||||
}
|
||||
X4 = phi(X3);
|
||||
|
||||
... = X4 + 4;
|
||||
... = X4 + 4;
|
||||
```
|
||||
|
||||
This is still valid LLVM; the extra phi nodes are purely redundant,
|
||||
but all LoopPass'es are required to preserve them.
|
||||
This form is ensured by the LCSSA (:ref:`-lcssa <passes-lcssa>`)
|
||||
This form is ensured by the LCSSA ({ref}`-lcssa <passes-lcssa>`)
|
||||
pass and is added automatically by the LoopPassManager when
|
||||
scheduling a LoopPass.
|
||||
After the loop optimizations are done, these extra phi nodes
|
||||
will be deleted by :ref:`-instcombine <passes-instcombine>`.
|
||||
will be deleted by {ref}`-instcombine <passes-instcombine>`.
|
||||
|
||||
Note that an exit block is outside of a loop, so how can such a phi "close"
|
||||
the value inside the loop since it uses it outside of it ? First of all,
|
||||
for phi nodes, as
|
||||
`mentioned in the LangRef <https://llvm.org/docs/LangRef.html#phi-instruction>`_:
|
||||
[mentioned in the LangRef](https://llvm.org/docs/LangRef.html#phi-instruction):
|
||||
"the use of each incoming value is deemed to occur on the edge from the
|
||||
corresponding predecessor block to the current block". Now, an
|
||||
edge to an exit block is considered outside of the loop because
|
||||
|
|
@ -362,7 +360,7 @@ However, an edge doesn't actually contain any IR, so in source code,
|
|||
we have to choose a convention of whether the use happens in
|
||||
the current block or in the respective predecessor. For LCSSA's purpose,
|
||||
we consider the use happens in the latter (so as to consider the
|
||||
use inside) [#point-of-use-phis]_.
|
||||
use inside) [^point-of-use-phis].
|
||||
|
||||
The major benefit of LCSSA is that it makes many other loop optimizations
|
||||
simpler.
|
||||
|
|
@ -370,37 +368,37 @@ simpler.
|
|||
First of all, a simple observation is that if one needs to see all
|
||||
the outside users, they can just iterate over all the (loop closing)
|
||||
PHI nodes in the exit blocks (the alternative would be to
|
||||
scan the def-use chain [#def-use-chain]_ of all instructions in the loop).
|
||||
scan the def-use chain [^def-use-chain] of all instructions in the loop).
|
||||
|
||||
Then, consider for example
|
||||
:ref:`simple-loop-unswitch <passes-simple-loop-unswitch>` ing the loop above.
|
||||
{ref}`simple-loop-unswitch <passes-simple-loop-unswitch>` ing the loop above.
|
||||
Because it is in LCSSA form, we know that any value defined inside of
|
||||
the loop will be used either only inside the loop or in a loop closing
|
||||
PHI node. In this case, the only loop closing PHI node is X4.
|
||||
This means that we can just copy the loop and change the X4
|
||||
accordingly, like so:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
c = ...;
|
||||
if (c) {
|
||||
for (...) {
|
||||
if (true)
|
||||
X1 = ...
|
||||
else
|
||||
X2 = ...
|
||||
X3 = phi(X1, X2);
|
||||
}
|
||||
} else {
|
||||
for (...) {
|
||||
if (false)
|
||||
X1' = ...
|
||||
else
|
||||
X2' = ...
|
||||
X3' = phi(X1', X2');
|
||||
}
|
||||
```c
|
||||
c = ...;
|
||||
if (c) {
|
||||
for (...) {
|
||||
if (true)
|
||||
X1 = ...
|
||||
else
|
||||
X2 = ...
|
||||
X3 = phi(X1, X2);
|
||||
}
|
||||
X4 = phi(X3, X3')
|
||||
} else {
|
||||
for (...) {
|
||||
if (false)
|
||||
X1' = ...
|
||||
else
|
||||
X2' = ...
|
||||
X3' = phi(X1', X2');
|
||||
}
|
||||
}
|
||||
X4 = phi(X3, X3')
|
||||
```
|
||||
|
||||
Now, all uses of X4 will get the updated value (in general,
|
||||
if a loop is in LCSSA form, in any loop transformation,
|
||||
|
|
@ -409,7 +407,7 @@ to take effect). If we did not have Loop Closed SSA form, it means that X3 coul
|
|||
possibly be used outside the loop. So, we would have to introduce the
|
||||
X4 (which is the new X3) and replace all uses of X3 with that.
|
||||
However, we should note that because LLVM keeps a def-use chain
|
||||
[#def-use-chain]_ for each Value, we wouldn't need
|
||||
[^def-use-chain] for each Value, we wouldn't need
|
||||
to perform data-flow analysis to find and replace all the uses
|
||||
(there is even a utility function, replaceAllUsesWith(),
|
||||
that performs this transformation by iterating the def-use chain).
|
||||
|
|
@ -419,21 +417,21 @@ of an induction variable is the same. Without this, you need to
|
|||
distinguish the case when the variable is used outside of
|
||||
the loop it is defined in, for example:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
for (j = 0; j < 100; j++) {
|
||||
k = i + j;
|
||||
use(k); // use 1
|
||||
}
|
||||
use(k); // use 2
|
||||
```c
|
||||
for (i = 0; i < 100; i++) {
|
||||
for (j = 0; j < 100; j++) {
|
||||
k = i + j;
|
||||
use(k); // use 1
|
||||
}
|
||||
use(k); // use 2
|
||||
}
|
||||
```
|
||||
|
||||
Looking from the outer loop with the normal SSA form, the first use of k
|
||||
is not well-behaved, while the second one is an induction variable with
|
||||
base 100 and step 1. Although, in practice, and in the LLVM context,
|
||||
such cases can be handled effectively by SCEV. Scalar Evolution
|
||||
(:ref:`scalar-evolution <passes-scalar-evolution>`) or SCEV, is a
|
||||
({ref}`scalar-evolution <passes-scalar-evolution>`) or SCEV, is a
|
||||
(analysis) pass that analyzes and categorizes the evolution of scalar
|
||||
expressions in loops.
|
||||
|
||||
|
|
@ -441,13 +439,13 @@ In general, it's easier to use SCEV in loops that are in LCSSA form.
|
|||
The evolution of a scalar (loop-variant) expression that
|
||||
SCEV can analyze is, by definition, relative to a loop.
|
||||
An expression is represented in LLVM by an
|
||||
`llvm::Instruction <https://llvm.org/doxygen/classllvm_1_1Instruction.html>`_.
|
||||
[llvm::Instruction](https://llvm.org/doxygen/classllvm_1_1Instruction.html).
|
||||
If the expression is inside two (or more) loops (which can only
|
||||
happen if the loops are nested, like in the example above) and you want
|
||||
to get an analysis of its evolution (from SCEV),
|
||||
you have to also specify relative to what Loop you want it.
|
||||
Specifically, you have to use
|
||||
`getSCEVAtScope() <https://llvm.org/doxygen/classllvm_1_1ScalarEvolution.html#a21d6ee82eed29080d911dbb548a8bb68>`_.
|
||||
[getSCEVAtScope()](https://llvm.org/doxygen/classllvm_1_1ScalarEvolution.html#a21d6ee82eed29080d911dbb548a8bb68).
|
||||
|
||||
However, if all loops are in LCSSA form, each expression is actually
|
||||
represented by two different llvm::Instructions. One inside the loop
|
||||
|
|
@ -455,16 +453,14 @@ and one outside, which is the loop-closing PHI node and represents
|
|||
the value of the expression after the last iteration (effectively,
|
||||
we break each loop-variant expression into two expressions and so, every
|
||||
expression is at most in one loop). You can now just use
|
||||
`getSCEV() <https://llvm.org/doxygen/classllvm_1_1ScalarEvolution.html#a30bd18ac905eacf3601bc6a553a9ff49>`_.
|
||||
[getSCEV()](https://llvm.org/doxygen/classllvm_1_1ScalarEvolution.html#a30bd18ac905eacf3601bc6a553a9ff49).
|
||||
and which of these two llvm::Instructions you pass to it disambiguates
|
||||
the context / scope / relative loop.
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#lcssa-construction] To insert these loop-closing PHI nodes, one has to
|
||||
[^lcssa-construction]: To insert these loop-closing PHI nodes, one has to
|
||||
(re-)compute dominance frontiers (if the loop has multiple exits).
|
||||
|
||||
.. [#point-of-use-phis] Considering the point of use of a PHI entry value
|
||||
[^point-of-use-phis]: Considering the point of use of a PHI entry value
|
||||
to be in the respective predecessor is a convention across the whole LLVM.
|
||||
The reason is mostly practical; for example it preserves the dominance
|
||||
property of SSA. It is also just an overapproximation of the actual
|
||||
|
|
@ -484,121 +480,122 @@ the context / scope / relative loop.
|
|||
use the original value afterwards (in which case the two live ranges overlap,
|
||||
although in LCSSA (the whole point is that) we never do that).
|
||||
|
||||
|
||||
.. [#def-use-chain] A property of SSA is that there exists a def-use chain
|
||||
[^def-use-chain]: A property of SSA is that there exists a def-use chain
|
||||
for each definition, which is a list of all the uses of this definition.
|
||||
LLVM implements this property by keeping a list of all the uses of a Value
|
||||
in an internal data structure.
|
||||
|
||||
"More Canonical" Loops
|
||||
======================
|
||||
## "More Canonical" Loops
|
||||
|
||||
.. _loop-terminology-loop-rotate:
|
||||
(loop-terminology-loop-rotate)=
|
||||
|
||||
Rotated Loops
|
||||
-------------
|
||||
### Rotated Loops
|
||||
|
||||
Loops are rotated by the LoopRotate (:ref:`loop-rotate <passes-loop-rotate>`)
|
||||
Loops are rotated by the LoopRotate ({ref}`loop-rotate <passes-loop-rotate>`)
|
||||
pass, which converts loops into do/while style loops and is
|
||||
implemented in
|
||||
`LoopRotation.h <https://llvm.org/doxygen/LoopRotation_8h_source.html>`_. Example:
|
||||
[LoopRotation.h](https://llvm.org/doxygen/LoopRotation_8h_source.html). Example:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void test(int n) {
|
||||
for (int i = 0; i < n; i += 1)
|
||||
// Loop body
|
||||
}
|
||||
```c
|
||||
void test(int n) {
|
||||
for (int i = 0; i < n; i += 1)
|
||||
// Loop body
|
||||
}
|
||||
```
|
||||
|
||||
is transformed to:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void test(int n) {
|
||||
int i = 0;
|
||||
do {
|
||||
// Loop body
|
||||
i += 1;
|
||||
} while (i < n);
|
||||
}
|
||||
```c
|
||||
void test(int n) {
|
||||
int i = 0;
|
||||
do {
|
||||
// Loop body
|
||||
i += 1;
|
||||
} while (i < n);
|
||||
}
|
||||
```
|
||||
|
||||
**Warning**: This transformation is valid only if the compiler
|
||||
can prove that the loop body will be executed at least once. Otherwise,
|
||||
it has to insert a guard which will test it at runtime. In the example
|
||||
above, that would be:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void test(int n) {
|
||||
int i = 0;
|
||||
if (n > 0) {
|
||||
do {
|
||||
// Loop body
|
||||
i += 1;
|
||||
} while (i < n);
|
||||
}
|
||||
```c
|
||||
void test(int n) {
|
||||
int i = 0;
|
||||
if (n > 0) {
|
||||
do {
|
||||
// Loop body
|
||||
i += 1;
|
||||
} while (i < n);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's important to understand the effect of loop rotation
|
||||
at the LLVM IR level. We follow with the previous examples
|
||||
in LLVM IR while also providing a graphical representation
|
||||
of the control-flow graphs (CFG). You can get the same graphical
|
||||
results by utilizing the :ref:`view-cfg <passes-view-cfg>` pass.
|
||||
results by utilizing the {ref}`view-cfg <passes-view-cfg>` pass.
|
||||
|
||||
The initial **for** loop could be translated to:
|
||||
|
||||
.. code-block:: none
|
||||
```
|
||||
|
||||
define void @test(i32 %n) {
|
||||
entry:
|
||||
br label %for.header
|
||||
define void @test(i32 %n) {
|
||||
entry:
|
||||
br label %for.header
|
||||
|
||||
for.header:
|
||||
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
|
||||
%cond = icmp slt i32 %i, %n
|
||||
br i1 %cond, label %body, label %exit
|
||||
for.header:
|
||||
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
|
||||
%cond = icmp slt i32 %i, %n
|
||||
br i1 %cond, label %body, label %exit
|
||||
|
||||
body:
|
||||
; Loop body
|
||||
br label %latch
|
||||
body:
|
||||
; Loop body
|
||||
br label %latch
|
||||
|
||||
latch:
|
||||
%i.next = add nsw i32 %i, 1
|
||||
br label %for.header
|
||||
latch:
|
||||
%i.next = add nsw i32 %i, 1
|
||||
br label %for.header
|
||||
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
.. image:: ./loop-terminology-initial-loop.png
|
||||
:width: 400 px
|
||||
```{image} ./loop-terminology-initial-loop.png
|
||||
:width: 400 px
|
||||
```
|
||||
|
||||
Before we explain how LoopRotate will actually
|
||||
transform this loop, here's how we could convert
|
||||
it (by hand) to a do-while style loop.
|
||||
|
||||
.. code-block:: none
|
||||
```
|
||||
|
||||
define void @test(i32 %n) {
|
||||
entry:
|
||||
br label %body
|
||||
define void @test(i32 %n) {
|
||||
entry:
|
||||
br label %body
|
||||
|
||||
body:
|
||||
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
|
||||
; Loop body
|
||||
br label %latch
|
||||
body:
|
||||
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
|
||||
; Loop body
|
||||
br label %latch
|
||||
|
||||
latch:
|
||||
%i.next = add nsw i32 %i, 1
|
||||
%cond = icmp slt i32 %i.next, %n
|
||||
br i1 %cond, label %body, label %exit
|
||||
latch:
|
||||
%i.next = add nsw i32 %i, 1
|
||||
%cond = icmp slt i32 %i.next, %n
|
||||
br i1 %cond, label %body, label %exit
|
||||
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
.. image:: ./loop-terminology-rotated-loop.png
|
||||
:width: 400 px
|
||||
```{image} ./loop-terminology-rotated-loop.png
|
||||
:width: 400 px
|
||||
```
|
||||
|
||||
Note two things:
|
||||
|
||||
|
|
@ -612,38 +609,40 @@ Note two things:
|
|||
|
||||
This is how LoopRotate transforms this loop:
|
||||
|
||||
.. code-block:: none
|
||||
```
|
||||
|
||||
define void @test(i32 %n) {
|
||||
entry:
|
||||
%guard_cond = icmp slt i32 0, %n
|
||||
br i1 %guard_cond, label %loop.preheader, label %exit
|
||||
define void @test(i32 %n) {
|
||||
entry:
|
||||
%guard_cond = icmp slt i32 0, %n
|
||||
br i1 %guard_cond, label %loop.preheader, label %exit
|
||||
|
||||
loop.preheader:
|
||||
br label %body
|
||||
loop.preheader:
|
||||
br label %body
|
||||
|
||||
body:
|
||||
%i2 = phi i32 [ 0, %loop.preheader ], [ %i.next, %latch ]
|
||||
br label %latch
|
||||
body:
|
||||
%i2 = phi i32 [ 0, %loop.preheader ], [ %i.next, %latch ]
|
||||
br label %latch
|
||||
|
||||
latch:
|
||||
%i.next = add nsw i32 %i2, 1
|
||||
%cond = icmp slt i32 %i.next, %n
|
||||
br i1 %cond, label %body, label %loop.exit
|
||||
latch:
|
||||
%i.next = add nsw i32 %i2, 1
|
||||
%cond = icmp slt i32 %i.next, %n
|
||||
br i1 %cond, label %body, label %loop.exit
|
||||
|
||||
loop.exit:
|
||||
br label %exit
|
||||
loop.exit:
|
||||
br label %exit
|
||||
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
.. image:: ./loop-terminology-guarded-loop.png
|
||||
:width: 500 px
|
||||
```{image} ./loop-terminology-guarded-loop.png
|
||||
:width: 500 px
|
||||
```
|
||||
|
||||
The result is a little bit more complicated than we may expect
|
||||
because LoopRotate ensures that the loop is in
|
||||
:ref:`Loop Simplify Form <loop-terminology-loop-simplify>`
|
||||
{ref}`Loop Simplify Form <loop-terminology-loop-simplify>`
|
||||
after rotation.
|
||||
In this case, it inserted the %loop.preheader basic block so
|
||||
that the loop has a preheader and it introduced the %loop.exit
|
||||
|
|
@ -658,24 +657,24 @@ invariant instructions, especially loads, into the preheader.
|
|||
That could be done in non-rotated loops as well but with
|
||||
some disadvantages. Let's illustrate them with an example:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
auto v = *p;
|
||||
use(v);
|
||||
}
|
||||
```c
|
||||
for (int i = 0; i < n; ++i) {
|
||||
auto v = *p;
|
||||
use(v);
|
||||
}
|
||||
```
|
||||
|
||||
We assume that loading from p is invariant and use(v) is some
|
||||
statement that uses v.
|
||||
If we wanted to execute the load only once we could move it
|
||||
"out" of the loop body, resulting in this:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
auto v = *p;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
use(v);
|
||||
}
|
||||
```c
|
||||
auto v = *p;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
use(v);
|
||||
}
|
||||
```
|
||||
|
||||
However, now, in the case that n <= 0, in the initial form,
|
||||
the loop body would never execute, and so, the load would
|
||||
|
|
@ -687,14 +686,14 @@ the initial semantics.
|
|||
|
||||
To avoid both of these problems, we can insert a guard:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
if (n > 0) { // loop guard
|
||||
auto v = *p;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
use(v);
|
||||
}
|
||||
```c
|
||||
if (n > 0) { // loop guard
|
||||
auto v = *p;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
use(v);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is certainly better but it could be improved slightly. Notice
|
||||
that the check for whether n is bigger than 0 is executed twice (and
|
||||
|
|
@ -703,16 +702,16 @@ and once in the first execution of the loop. To avoid that, we could
|
|||
do an unconditional first execution and insert the loop condition
|
||||
in the end. This effectively means transforming the loop into a do-while loop:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
if (0 < n) {
|
||||
auto v = *p;
|
||||
do {
|
||||
use(v);
|
||||
++i;
|
||||
} while (i < n);
|
||||
}
|
||||
```c
|
||||
if (0 < n) {
|
||||
auto v = *p;
|
||||
do {
|
||||
use(v);
|
||||
++i;
|
||||
} while (i < n);
|
||||
}
|
||||
```
|
||||
|
||||
Note that LoopRotate does not generally do such
|
||||
hoisting. Rather, it is an enabling transformation for other
|
||||
passes like Loop-Invariant Code Motion (:ref:`-licm <passes-licm>`).
|
||||
passes like Loop-Invariant Code Motion ({ref}`-licm <passes-licm>`).
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,123 +1,120 @@
|
|||
=========
|
||||
MemorySSA
|
||||
=========
|
||||
# MemorySSA
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
Introduction
|
||||
============
|
||||
## Introduction
|
||||
|
||||
``MemorySSA`` is an analysis that allows us to cheaply reason about the
|
||||
`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. One common use of ``MemorySSA`` is to quickly find out
|
||||
`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. One common use of `MemorySSA` is to quickly find out
|
||||
that something definitely cannot happen (for example, reason that a hoist
|
||||
out of a loop can't happen).
|
||||
|
||||
At a high level, one of the goals of ``MemorySSA`` is to provide an SSA based
|
||||
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 memory, and associate memory operations with those versions.
|
||||
|
||||
This document goes over how ``MemorySSA`` is structured, and some basic
|
||||
intuition on how ``MemorySSA`` works.
|
||||
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
|
||||
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 memory 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 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.
|
||||
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:
|
||||
Each `MemoryAccess` can be one of three types:
|
||||
|
||||
- ``MemoryDef``
|
||||
- ``MemoryPhi``
|
||||
- ``MemoryUse``
|
||||
- `MemoryDef`
|
||||
- `MemoryPhi`
|
||||
- `MemoryUse`
|
||||
|
||||
``MemoryDef``\ s are operations which may either modify memory, or which
|
||||
introduce some kind of ordering constraints. Examples of ``MemoryDef``\ s
|
||||
include ``store``\ s, function calls, ``load``\ s with ``acquire`` (or higher)
|
||||
ordering, volatile operations, memory fences, etc. A ``MemoryDef``
|
||||
`MemoryDef`s are operations which may either modify memory, or which
|
||||
introduce some kind of ordering constraints. Examples of `MemoryDef`s
|
||||
include `store`s, function calls, `load`s with `acquire` (or higher)
|
||||
ordering, volatile operations, memory fences, etc. A `MemoryDef`
|
||||
always introduces a new version of the entire memory and is linked with a single
|
||||
``MemoryDef/MemoryPhi`` which is the version of memory that the new
|
||||
`MemoryDef/MemoryPhi` which is the version of memory that the new
|
||||
version is based on. This implies that there is a *single*
|
||||
``Def`` chain that connects all the ``Def``\ s, either directly
|
||||
`Def` chain that connects all the `Def`s, either directly
|
||||
or indirectly. For example in:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
b = MemoryDef(a)
|
||||
c = MemoryDef(b)
|
||||
d = MemoryDef(c)
|
||||
|
||||
``d`` is connected directly with ``c`` and indirectly with ``b``.
|
||||
This means that ``d`` potentially clobbers (see below) ``c`` *or*
|
||||
``b`` *or* both. This in turn implies that without the use of `The walker`_,
|
||||
initially every ``MemoryDef`` clobbers every other ``MemoryDef``.
|
||||
```
|
||||
|
||||
``MemoryPhi``\ s are ``PhiNode``\ s, 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, ``BasicBlock``\ s are mapped to ``MemoryPhi``\ s
|
||||
inside ``MemorySSA``, whereas ``Instruction``\ s are mapped to ``MemoryUse``\ s
|
||||
and ``MemoryDef``\ s.
|
||||
`d` is connected directly with `c` and indirectly with `b`.
|
||||
This means that `d` potentially clobbers (see below) `c` *or*
|
||||
`b` *or* both. This in turn implies that without the use of [The walker](#the-walker),
|
||||
initially every `MemoryDef` clobbers every other `MemoryDef`.
|
||||
|
||||
`MemoryPhi`s are `PhiNode`s, 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, `BasicBlock`s are mapped to `MemoryPhi`s
|
||||
inside `MemorySSA`, whereas `Instruction`s are mapped to `MemoryUse`s
|
||||
and `MemoryDef`s.
|
||||
|
||||
Note also that in SSA, Phi nodes merge must-reach definitions (that is,
|
||||
definitions that *must* be 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.
|
||||
`MemoryUse`s are operations which use but don't modify memory. An example of
|
||||
a `MemoryUse` is a `load`, or a `readonly` function call.
|
||||
|
||||
Every function that exists has a special ``MemoryDef`` called ``liveOnEntry``.
|
||||
It dominates every ``MemoryAccess`` in the function that ``MemorySSA`` is being
|
||||
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
|
||||
`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 overlaid on LLVM IR (obtained by running ``opt
|
||||
-passes='print<memoryssa>' -disable-output`` on an ``.ll`` file) is below. When
|
||||
An example of all of this overlaid 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.
|
||||
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.
|
||||
|
||||
If a ``MemoryAccess`` is a *clobber* of another, it means that these two
|
||||
``MemoryAccess``\ es may access the same memory. For example, ``x = MemoryDef(y)``
|
||||
means that ``x`` potentially modifies memory that ``y`` modifies/constrains
|
||||
If a `MemoryAccess` is a *clobber* of another, it means that these two
|
||||
`MemoryAccess`es may access the same memory. For example, `x = MemoryDef(y)`
|
||||
means that `x` potentially modifies memory that `y` modifies/constrains
|
||||
(or has modified / constrained).
|
||||
In the same manner, ``a = MemoryPhi({BB1,b},{BB2,c})`` means that
|
||||
anyone that uses ``a`` is accessing memory potentially modified / constrained
|
||||
by either ``b`` or ``c`` (or both). And finally, ``MemoryUse(x)`` means
|
||||
that this use accesses memory that ``x`` has modified / constrained
|
||||
(as an example, think that if ``x = MemoryDef(...)``
|
||||
and ``MemoryUse(x)`` are in the same loop, the use can't
|
||||
In the same manner, `a = MemoryPhi({BB1,b},{BB2,c})` means that
|
||||
anyone that uses `a` is accessing memory potentially modified / constrained
|
||||
by either `b` or `c` (or both). And finally, `MemoryUse(x)` means
|
||||
that this use accesses memory that `x` has modified / constrained
|
||||
(as an example, think that if `x = MemoryDef(...)`
|
||||
and `MemoryUse(x)` are in the same loop, the use can't
|
||||
be hoisted outside alone).
|
||||
|
||||
Another useful way of looking at it is in terms of memory versions.
|
||||
In that view, operands of a given ``MemoryAccess`` are the version
|
||||
In that view, operands of a given `MemoryAccess` are the version
|
||||
of the entire memory before the operation, and if the access produces
|
||||
a value (i.e. ``MemoryDef/MemoryPhi``),
|
||||
a value (i.e. `MemoryDef/MemoryPhi`),
|
||||
the value is the new version of the memory after the operation.
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
define void @foo() {
|
||||
entry:
|
||||
%p1 = alloca i8
|
||||
|
|
@ -152,64 +149,63 @@ the value is the new version of the memory after the operation.
|
|||
br label %while.cond
|
||||
}
|
||||
|
||||
The ``MemorySSA`` IR is shown in comments that precede the instructions they map
|
||||
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, ptr %p3``. Other places in ``MemorySSA`` refer to this
|
||||
particular ``MemoryDef`` as ``1`` (much like how one can refer to ``load i8, ptr
|
||||
%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.
|
||||
```
|
||||
|
||||
The `MemorySSA` IR is shown in comments that precede the instructions they map
|
||||
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, ptr %p3`. Other places in `MemorySSA` refer to this
|
||||
particular `MemoryDef` as `1` (much like how one can refer to `load i8, ptr
|
||||
%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({entry,1},{if.end,4})`` notes that, when entering
|
||||
``while.cond``, 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, ptr %p1`` is a definition,
|
||||
and its reaching definition before it is ``6``, or the ``MemoryPhi`` after
|
||||
``while.cond``. (See the `Use and Def optimization`_ and `Precision`_
|
||||
sections below for why this ``MemoryDef`` isn't linked to a separate,
|
||||
disambiguated ``MemoryPhi``.)
|
||||
- ``3 = MemoryDef(6)`` notes that ``store i8 0, ptr %p2`` is a definition; its
|
||||
reaching definition is also ``6``.
|
||||
- ``5 = MemoryPhi({if.then,2},{if.else,3})`` notes that the clobber before
|
||||
this block could either be ``2`` or ``3``.
|
||||
- ``MemoryUse(5)`` notes that ``load i8, ptr %p1`` is a use of memory, and that
|
||||
it's clobbered by ``5``.
|
||||
- ``4 = MemoryDef(5)`` notes that ``store i8 2, ptr %p2`` is a definition; its
|
||||
reaching definition is ``5``.
|
||||
- ``MemoryUse(1)`` notes that ``load i8, ptr %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 memory versioning parlance, it really only depends on
|
||||
- `6 = MemoryPhi({entry,1},{if.end,4})` notes that, when entering
|
||||
`while.cond`, 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, ptr %p1` is a definition,
|
||||
and its reaching definition before it is `6`, or the `MemoryPhi` after
|
||||
`while.cond`. (See the [Use and Def optimization](#use-and-def-optimization) and
|
||||
[Precision](#precision) sections below for why this `MemoryDef` isn't linked
|
||||
to a separate, disambiguated `MemoryPhi`.)
|
||||
- `3 = MemoryDef(6)` notes that `store i8 0, ptr %p2` is a definition; its
|
||||
reaching definition is also `6`.
|
||||
- `5 = MemoryPhi({if.then,2},{if.else,3})` notes that the clobber before
|
||||
this block could either be `2` or `3`.
|
||||
- `MemoryUse(5)` notes that `load i8, ptr %p1` is a use of memory, and that
|
||||
it's clobbered by `5`.
|
||||
- `4 = MemoryDef(5)` notes that `store i8 2, ptr %p2` is a definition; its
|
||||
reaching definition is `5`.
|
||||
- `MemoryUse(1)` notes that `load i8, ptr %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 memory versioning parlance, it really only depends on
|
||||
the memory version 1, and is unaffected by the new memory versions generated since
|
||||
then.
|
||||
|
||||
As an aside, ``MemoryAccess`` is a ``Value`` mostly for convenience; it's not
|
||||
As an aside, `MemoryAccess` is a `Value` mostly for convenience; it's not
|
||||
meant to interact with LLVM IR.
|
||||
|
||||
Design of MemorySSA
|
||||
===================
|
||||
## Design of MemorySSA
|
||||
|
||||
``MemorySSA`` is an analysis that can be built for any arbitrary function. When
|
||||
`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`` .
|
||||
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``
|
||||
When `MemorySSA` is done building, it also hands you a `MemorySSAWalker`
|
||||
that you can use (see below).
|
||||
|
||||
|
||||
The walker
|
||||
----------
|
||||
### The walker
|
||||
|
||||
A structure that helps ``MemorySSA`` do its job is the ``MemorySSAWalker``, or
|
||||
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,
|
||||
queries beyond what's represented directly by `MemoryAccess`es. For example,
|
||||
given:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
define void @foo() {
|
||||
%a = alloca i8
|
||||
%b = alloca i8
|
||||
|
|
@ -220,80 +216,78 @@ given:
|
|||
store i8 0, ptr %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 whatever alias analysis stack you happen to
|
||||
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 whatever alias analysis stack you happen to
|
||||
be using. Walkers were built to be flexible, though, so it's entirely reasonable
|
||||
(and expected) to create more specialized walkers (e.g. one that specifically
|
||||
queries ``GlobalsAA``, one that always stops at ``MemoryPhi`` nodes, etc).
|
||||
queries `GlobalsAA`, one that always stops at `MemoryPhi` nodes, etc).
|
||||
|
||||
Default walker APIs
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
#### Default walker APIs
|
||||
|
||||
There are two main APIs used to retrieve the clobbering access using the walker:
|
||||
|
||||
- ``MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA);`` return the
|
||||
clobbering memory access for ``MA``, caching all intermediate results
|
||||
- `MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA);` return the
|
||||
clobbering memory access for `MA`, caching all intermediate results
|
||||
computed along the way as part of each access queried.
|
||||
|
||||
- ``MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA, const MemoryLocation &Loc);``
|
||||
returns the access clobbering memory location ``Loc``, starting at ``MA``.
|
||||
- `MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA, const MemoryLocation &Loc);`
|
||||
returns the access clobbering memory location `Loc`, starting at `MA`.
|
||||
Because this API does not request the clobbering access of a specific memory
|
||||
access, there are no results that can be cached.
|
||||
|
||||
Locating clobbers yourself
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#### 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;
|
||||
`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``.
|
||||
`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 and Def optimization
|
||||
------------------------
|
||||
### Use and Def optimization
|
||||
|
||||
``MemoryUse``\ s keep a single operand, which is their defining or optimized
|
||||
`MemoryUse`s keep a single operand, which is their defining or optimized
|
||||
access.
|
||||
Traditionally ``MemorySSA`` optimized ``MemoryUse``\ s at build-time, up to a
|
||||
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,
|
||||
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
|
||||
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()``.
|
||||
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.
|
||||
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
|
||||
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
|
||||
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++
|
||||
|
||||
```c++
|
||||
MemoryDef *Def; // find who's optimized or defining for this MemoryDef
|
||||
for (auto &U : Def->uses()) {
|
||||
MemoryAccess *MA = cast<MemoryAccess>(U.getUser());
|
||||
|
|
@ -305,12 +299,13 @@ A code snippet for such a walk looks like this:
|
|||
}
|
||||
}
|
||||
|
||||
When ``MemoryUse``\ s are optimized, for a given store, you can find all loads
|
||||
```
|
||||
|
||||
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++
|
||||
|
||||
```c++
|
||||
checkUses(MemoryAccess *Def) { // Def can be a MemoryDef or a MemoryPhi.
|
||||
for (auto &U : Def->uses()) {
|
||||
MemoryAccess *MA = cast<MemoryAccess>(U.getUser());
|
||||
|
|
@ -328,30 +323,29 @@ the store.
|
|||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
An example of similar traversals can be found in the DeadStoreElimination pass.
|
||||
|
||||
Invalidation and updating
|
||||
-------------------------
|
||||
### Invalidation and updating
|
||||
|
||||
Because ``MemorySSA`` keeps track of LLVM IR, it needs to be updated whenever
|
||||
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`` and ``LICM`` are users of ``MemorySSA``\ s
|
||||
motion of `Instructions`. The update API is being made on an as-needed basis.
|
||||
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.
|
||||
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
|
||||
^^^^^^^^^^^^^
|
||||
#### Phi placement
|
||||
|
||||
``MemorySSA`` only places ``MemoryPhi``\ s where they're actually
|
||||
`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
|
||||
|
||||
```llvm
|
||||
define void @foo() {
|
||||
entry:
|
||||
%p1 = alloca i8
|
||||
|
|
@ -381,27 +375,27 @@ example, consider:
|
|||
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
|
||||
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
|
||||
---------
|
||||
### Non-Goals
|
||||
|
||||
``MemorySSA`` is meant to reason about the relation between memory
|
||||
`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``
|
||||
optimizations. Specifically, care must be taken when trying to use `MemorySSA`
|
||||
to reason about atomic or volatile operations, as in:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
```llvm
|
||||
define i8 @foo(ptr %a) {
|
||||
entry:
|
||||
br i1 undef, label %if.then, label %if.end
|
||||
|
|
@ -416,17 +410,17 @@ to reason about atomic or volatile operations, as in:
|
|||
ret i8 %av
|
||||
}
|
||||
|
||||
Going solely by ``MemorySSA``'s analysis, hoisting the ``load`` to ``entry`` may
|
||||
```
|
||||
|
||||
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
|
||||
----------------
|
||||
### Design tradeoffs
|
||||
|
||||
Precision
|
||||
^^^^^^^^^
|
||||
#### Precision
|
||||
|
||||
``MemorySSA`` in LLVM deliberately trades off precision for speed.
|
||||
`MemorySSA` in LLVM deliberately trades off precision for speed.
|
||||
Let us think about memory variables as if they were disjoint partitions of the
|
||||
memory (that is, if you have one variable, as above, it represents the entire
|
||||
memory, and if you have multiple variables, each one represents some
|
||||
|
|
@ -462,31 +456,29 @@ have to query something to disambiguate further anyway.
|
|||
|
||||
As a result, LLVM partitions to one variable.
|
||||
|
||||
Precision in practice
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
#### 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 different passes may make MemorySSA either "overly
|
||||
optimized" (it can provide a more accurate 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 acquired by ``MemorySSA`` due to being updated by
|
||||
when the result relies on the state acquired 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.
|
||||
|
||||
|
||||
LLVM Developers Meeting presentations
|
||||
-------------------------------------
|
||||
### LLVM Developers Meeting presentations
|
||||
|
||||
- `2016 LLVM Developers' Meeting: G. Burgess - MemorySSA in Five Minutes <https://www.youtube.com/watch?v=bdxWmryoHak>`_.
|
||||
- `2020 LLVM Developers' Meeting: S. Baziotis & S. Moll - Finding Your Way Around the LLVM Dependence Analysis Zoo <https://www.youtube.com/watch?v=1e5y6WDbXCQ>`_
|
||||
- [2016 LLVM Developers' Meeting: G. Burgess - MemorySSA in Five Minutes](https://www.youtube.com/watch?v=bdxWmryoHak).
|
||||
- [2020 LLVM Developers' Meeting: S. Baziotis & S. Moll - Finding Your Way Around the LLVM Dependence Analysis Zoo](https://www.youtube.com/watch?v=1e5y6WDbXCQ)
|
||||
|
|
|
|||
|
|
@ -1,143 +1,139 @@
|
|||
==========================
|
||||
Using the New Pass Manager
|
||||
==========================
|
||||
# Using the New Pass Manager
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
```{contents}
|
||||
:local:
|
||||
```
|
||||
|
||||
Overview
|
||||
========
|
||||
## Overview
|
||||
|
||||
For an overview of the new pass manager, see the `blog post
|
||||
<https://blog.llvm.org/posts/2021-03-26-the-new-pass-manager/>`_.
|
||||
For an overview of the new pass manager, see the [blog post].
|
||||
|
||||
Just Tell Me How To Run The Default Optimization Pipeline With The New Pass Manager
|
||||
===================================================================================
|
||||
[blog post]: https://blog.llvm.org/posts/2021-03-26-the-new-pass-manager/
|
||||
|
||||
.. code-block:: c++
|
||||
## Just Tell Me How To Run The Default Optimization Pipeline With The New Pass Manager
|
||||
|
||||
// Create the analysis managers.
|
||||
// These must be declared in this order so that they are destroyed in the
|
||||
// correct order due to inter-analysis-manager references.
|
||||
LoopAnalysisManager LAM;
|
||||
FunctionAnalysisManager FAM;
|
||||
CGSCCAnalysisManager CGAM;
|
||||
ModuleAnalysisManager MAM;
|
||||
```cpp
|
||||
// Create the analysis managers.
|
||||
// These must be declared in this order so that they are destroyed in the
|
||||
// correct order due to inter-analysis-manager references.
|
||||
LoopAnalysisManager LAM;
|
||||
FunctionAnalysisManager FAM;
|
||||
CGSCCAnalysisManager CGAM;
|
||||
ModuleAnalysisManager MAM;
|
||||
|
||||
// Create the new pass manager builder.
|
||||
// Take a look at the PassBuilder constructor parameters for more
|
||||
// customization, e.g. specifying a TargetMachine or various debugging
|
||||
// options.
|
||||
PassBuilder PB;
|
||||
// Create the new pass manager builder.
|
||||
// Take a look at the PassBuilder constructor parameters for more
|
||||
// customization, e.g. specifying a TargetMachine or various debugging
|
||||
// options.
|
||||
PassBuilder PB;
|
||||
|
||||
// Register all the basic analyses with the managers.
|
||||
PB.registerModuleAnalyses(MAM);
|
||||
PB.registerCGSCCAnalyses(CGAM);
|
||||
PB.registerFunctionAnalyses(FAM);
|
||||
PB.registerLoopAnalyses(LAM);
|
||||
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
|
||||
// Register all the basic analyses with the managers.
|
||||
PB.registerModuleAnalyses(MAM);
|
||||
PB.registerCGSCCAnalyses(CGAM);
|
||||
PB.registerFunctionAnalyses(FAM);
|
||||
PB.registerLoopAnalyses(LAM);
|
||||
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
|
||||
|
||||
// Create the pass manager.
|
||||
// This one corresponds to a typical -O2 optimization pipeline.
|
||||
ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OptimizationLevel::O2);
|
||||
// Create the pass manager.
|
||||
// This one corresponds to a typical -O2 optimization pipeline.
|
||||
ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OptimizationLevel::O2);
|
||||
|
||||
// Optimize the IR!
|
||||
MPM.run(MyModule, MAM);
|
||||
// Optimize the IR!
|
||||
MPM.run(MyModule, MAM);
|
||||
```
|
||||
|
||||
The C API also supports most of this, see ``llvm-c/Transforms/PassBuilder.h``.
|
||||
The C API also supports most of this, see `llvm-c/Transforms/PassBuilder.h`.
|
||||
|
||||
Adding Passes to a Pass Manager
|
||||
===============================
|
||||
## Adding Passes to a Pass Manager
|
||||
|
||||
For how to write a new PM pass, see :doc:`this page <WritingAnLLVMNewPMPass>`.
|
||||
For how to write a new PM pass, see {doc}`this page <WritingAnLLVMNewPMPass>`.
|
||||
|
||||
To add a pass to a new PM pass manager, the important thing is to match the
|
||||
pass type and the pass manager type. For example, a ``FunctionPassManager``
|
||||
pass type and the pass manager type. For example, a `FunctionPassManager`
|
||||
can only contain function passes:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
FunctionPassManager FPM;
|
||||
// InstSimplifyPass is a function pass
|
||||
FPM.addPass(InstSimplifyPass());
|
||||
```cpp
|
||||
FunctionPassManager FPM;
|
||||
// InstSimplifyPass is a function pass
|
||||
FPM.addPass(InstSimplifyPass());
|
||||
```
|
||||
|
||||
If you want to add a loop pass that runs on all loops in a function to a
|
||||
``FunctionPassManager``, the loop pass must be wrapped in a function pass
|
||||
`FunctionPassManager`, the loop pass must be wrapped in a function pass
|
||||
adaptor that goes through all the loops in the function and runs the loop
|
||||
pass on each one.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
FunctionPassManager FPM;
|
||||
// LoopRotatePass is a loop pass
|
||||
FPM.addPass(createFunctionToLoopPassAdaptor(LoopRotatePass()));
|
||||
```cpp
|
||||
FunctionPassManager FPM;
|
||||
// LoopRotatePass is a loop pass
|
||||
FPM.addPass(createFunctionToLoopPassAdaptor(LoopRotatePass()));
|
||||
```
|
||||
|
||||
The IR hierarchy in terms of the new PM is Module -> (CGSCC ->) Function ->
|
||||
Loop, where going through a CGSCC is optional.
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
FunctionPassManager FPM;
|
||||
// loop -> function
|
||||
FPM.addPass(createFunctionToLoopPassAdaptor(LoopFooPass()));
|
||||
|
||||
FunctionPassManager FPM;
|
||||
// loop -> function
|
||||
FPM.addPass(createFunctionToLoopPassAdaptor(LoopFooPass()));
|
||||
CGSCCPassManager CGPM;
|
||||
// loop -> function -> cgscc
|
||||
CGPM.addPass(createCGSCCToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(LoopFooPass())));
|
||||
// function -> cgscc
|
||||
CGPM.addPass(createCGSCCToFunctionPassAdaptor(FunctionFooPass()));
|
||||
|
||||
CGSCCPassManager CGPM;
|
||||
// loop -> function -> cgscc
|
||||
CGPM.addPass(createCGSCCToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(LoopFooPass())));
|
||||
// function -> cgscc
|
||||
CGPM.addPass(createCGSCCToFunctionPassAdaptor(FunctionFooPass()));
|
||||
|
||||
ModulePassManager MPM;
|
||||
// loop -> function -> module
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(LoopFooPass())));
|
||||
// function -> module
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(FunctionFooPass()));
|
||||
|
||||
// loop -> function -> cgscc -> module
|
||||
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(createCGSCCToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(LoopFooPass()))));
|
||||
// function -> cgscc -> module
|
||||
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(createCGSCCToFunctionPassAdaptor(FunctionFooPass())));
|
||||
ModulePassManager MPM;
|
||||
// loop -> function -> module
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(LoopFooPass())));
|
||||
// function -> module
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(FunctionFooPass()));
|
||||
|
||||
// loop -> function -> cgscc -> module
|
||||
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(createCGSCCToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(LoopFooPass()))));
|
||||
// function -> cgscc -> module
|
||||
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(createCGSCCToFunctionPassAdaptor(FunctionFooPass())));
|
||||
```
|
||||
|
||||
A pass manager of a specific IR unit is also a pass of that kind. For
|
||||
example, a ``FunctionPassManager`` is a function pass, meaning it can be
|
||||
added to a ``ModulePassManager``:
|
||||
example, a `FunctionPassManager` is a function pass, meaning it can be
|
||||
added to a `ModulePassManager`:
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
ModulePassManager MPM;
|
||||
|
||||
ModulePassManager MPM;
|
||||
FunctionPassManager FPM;
|
||||
// InstSimplifyPass is a function pass
|
||||
FPM.addPass(InstSimplifyPass());
|
||||
|
||||
FunctionPassManager FPM;
|
||||
// InstSimplifyPass is a function pass
|
||||
FPM.addPass(InstSimplifyPass());
|
||||
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
||||
```
|
||||
|
||||
Generally you want to group CGSCC/function/loop passes together in a pass
|
||||
manager, as opposed to adding adaptors for each pass to the containing upper
|
||||
level pass manager. For example,
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
ModulePassManager MPM;
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(FunctionPass1()));
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(FunctionPass2()));
|
||||
MPM.run();
|
||||
```
|
||||
|
||||
ModulePassManager MPM;
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(FunctionPass1()));
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(FunctionPass2()));
|
||||
MPM.run();
|
||||
will run `FunctionPass1` on each function in a module, then run
|
||||
`FunctionPass2` on each function in the module. In contrast,
|
||||
|
||||
will run ``FunctionPass1`` on each function in a module, then run
|
||||
``FunctionPass2`` on each function in the module. In contrast,
|
||||
```cpp
|
||||
ModulePassManager MPM;
|
||||
|
||||
.. code-block:: c++
|
||||
FunctionPassManager FPM;
|
||||
FPM.addPass(FunctionPass1());
|
||||
FPM.addPass(FunctionPass2());
|
||||
|
||||
ModulePassManager MPM;
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
||||
```
|
||||
|
||||
FunctionPassManager FPM;
|
||||
FPM.addPass(FunctionPass1());
|
||||
FPM.addPass(FunctionPass2());
|
||||
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
||||
|
||||
will run ``FunctionPass1`` and ``FunctionPass2`` on the first function in a
|
||||
will run `FunctionPass1` and `FunctionPass2` on the first function in a
|
||||
module, then run both passes on the second function in the module, and so on.
|
||||
This is better for cache locality around LLVM data structures. This similarly
|
||||
applies for the other IR types, and in some cases can even affect the quality
|
||||
|
|
@ -145,48 +141,46 @@ of optimization. For example, running all loop passes on a loop may cause a
|
|||
later loop to be able to be optimized more than if each loop pass were run
|
||||
separately.
|
||||
|
||||
Inserting Passes into Default Pipelines
|
||||
=======================================
|
||||
## Inserting Passes into Default Pipelines
|
||||
|
||||
Rather than manually adding passes to a pass manager, the typical way of
|
||||
creating a pass manager is to use a ``PassBuilder`` and call something like
|
||||
``PassBuilder::buildPerModuleDefaultPipeline()`` which creates a typical
|
||||
creating a pass manager is to use a `PassBuilder` and call something like
|
||||
`PassBuilder::buildPerModuleDefaultPipeline()` which creates a typical
|
||||
pipeline for a given optimization level.
|
||||
|
||||
Sometimes either frontends or backends will want to inject passes into the
|
||||
pipeline. For example, frontends may want to add instrumentation, and target
|
||||
backends may want to add passes that lower custom intrinsics. For these
|
||||
cases, ``PassBuilder`` exposes callbacks that allow injecting passes into
|
||||
cases, `PassBuilder` exposes callbacks that allow injecting passes into
|
||||
certain parts of the pipeline. For example,
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
PassBuilder PB;
|
||||
PB.registerPipelineStartEPCallback(
|
||||
[&](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
|
||||
MPM.addPass(FooPass());
|
||||
});
|
||||
```
|
||||
|
||||
PassBuilder PB;
|
||||
PB.registerPipelineStartEPCallback(
|
||||
[&](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
|
||||
MPM.addPass(FooPass());
|
||||
});
|
||||
will add `FooPass` near the very beginning of the pipeline for pass
|
||||
managers created by that `PassBuilder`. See the documentation for
|
||||
`PassBuilder` for the various places that passes can be added.
|
||||
|
||||
will add ``FooPass`` near the very beginning of the pipeline for pass
|
||||
managers created by that ``PassBuilder``. See the documentation for
|
||||
``PassBuilder`` for the various places that passes can be added.
|
||||
|
||||
If a ``PassBuilder`` has a corresponding ``TargetMachine`` for a backend, it
|
||||
will call ``TargetMachine::registerPassBuilderCallbacks()`` to allow the
|
||||
If a `PassBuilder` has a corresponding `TargetMachine` for a backend, it
|
||||
will call `TargetMachine::registerPassBuilderCallbacks()` to allow the
|
||||
backend to inject passes into the pipeline.
|
||||
|
||||
Clang's ``BackendUtil.cpp`` shows examples of a frontend adding (mostly
|
||||
Clang's `BackendUtil.cpp` shows examples of a frontend adding (mostly
|
||||
sanitizer) passes to various parts of the pipeline.
|
||||
``AMDGPUTargetMachine::registerPassBuilderCallbacks()`` is an example of a
|
||||
`AMDGPUTargetMachine::registerPassBuilderCallbacks()` is an example of a
|
||||
backend adding passes to various parts of the pipeline.
|
||||
|
||||
Pass plugins can also add passes into default pipelines. Different tools have
|
||||
different ways of loading dynamic pass plugins. For example, ``opt
|
||||
-load-pass-plugin=path/to/plugin.so`` loads a pass plugin into ``opt``. For
|
||||
information on writing a pass plugin, see :doc:`WritingAnLLVMNewPMPass`.
|
||||
different ways of loading dynamic pass plugins. For example, `opt
|
||||
-load-pass-plugin=path/to/plugin.so` loads a pass plugin into `opt`. For
|
||||
information on writing a pass plugin, see {doc}`WritingAnLLVMNewPMPass`.
|
||||
|
||||
Using Analyses
|
||||
==============
|
||||
## Using Analyses
|
||||
|
||||
LLVM provides many analyses that passes can use, such as a dominator tree.
|
||||
Calculating these can be expensive, so the new pass manager has
|
||||
|
|
@ -196,7 +190,7 @@ When a pass runs on some IR, it also receives an analysis manager which it can
|
|||
query for analyses. Querying for an analysis will cause the manager to check if
|
||||
it has already computed the result for the requested IR. If it already has and
|
||||
the result is still valid, it will return that. Otherwise it will construct a
|
||||
new result by calling the analysis's ``run()`` method, cache it, and return it.
|
||||
new result by calling the analysis's `run()` method, cache it, and return it.
|
||||
You can also ask the analysis manager to only return an analysis if it's
|
||||
already cached.
|
||||
|
||||
|
|
@ -208,26 +202,26 @@ down the IR hierarchy. For example, an SCC pass may want to look at function
|
|||
analyses for the functions inside the SCC. Or it may want to look at some
|
||||
immutable global analysis. In these cases, the analysis manager can provide a
|
||||
proxy to an outer or inner level analysis manager. For example, to get a
|
||||
``FunctionAnalysisManager`` from a ``CGSCCAnalysisManager``, you can call
|
||||
`FunctionAnalysisManager` from a `CGSCCAnalysisManager`, you can call
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
FunctionAnalysisManager &FAM =
|
||||
AM.getResult<FunctionAnalysisManagerCGSCCProxy>(InitialC, CG)
|
||||
.getManager();
|
||||
```
|
||||
|
||||
FunctionAnalysisManager &FAM =
|
||||
AM.getResult<FunctionAnalysisManagerCGSCCProxy>(InitialC, CG)
|
||||
.getManager();
|
||||
|
||||
and use ``FAM`` as a typical ``FunctionAnalysisManager`` that a function pass
|
||||
and use `FAM` as a typical `FunctionAnalysisManager` that a function pass
|
||||
would have access to. To get access to an outer level IR analysis, you can
|
||||
call
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
const auto &MAMProxy =
|
||||
AM.getResult<ModuleAnalysisManagerCGSCCProxy>(InitialC, CG);
|
||||
FooAnalysisResult *AR = MAMProxy.getCachedResult<FooAnalysis>(M);
|
||||
```cpp
|
||||
const auto &MAMProxy =
|
||||
AM.getResult<ModuleAnalysisManagerCGSCCProxy>(InitialC, CG);
|
||||
FooAnalysisResult *AR = MAMProxy.getCachedResult<FooAnalysis>(M);
|
||||
```
|
||||
|
||||
Asking for a cached and immutable outer level IR analysis works via
|
||||
``getCachedResult()``, but getting direct access to an outer level IR analysis
|
||||
`getCachedResult()`, but getting direct access to an outer level IR analysis
|
||||
manager to compute an outer level IR analysis is not allowed. This is for a
|
||||
couple reasons.
|
||||
|
||||
|
|
@ -265,7 +259,7 @@ make sure the function analyses that loop passes use are valid, they are
|
|||
manually updated in the loop passes to ensure that invalidation is not
|
||||
necessary. There is a set of common function analyses that loop passes and
|
||||
analyses have access to which is passed into loop passes as a
|
||||
``LoopStandardAnalysisResults`` parameter. Other mutable function analyses are
|
||||
`LoopStandardAnalysisResults` parameter. Other mutable function analyses are
|
||||
not accessible from loop passes.
|
||||
|
||||
As with any caching mechanism, we need some way to tell analysis managers
|
||||
|
|
@ -286,55 +280,55 @@ valid and should be invalidated. If a pass wants to keep some specific
|
|||
analysis up to date, such as when updating it would be faster than
|
||||
invalidating and recalculating it, the analysis itself may have methods to
|
||||
update it for specific transformations, or there may be helper updaters like
|
||||
``DomTreeUpdater`` for a ``DominatorTree``. Otherwise to mark some analysis
|
||||
as no longer valid, the pass can return a ``PreservedAnalyses`` with the
|
||||
`DomTreeUpdater` for a `DominatorTree`. Otherwise to mark some analysis
|
||||
as no longer valid, the pass can return a `PreservedAnalyses` with the
|
||||
proper analyses invalidated.
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
// We've made no transformations that can affect any analyses.
|
||||
return PreservedAnalyses::all();
|
||||
|
||||
// We've made no transformations that can affect any analyses.
|
||||
return PreservedAnalyses::all();
|
||||
// We've made transformations and don't want to bother to update any analyses.
|
||||
return PreservedAnalyses::none();
|
||||
|
||||
// We've made transformations and don't want to bother to update any analyses.
|
||||
return PreservedAnalyses::none();
|
||||
// We've specifically updated the dominator tree alongside any transformations, but other analysis results may be invalid.
|
||||
PreservedAnalyses PA;
|
||||
PA.preserve<DominatorAnalysis>();
|
||||
return PA;
|
||||
|
||||
// We've specifically updated the dominator tree alongside any transformations, but other analysis results may be invalid.
|
||||
PreservedAnalyses PA;
|
||||
PA.preserve<DominatorAnalysis>();
|
||||
return PA;
|
||||
// We haven't made any control flow changes, any analyses that only care about the control flow are still valid.
|
||||
PreservedAnalyses PA;
|
||||
PA.preserveSet<CFGAnalyses>();
|
||||
return PA;
|
||||
```
|
||||
|
||||
// We haven't made any control flow changes, any analyses that only care about the control flow are still valid.
|
||||
PreservedAnalyses PA;
|
||||
PA.preserveSet<CFGAnalyses>();
|
||||
return PA;
|
||||
|
||||
The pass manager will call the analysis manager's ``invalidate()`` method
|
||||
with the pass's returned ``PreservedAnalyses``. This can be also done
|
||||
The pass manager will call the analysis manager's `invalidate()` method
|
||||
with the pass's returned `PreservedAnalyses`. This can be also done
|
||||
manually within the pass:
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
FooModulePass::run(Module& M, ModuleAnalysisManager& AM) {
|
||||
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
|
||||
|
||||
FooModulePass::run(Module& M, ModuleAnalysisManager& AM) {
|
||||
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
|
||||
// Invalidate all analysis results for function F1.
|
||||
FAM.invalidate(F1, PreservedAnalyses::none());
|
||||
|
||||
// Invalidate all analysis results for function F1.
|
||||
FAM.invalidate(F1, PreservedAnalyses::none());
|
||||
// Invalidate all analysis results across the entire module.
|
||||
AM.invalidate(M, PreservedAnalyses::none());
|
||||
|
||||
// Invalidate all analysis results across the entire module.
|
||||
AM.invalidate(M, PreservedAnalyses::none());
|
||||
// Clear the entry in the analysis manager for function F2 if we've completely removed it from the module.
|
||||
FAM.clear(F2);
|
||||
|
||||
// Clear the entry in the analysis manager for function F2 if we've completely removed it from the module.
|
||||
FAM.clear(F2);
|
||||
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
One thing to note when accessing inner level IR analyses is cached results for
|
||||
deleted IR. If a function is deleted in a module pass, its address is still used
|
||||
as the key for cached analyses. Take care in the pass to either clear the
|
||||
results for that function or not use inner analyses at all.
|
||||
|
||||
``AM.invalidate(M, PreservedAnalyses::none());`` will invalidate the inner
|
||||
`AM.invalidate(M, PreservedAnalyses::none());` will invalidate the inner
|
||||
analysis manager proxy which will clear all cached analyses, conservatively
|
||||
assuming that there are invalid addresses used as keys for cached analyses.
|
||||
However, if you'd like to be more selective about which analyses are
|
||||
|
|
@ -343,65 +337,64 @@ essentially saying that all deleted entries have been taken care of manually.
|
|||
This should only be done with measurable compile time gains as it can be tricky
|
||||
to make sure all the right analyses are invalidated.
|
||||
|
||||
Implementing Analysis Invalidation
|
||||
==================================
|
||||
## Implementing Analysis Invalidation
|
||||
|
||||
By default, an analysis is invalidated if ``PreservedAnalyses`` says that
|
||||
By default, an analysis is invalidated if `PreservedAnalyses` says that
|
||||
analyses on the IR unit it runs on are not preserved (see
|
||||
``AnalysisResultModel::invalidate()``). An analysis can implement
|
||||
``invalidate()`` to be more conservative when it comes to invalidation. For
|
||||
`AnalysisResultModel::invalidate()`). An analysis can implement
|
||||
`invalidate()` to be more conservative when it comes to invalidation. For
|
||||
example,
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &) {
|
||||
auto PAC = PA.getChecker<FooAnalysis>();
|
||||
// the default would be:
|
||||
// return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>());
|
||||
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()
|
||||
|| PAC.preservedSet<CFGAnalyses>());
|
||||
}
|
||||
```
|
||||
|
||||
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &) {
|
||||
auto PAC = PA.getChecker<FooAnalysis>();
|
||||
// the default would be:
|
||||
// return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>());
|
||||
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()
|
||||
|| PAC.preservedSet<CFGAnalyses>());
|
||||
}
|
||||
|
||||
says that if the ``PreservedAnalyses`` specifically preserves
|
||||
``FooAnalysis``, or if ``PreservedAnalyses`` preserves all analyses (implicit
|
||||
in ``PAC.preserved()``), or if ``PreservedAnalyses`` preserves all function
|
||||
analyses, or ``PreservedAnalyses`` preserves all analyses that only care
|
||||
about the CFG, the ``FooAnalysisResult`` should not be invalidated.
|
||||
says that if the `PreservedAnalyses` specifically preserves
|
||||
`FooAnalysis`, or if `PreservedAnalyses` preserves all analyses (implicit
|
||||
in `PAC.preserved()`), or if `PreservedAnalyses` preserves all function
|
||||
analyses, or `PreservedAnalyses` preserves all analyses that only care
|
||||
about the CFG, the `FooAnalysisResult` should not be invalidated.
|
||||
|
||||
If an analysis is stateless and generally shouldn't be invalidated, use the
|
||||
following:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &) {
|
||||
// Check whether the analysis has been explicitly invalidated. Otherwise, it's
|
||||
// stateless and remains preserved.
|
||||
auto PAC = PA.getChecker<FooAnalysis>();
|
||||
return !PAC.preservedWhenStateless();
|
||||
}
|
||||
```cpp
|
||||
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &) {
|
||||
// Check whether the analysis has been explicitly invalidated. Otherwise, it's
|
||||
// stateless and remains preserved.
|
||||
auto PAC = PA.getChecker<FooAnalysis>();
|
||||
return !PAC.preservedWhenStateless();
|
||||
}
|
||||
```
|
||||
|
||||
If an analysis depends on other analyses, those analyses also need to be
|
||||
checked if they are invalidated:
|
||||
|
||||
.. code-block:: c++
|
||||
```cpp
|
||||
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &Inv) {
|
||||
auto PAC = PA.getChecker<FooAnalysis>();
|
||||
if (!PAC.preserved() && !PAC.preservedSet<AllAnalysesOn<Function>>())
|
||||
return true;
|
||||
|
||||
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &Inv) {
|
||||
auto PAC = PA.getChecker<FooAnalysis>();
|
||||
if (!PAC.preserved() && !PAC.preservedSet<AllAnalysesOn<Function>>())
|
||||
return true;
|
||||
|
||||
// Check transitive dependencies.
|
||||
return Inv.invalidate<BarAnalysis>(F, PA) ||
|
||||
Inv.invalidate<BazAnalysis>(F, PA);
|
||||
}
|
||||
// Check transitive dependencies.
|
||||
return Inv.invalidate<BarAnalysis>(F, PA) ||
|
||||
Inv.invalidate<BazAnalysis>(F, PA);
|
||||
}
|
||||
```
|
||||
|
||||
Combining invalidation and analysis manager proxies results in some
|
||||
complexity. For example, when we invalidate all analyses in a module pass,
|
||||
we have to make sure that we also invalidate function analyses accessible via
|
||||
any existing inner proxies. The inner proxy's ``invalidate()`` first checks
|
||||
any existing inner proxies. The inner proxy's `invalidate()` first checks
|
||||
if the proxy itself should be invalidated. If so, that means the proxy may
|
||||
contain pointers to IR that is no longer valid, meaning that the inner proxy
|
||||
needs to completely clear all relevant analysis results. Otherwise the proxy
|
||||
|
|
@ -414,41 +407,40 @@ the outer analysis is invalidated, we need to make sure that dependent inner
|
|||
analyses are also invalidated. This actually happens with alias analysis
|
||||
results. Alias analysis is a function-level analysis, but there are
|
||||
module-level implementations of specific types of alias analysis. Currently
|
||||
``GlobalsAA`` is the only module-level alias analysis and it generally is not
|
||||
`GlobalsAA` is the only module-level alias analysis and it generally is not
|
||||
invalidated so this is not so much of a concern. See
|
||||
``OuterAnalysisManagerProxy::Result::registerOuterAnalysisInvalidation()``
|
||||
`OuterAnalysisManagerProxy::Result::registerOuterAnalysisInvalidation()`
|
||||
for more details.
|
||||
|
||||
Invoking ``opt``
|
||||
================
|
||||
## Invoking `opt`
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ opt -passes='pass1,pass2' /tmp/a.ll -S
|
||||
# -p is an alias for -passes
|
||||
$ opt -p pass1,pass2 /tmp/a.ll -S
|
||||
```console
|
||||
$ opt -passes='pass1,pass2' /tmp/a.ll -S
|
||||
# -p is an alias for -passes
|
||||
$ opt -p pass1,pass2 /tmp/a.ll -S
|
||||
```
|
||||
|
||||
The new PM typically requires explicit pass nesting. For example, to run a
|
||||
function pass, then a module pass, we need to wrap the function pass in a module
|
||||
adaptor:
|
||||
|
||||
.. code-block:: shell
|
||||
```console
|
||||
$ opt -passes='function(no-op-function),no-op-module' /tmp/a.ll -S
|
||||
```
|
||||
|
||||
$ opt -passes='function(no-op-function),no-op-module' /tmp/a.ll -S
|
||||
|
||||
A more complete example, and ``-debug-pass-manager`` to show the execution
|
||||
A more complete example, and `-debug-pass-manager` to show the execution
|
||||
order:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ opt -passes='no-op-module,cgscc(no-op-cgscc,function(no-op-function,loop(no-op-loop))),function(no-op-function,loop(no-op-loop))' /tmp/a.ll -S -debug-pass-manager
|
||||
```console
|
||||
$ opt -passes='no-op-module,cgscc(no-op-cgscc,function(no-op-function,loop(no-op-loop))),function(no-op-function,loop(no-op-loop))' /tmp/a.ll -S -debug-pass-manager
|
||||
```
|
||||
|
||||
Improper nesting can lead to error messages such as
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ opt -passes='no-op-function,no-op-module' /tmp/a.ll -S
|
||||
opt: unknown function pass 'no-op-module'
|
||||
```console
|
||||
$ opt -passes='no-op-function,no-op-module' /tmp/a.ll -S
|
||||
opt: unknown function pass 'no-op-module'
|
||||
```
|
||||
|
||||
The nesting is: module (-> cgscc) -> function -> loop, where the CGSCC nesting is optional.
|
||||
|
||||
|
|
@ -459,42 +451,41 @@ There are a couple of special cases for easier typing:
|
|||
|
||||
* For example, the following are equivalent
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ opt -passes='no-op-function,no-op-function' /tmp/a.ll -S
|
||||
$ opt -passes='function(no-op-function,no-op-function)' /tmp/a.ll -S
|
||||
```console
|
||||
$ opt -passes='no-op-function,no-op-function' /tmp/a.ll -S
|
||||
$ opt -passes='function(no-op-function,no-op-function)' /tmp/a.ll -S
|
||||
```
|
||||
|
||||
* If there is an adaptor for a pass that lets it fit in the previous pass
|
||||
manager, that is implicitly created
|
||||
|
||||
* For example, the following are equivalent
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ opt -passes='no-op-function,no-op-loop' /tmp/a.ll -S
|
||||
$ opt -passes='no-op-function,loop(no-op-loop)' /tmp/a.ll -S
|
||||
```console
|
||||
$ opt -passes='no-op-function,no-op-loop' /tmp/a.ll -S
|
||||
$ opt -passes='no-op-function,loop(no-op-loop)' /tmp/a.ll -S
|
||||
```
|
||||
|
||||
For a list of available passes and analyses, including the IR unit (module,
|
||||
CGSCC, function, loop) they operate on, run
|
||||
|
||||
.. code-block:: shell
|
||||
```console
|
||||
$ opt --print-passes
|
||||
```
|
||||
|
||||
$ opt --print-passes
|
||||
or take a look at `PassRegistry.def`.
|
||||
|
||||
or take a look at ``PassRegistry.def``.
|
||||
|
||||
To make sure an analysis named ``foo`` is available before a pass, add
|
||||
``require<foo>`` to the pass pipeline. This adds a pass that simply requests
|
||||
To make sure an analysis named `foo` is available before a pass, add
|
||||
`require<foo>` to the pass pipeline. This adds a pass that simply requests
|
||||
that the analysis is run. This pass is also subject to proper nesting. For
|
||||
example, to make sure some function analysis is already computed for all
|
||||
functions before a module pass:
|
||||
|
||||
.. code-block:: shell
|
||||
```console
|
||||
$ opt -passes='function(require<my-function-analysis>),my-module-pass' /tmp/a.ll -S
|
||||
```
|
||||
|
||||
$ opt -passes='function(require<my-function-analysis>),my-module-pass' /tmp/a.ll -S
|
||||
|
||||
Status of the New and Legacy Pass Managers
|
||||
==========================================
|
||||
## Status of the New and Legacy Pass Managers
|
||||
|
||||
LLVM currently contains two pass managers, the legacy PM and the new PM. The
|
||||
optimization pipeline (aka the middle-end) uses the new PM, whereas the backend
|
||||
|
|
@ -505,14 +496,14 @@ deprecated and there are ongoing efforts to remove its usage.
|
|||
|
||||
Some IR passes are considered part of the backend codegen pipeline even if
|
||||
they are LLVM IR passes (whereas all MIR passes are codegen passes). This
|
||||
includes anything added via ``TargetPassConfig`` hooks, e.g.
|
||||
``TargetPassConfig::addCodeGenPrepare()``.
|
||||
includes anything added via `TargetPassConfig` hooks, e.g.
|
||||
`TargetPassConfig::addCodeGenPrepare()`.
|
||||
|
||||
The ``TargetMachine::adjustPassManager()`` function that was used to extend a
|
||||
The `TargetMachine::adjustPassManager()` function that was used to extend a
|
||||
legacy PM with passes on a per target basis has been removed. It was mainly
|
||||
used from opt, but since support for using the default pipelines has been
|
||||
removed in opt the function isn't needed any longer. In the new PM such
|
||||
adjustments are done by using ``TargetMachine::registerPassBuilderCallbacks()``.
|
||||
adjustments are done by using `TargetMachine::registerPassBuilderCallbacks()`.
|
||||
|
||||
Currently there are efforts to make the codegen pipeline work with the new
|
||||
PM.
|
||||
|
|
|
|||
Loading…
Reference in a new issue